evcc-io/server/mqtt.go

103 lines
2.4 KiB
Go

package server
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/core"
"github.com/andig/evcc/provider"
"github.com/andig/evcc/util"
)
// MQTT is the MQTT server. It uses the MQTT client for publishing.
type MQTT struct {
Handler *provider.MqttClient
root string
}
// NewMQTT creates MQTT server
func NewMQTT(root string) *MQTT {
return &MQTT{
Handler: provider.MQTT,
root: root,
}
}
func (m *MQTT) encode(v interface{}) string {
var s string
switch val := v.(type) {
case time.Time:
s = strconv.FormatInt(val.Unix(), 10)
case time.Duration:
// must be before stringer to convert to seconds instead of string
s = fmt.Sprintf("%d", int64(val.Seconds()))
case fmt.Stringer, string:
s = fmt.Sprintf("%s", val)
case float64:
s = fmt.Sprintf("%.3f", val)
default:
s = fmt.Sprintf("%v", val)
}
return s
}
func (m *MQTT) publish(topic string, retained bool, payload interface{}) {
token := m.Handler.Client.Publish(topic, m.Handler.Qos, retained, m.encode(payload))
go m.Handler.WaitForToken(token)
}
type apiHandler interface {
SetMode(api.ChargeMode)
SetTargetSoC(int)
}
func (m *MQTT) listenSetters(topic string, apiHandler apiHandler) {
m.Handler.Listen(topic+"/mode/set", func(payload string) {
apiHandler.SetMode(api.ChargeMode(payload))
})
m.Handler.Listen(topic+"/targetsoc/set", func(payload string) {
soc, err := strconv.Atoi(payload)
if err == nil {
apiHandler.SetTargetSoC(soc)
}
})
}
// Run starts the MQTT publisher for the MQTT API
func (m *MQTT) Run(site *core.Site, in <-chan util.Param) {
topic := fmt.Sprintf("%s/site", m.root)
m.listenSetters(topic, site)
// number of loadpoints
topic = fmt.Sprintf("%s/loadpoints", m.root)
m.publish(topic, true, len(site.LoadPoints()))
for id, lp := range site.LoadPoints() {
topic := fmt.Sprintf("%s/loadpoints/%d", m.root, id)
m.listenSetters(topic, lp)
}
// alive indicator
updated := time.Now().Unix()
m.publish(fmt.Sprintf("%s/updated", m.root), true, updated)
for p := range in {
topic := fmt.Sprintf("%s/site", m.root)
if p.LoadPoint != nil {
topic = fmt.Sprintf("%s/loadpoints/%d", m.root, *p.LoadPoint)
}
// alive indicator
if now := time.Now().Unix(); now != updated {
updated = now
m.publish(fmt.Sprintf("%s/updated", m.root), true, updated)
}
// value
topic += "/" + strings.ToLower(p.Key)
m.publish(topic, false, p.Val)
}
}