temp refactor

This commit is contained in:
2021-01-09 19:38:57 +01:00
parent 04d2fac964
commit 1a5819d46f
24 changed files with 809 additions and 328 deletions

57
src/board/board.go Normal file
View File

@@ -0,0 +1,57 @@
package board
import (
"errors"
MQTT "github.com/eclipse/paho.mqtt.golang"
"github.com/spf13/viper"
)
var boardCreatorFunctions = make(map[string]BoardCreatorFunction)
var AllChannels = make(map[string]Channel)
type BoardCreatorFunction func(*viper.Viper, string) Board
type onChannelUpdateFunction func(oldValue bool, c Channel)
type Board interface {
Init()
Dump()
Channel(uint64) Channel
}
type Channel interface {
Toggle() (bool, error)
On() error
Off() error
ToString() string
Status() bool
Parent() Board
Dump()
Name() string
OnBoot() string
SetOnBoot(string)
SetMQTTStateTopic(string)
SetMQTTCommandTopic(string)
MQTTStateTopic() string
MQTTCommandTopic() string
MQTTHandler(MQTT.Client, MQTT.Message)
AddOnChannelUpdateFunction(string, onChannelUpdateFunction)
}
func RegisterBoardType(name string, fun BoardCreatorFunction) {
boardCreatorFunctions[name] = fun
}
func CreateBoard(cfg *viper.Viper, key string) (Board, error) {
var createNewBoard BoardCreatorFunction
var ok bool
boardType := cfg.GetString("type")
createNewBoard, ok = boardCreatorFunctions[boardType]
if !ok {
return nil, errors.New("Unknown board type")
}
return createNewBoard(cfg, key), nil
}

232
src/board/board_dummy.go Normal file
View File

@@ -0,0 +1,232 @@
package board
import (
"fmt"
"log"
"strings"
PahoMQTT "github.com/eclipse/paho.mqtt.golang"
"github.com/spf13/viper"
)
type DummyChannel struct {
ID string
Num uint
name string
mqttStateTopic string
mqttCommandTopic string
Value bool
onboot string
parent *DummyBoard
onChannelUpdateFunctions map[string]onChannelUpdateFunction
}
type DummyBoard struct {
ID string
Name string
ChannelCount uint
channels []*DummyChannel
}
func newDummyChannel(v *viper.Viper, channelID string) DummyChannel {
v.SetDefault("name", "unknown")
v.SetDefault("lastValue", false)
v.SetDefault("onboot", "off")
v.SetDefault("mqtt.statetopic", v.GetString("name"))
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 DummyChannel{
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"),
}
}
func newDummyBoard(v *viper.Viper, id string) Board {
var b DummyBoard
v.SetDefault("name", "board "+id)
v.SetDefault("type", "dummy")
v.SetDefault("channelCount", 0)
v.SetDefault("channels", "")
if v.GetInt("channelCount") > 0 {
for i := 0; i < v.GetInt("channelCount"); i++ {
v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
}
}
b = DummyBoard{
ID: id,
Name: v.GetString("name"),
ChannelCount: v.GetUint("channelCount"),
}
channels := make([]*DummyChannel, 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 := newDummyChannel(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 *DummyChannel) Toggle() (bool, error) {
c.Value = !c.Value
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](!c.Value, c)
}
return c.Value, nil
}
func (c *DummyChannel) On() error {
oldval := c.Value
c.Value = true
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](oldval, c)
}
return nil
}
func (c *DummyChannel) Off() error {
oldval := c.Value
c.Value = true
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](oldval, c)
}
return nil
}
func (c *DummyChannel) ToString() string {
if !c.Value {
return "off"
}
return "on"
}
// func (c *DummyChannel) UpdateMQTT() {
// mqtt.Publish(c.MQTTStateTopic, c.ToString()) // bisogna rimuovere questa
// }
func (c *DummyChannel) 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 *DummyChannel) Status() bool {
return c.Value
}
func (c *DummyChannel) Parent() Board {
return c.parent
}
func (c *DummyChannel) Dump() {
log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
}
func (b DummyBoard) 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 DummyBoard) Init() {
return
}
func (b DummyBoard) Channel(num uint64) Channel {
return b.channels[num]
}
func (c *DummyChannel) Name() string {
return c.name
}
func (c *DummyChannel) OnBoot() string {
return c.onboot
}
func (c *DummyChannel) 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 *DummyChannel) 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 *DummyChannel) 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 *DummyChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
switch string(msg.Payload()) {
case "on":
if !c.Value {
c.Value = true
c.SaveLastState()
}
case "off":
if c.Value {
c.Value = false
c.SaveLastState()
}
}
}
func (c *DummyChannel) MQTTStateTopic() string {
return c.mqttStateTopic
}
func (c *DummyChannel) MQTTCommandTopic() string {
return c.mqttCommandTopic
}
func init() {
RegisterBoardType("dummy", newDummyBoard)
}
func (c *DummyChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
if c.onChannelUpdateFunctions == nil {
c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
}
c.onChannelUpdateFunctions[name] = f
}

346
src/board/board_mqtt.go Normal file
View File

@@ -0,0 +1,346 @@
package board
import (
"fmt"
"log"
"strings"
"time"
"git.openpdu.org/OpenPDU/openpdu/syslog"
PahoMQTT "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
onChannelUpdateFunctions map[string]onChannelUpdateFunction
}
type MQTTBoard struct {
ID string
Name string
ChannelCount uint
channels []*MQTTChannel
MQTTRemoteSchema string
MQTTRemoteHost string
MQTTRemotePort string
MQTTRemoteUsername string
MQTTRemotePassword string
MQTTClient PahoMQTT.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) Board {
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 init() {
RegisterBoardType("mqtt", newMQTTBoard)
}
func (c *MQTTChannel) Toggle() (bool, error) {
c.Value = !c.Value
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](!c.Value, c)
}
c.UpdateRemoteMQTT()
return c.Value, nil
}
func (c *MQTTChannel) On() error {
oldval := c.Value
c.Value = true
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](oldval, c)
}
c.UpdateRemoteMQTT()
return nil
}
func (c *MQTTChannel) Off() error {
oldval := c.Value
c.Value = false
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](oldval, c)
}
c.UpdateRemoteMQTT()
return nil
}
func (c *MQTTChannel) ToString() string {
if !c.Value {
return "off"
}
return "on"
}
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() {
connected := c.parent.MQTTClient.IsConnected()
if connected {
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 := PahoMQTT.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 PahoMQTT.Client, err error) {
syslog.Err("mqtt connection lost error: " + err.Error())
})
opts.SetReconnectingHandler(func(c PahoMQTT.Client, options *PahoMQTT.ClientOptions) {
syslog.Notice("mqtt reconnecting")
})
opts.SetOnConnectHandler(func(c PahoMQTT.Client) {
syslog.Notice("mqtt connected")
// MQTTclient.Publish("openpdu/status", 0, false, "connected")
})
b.MQTTClient = PahoMQTT.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 PahoMQTT.Client, msg PahoMQTT.Message) {
switch string(msg.Payload()) {
case c.MQTTRemotePayloadOn:
if !c.Value {
c.Value = true
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](false, c)
}
}
case c.MQTTRemotePayloadOff:
if c.Value {
c.Value = false
c.SaveLastState()
for f := range c.onChannelUpdateFunctions {
c.onChannelUpdateFunctions[f](true, c)
}
}
}
}
func (c *MQTTChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
syslog.Debug(fmt.Sprintf("Board '%s', channel '%s', received MQTT message %s",
c.parent.Name,
c.name,
string(msg.Payload()),
))
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) MQTTStateTopic() string {
return c.mqttStateTopic
}
func (c *MQTTChannel) MQTTCommandTopic() string {
return c.mqttCommandTopic
}
func (c *MQTTChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
if c.onChannelUpdateFunctions == nil {
c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
}
c.onChannelUpdateFunctions[name] = f
}