316 lines
7.5 KiB
Go
316 lines
7.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
MQTT "github.com/eclipse/paho.mqtt.golang"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type MQTTChannel struct {
|
|
ID string
|
|
Num uint
|
|
name string
|
|
MQTTStateTopic string
|
|
mqttCommandTopic string
|
|
Value bool
|
|
onboot string
|
|
parent *MQTTBoard
|
|
MQTTRemoteStateTopic string
|
|
MQTTRemoteCommandTopic string
|
|
MQTTRemotePayloadOn string
|
|
MQTTRemotePayloadOff string
|
|
}
|
|
|
|
type MQTTBoard struct {
|
|
ID string
|
|
Name string
|
|
ChannelCount uint
|
|
Channels []*MQTTChannel
|
|
MQTTRemoteSchema string
|
|
MQTTRemoteHost string
|
|
MQTTRemotePort string
|
|
MQTTRemoteUsername string
|
|
MQTTRemotePassword string
|
|
MQTTClient MQTT.Client
|
|
}
|
|
|
|
func newMQTTChannel(v *viper.Viper, channelID string) MQTTChannel {
|
|
v.SetDefault("name", "unknown")
|
|
v.SetDefault("lastValue", false)
|
|
v.SetDefault("onboot", "off")
|
|
v.SetDefault("mqtt.statetopic", v.GetString("name"))
|
|
v.SetDefault("mqtt.commandtopic", v.GetString("name"))
|
|
v.SetDefault("mqttremote.statetopic", v.GetString("name"))
|
|
v.SetDefault("mqttremote.commandtopic", v.GetString("name"))
|
|
v.SetDefault("mqttremote.payloadon", "on")
|
|
v.SetDefault("mqttremote.payloadoff", "off")
|
|
|
|
value := false
|
|
switch v.GetString("onboot") {
|
|
case "on":
|
|
value = true
|
|
case "last":
|
|
value = v.GetBool("lastValue")
|
|
}
|
|
// newUUID := UUID.New().String()
|
|
// v.SetDefault("id", newUUID)
|
|
return MQTTChannel{
|
|
ID: channelID,
|
|
Num: v.GetUint("num"),
|
|
name: v.GetString("name"),
|
|
MQTTStateTopic: v.GetString("mqtt.statetopic"),
|
|
mqttCommandTopic: v.GetString("mqtt.commandtopic"),
|
|
Value: value,
|
|
onboot: v.GetString("onboot"),
|
|
MQTTRemoteStateTopic: v.GetString("mqttremote.statetopic"),
|
|
MQTTRemoteCommandTopic: v.GetString("mqttremote.commandtopic"),
|
|
MQTTRemotePayloadOn: v.GetString("mqttremote.payloadon"),
|
|
MQTTRemotePayloadOff: v.GetString("mqttremote.payloadoff"),
|
|
}
|
|
}
|
|
|
|
func newMQTTBoard(v *viper.Viper, id string) *MQTTBoard {
|
|
var b MQTTBoard
|
|
|
|
v.SetDefault("name", "board "+id)
|
|
v.SetDefault("type", "mqtt")
|
|
v.SetDefault("channelCount", 0)
|
|
v.SetDefault("channels", "")
|
|
v.SetDefault("MQTTRemote.Schema", "tcp")
|
|
v.SetDefault("MQTTRemote.Host", "")
|
|
v.SetDefault("MQTTRemote.Port", "1883")
|
|
v.SetDefault("MQTTRemote.Username", "")
|
|
v.SetDefault("MQTTRemote.Password", "")
|
|
|
|
if v.GetInt("channelCount") > 0 {
|
|
for i := 0; i < v.GetInt("channelCount"); i++ {
|
|
v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
|
|
}
|
|
}
|
|
|
|
b = MQTTBoard{
|
|
ID: id,
|
|
Name: v.GetString("name"),
|
|
ChannelCount: v.GetUint("channelCount"),
|
|
MQTTRemoteSchema: v.GetString("MQTTRemote.Schema"),
|
|
MQTTRemoteHost: v.GetString("MQTTRemote.Host"),
|
|
MQTTRemotePort: v.GetString("MQTTRemote.Port"),
|
|
MQTTRemoteUsername: v.GetString("MQTTRemote.Username"),
|
|
MQTTRemotePassword: v.GetString("MQTTRemote.Password"),
|
|
}
|
|
|
|
channels := make([]*MQTTChannel, v.GetInt("channelCount"))
|
|
if v.GetInt("channelCount") > 0 {
|
|
channelsConfig := v.Sub("channels")
|
|
if channelsConfig != nil {
|
|
for channelid1 := range channelsConfig.AllSettings() {
|
|
channelid := strings.ToLower(channelid1)
|
|
channelConfig := channelsConfig.Sub(channelid)
|
|
c := newMQTTChannel(channelConfig, channelid)
|
|
c.parent = &b
|
|
if c.Num >= v.GetUint("channelCount") {
|
|
continue
|
|
}
|
|
channels[c.Num] = &c
|
|
allChannels[c.ID] = &c
|
|
}
|
|
}
|
|
}
|
|
b.Channels = channels
|
|
|
|
return &b
|
|
}
|
|
|
|
func (c *MQTTChannel) Toggle() (bool, error) {
|
|
c.Value = !c.Value
|
|
c.SaveLastState()
|
|
c.UpdateMQTT()
|
|
c.UpdateRemoteMQTT()
|
|
return c.Value, nil
|
|
}
|
|
|
|
func (c *MQTTChannel) On() error {
|
|
c.Value = true
|
|
c.SaveLastState()
|
|
c.UpdateMQTT()
|
|
c.UpdateRemoteMQTT()
|
|
return nil
|
|
}
|
|
|
|
func (c *MQTTChannel) Off() error {
|
|
c.Value = true
|
|
c.SaveLastState()
|
|
c.UpdateMQTT()
|
|
c.UpdateRemoteMQTT()
|
|
return nil
|
|
}
|
|
|
|
func (c *MQTTChannel) ToString() string {
|
|
if !c.Value {
|
|
return "off"
|
|
}
|
|
return "on"
|
|
}
|
|
|
|
func (c *MQTTChannel) UpdateMQTT() {
|
|
MQTTpublish(c.MQTTStateTopic, c.ToString())
|
|
}
|
|
|
|
func (c *MQTTChannel) SaveLastState() {
|
|
if c.onboot == "last" {
|
|
s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
|
|
viper.Set(s, c.Value)
|
|
viper.WriteConfig()
|
|
}
|
|
}
|
|
|
|
func (c *MQTTChannel) UpdateRemoteMQTT() {
|
|
if c.parent.MQTTClient.IsConnected() {
|
|
v := c.MQTTRemotePayloadOff
|
|
if c.Value {
|
|
v = c.MQTTRemotePayloadOn
|
|
}
|
|
c.parent.MQTTClient.Publish(c.MQTTRemoteCommandTopic, 0, false, v)
|
|
}
|
|
}
|
|
|
|
func (c *MQTTChannel) Status() bool {
|
|
return c.Value
|
|
}
|
|
|
|
func (c *MQTTChannel) Parent() Board {
|
|
return c.parent
|
|
}
|
|
|
|
func (c *MQTTChannel) Dump() {
|
|
log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
|
|
}
|
|
|
|
func (b *MQTTBoard) Dump() {
|
|
log.Printf("Board '%s' (id: %s): %d channels\n", b.Name, b.ID, b.ChannelCount)
|
|
for c := range b.Channels {
|
|
b.Channels[c].Dump()
|
|
}
|
|
}
|
|
|
|
func (b *MQTTBoard) Init() {
|
|
uri := b.MQTTRemoteSchema + "://" + b.MQTTRemoteHost + ":" + b.MQTTRemotePort
|
|
opts := MQTT.NewClientOptions().AddBroker(uri)
|
|
|
|
opts.SetClientID(b.ID)
|
|
|
|
if b.MQTTRemoteUsername != "" {
|
|
opts.SetUsername(b.MQTTRemoteUsername)
|
|
}
|
|
|
|
if b.MQTTRemotePassword != "" {
|
|
opts.SetPassword(b.MQTTRemotePassword)
|
|
}
|
|
|
|
opts.SetAutoReconnect(true)
|
|
opts.SetConnectRetryInterval(5 * time.Second)
|
|
opts.SetConnectTimeout(5 * time.Second)
|
|
|
|
opts.SetConnectionLostHandler(func(c MQTT.Client, err error) {
|
|
logErr("mqtt connection lost error: " + err.Error())
|
|
})
|
|
opts.SetReconnectingHandler(func(c MQTT.Client, options *MQTT.ClientOptions) {
|
|
logNotice("mqtt reconnecting")
|
|
})
|
|
opts.SetOnConnectHandler(func(c MQTT.Client) {
|
|
logNotice("mqtt connected")
|
|
// MQTTclient.Publish("openpdu/status", 0, false, "connected")
|
|
})
|
|
|
|
b.MQTTClient = MQTT.NewClient(opts)
|
|
|
|
for {
|
|
token := b.MQTTClient.Connect()
|
|
token.Wait()
|
|
if b.MQTTClient.IsConnected() {
|
|
break
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
}
|
|
|
|
for i := range b.Channels {
|
|
topic := b.Channels[i].MQTTRemoteStateTopic
|
|
if topic == "" {
|
|
continue
|
|
}
|
|
b.MQTTClient.Subscribe(topic, 1, b.Channels[i].MQTTRemoteHandler)
|
|
}
|
|
}
|
|
|
|
func (c *MQTTChannel) MQTTRemoteHandler(client MQTT.Client, msg MQTT.Message) {
|
|
switch string(msg.Payload()) {
|
|
case c.MQTTRemotePayloadOn:
|
|
if !c.Value {
|
|
c.Value = true
|
|
c.SaveLastState()
|
|
c.UpdateMQTT()
|
|
}
|
|
case c.MQTTRemotePayloadOff:
|
|
if c.Value {
|
|
c.Value = false
|
|
c.SaveLastState()
|
|
c.UpdateMQTT()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *MQTTChannel) MQTTHandler(client MQTT.Client, msg MQTT.Message) {
|
|
switch string(msg.Payload()) {
|
|
case "on":
|
|
if !c.Value {
|
|
c.Value = true
|
|
c.SaveLastState()
|
|
c.UpdateRemoteMQTT()
|
|
}
|
|
case "off":
|
|
if c.Value {
|
|
c.Value = false
|
|
c.SaveLastState()
|
|
c.UpdateRemoteMQTT()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *MQTTBoard) Channel(num uint64) Channel {
|
|
return b.Channels[num]
|
|
}
|
|
|
|
func (c *MQTTChannel) Name() string {
|
|
return c.name
|
|
}
|
|
|
|
func (c *MQTTChannel) OnBoot() string {
|
|
return c.onboot
|
|
}
|
|
|
|
func (c *MQTTChannel) SetOnBoot(str string) {
|
|
c.onboot = str
|
|
s := fmt.Sprintf("boards.%s.channels.%s.onboot", c.parent.ID, c.ID)
|
|
viper.Set(s, str)
|
|
}
|
|
|
|
func (c *MQTTChannel) SetMQTTStateTopic(str string) {
|
|
c.MQTTStateTopic = str
|
|
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.statetopic", c.parent.ID, c.ID)
|
|
viper.Set(s, str)
|
|
}
|
|
|
|
func (c *MQTTChannel) SetMQTTCommandTopic(str string) {
|
|
c.mqttCommandTopic = str
|
|
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.commandtopic", c.parent.ID, c.ID)
|
|
viper.Set(s, str)
|
|
}
|
|
|
|
func (c *MQTTChannel) MQTTCommandTopic() string {
|
|
return c.mqttCommandTopic
|
|
}
|