Implement int/string/bool getters over mqtt (#30)

This commit is contained in:
andig 2020-04-13 18:53:56 +02:00 • committed by GitHub
parent 50595af184
commit ca75de87c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 183 additions and 51 deletions

View file

@ -22,7 +22,7 @@ func NewConfigurableFromConfig(log *api.Logger, other map[string]interface{}) ap
provider.NewStringGetterFromConfig(cc.Status),
provider.NewBoolGetterFromConfig(cc.Enabled),
provider.NewBoolSetterFromConfig("enable", cc.Enable),
provider.NewIntSetterFromConfig("current", cc.MaxCurrent),
provider.NewIntSetterFromConfig("maxcurrent", cc.MaxCurrent),
)
return charger

View file

@ -83,6 +83,20 @@ chargers:
- name: go-e
type: go-e # go-e charger
uri: http://192.168.1.4 # go-e address
- name: configurable
type: default # Configurable charger
status: # charger status A..F
type: mqtt
topic: some/topic1
enabled: # charger enabled state (true/false or 0/1)
type: mqtt
topic: some/topic2
enable: # set charger enabled state
type: script
cmd: /bin/sh -c "echo ${enable}"
maxcurrent: # set charger max current
type: script
cmd: /bin/sh -c "echo ${maxcurrent}"
vehicles:
- name: default

View file

@ -1,6 +1,7 @@
package provider
import (
"strings"
"time"
)
@ -22,49 +23,9 @@ type Config struct {
// MQTT singleton
var MQTT *MqttClient
// NewStringGetterFromConfig creates a StringGetter from config
func NewStringGetterFromConfig(pc *Config) (res StringGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.StringGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCached(res, pc.Cache).StringGetter()
}
return
}
// NewBoolGetterFromConfig creates a BoolGetter from config
func NewBoolGetterFromConfig(pc *Config) (res BoolGetter) {
switch pc.Type {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.BoolGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCached(res, pc.Cache).BoolGetter()
}
return
}
// NewFloatGetterFromConfig creates a FloatGetter from config
func NewFloatGetterFromConfig(pc *Config) (res FloatGetter) {
switch pc.Type {
switch strings.ToLower(pc.Type) {
case "mqtt":
if MQTT == nil {
log.FATAL.Fatal("mqtt not configured")
@ -93,9 +54,96 @@ func NewFloatGetterFromConfig(pc *Config) (res FloatGetter) {
return
}
// NewIntGetterFromConfig creates a IntGetter from config
func NewIntGetterFromConfig(pc *Config) (res IntGetter) {
switch strings.ToLower(pc.Type) {
case "mqtt":
if MQTT == nil {
log.FATAL.Fatal("mqtt not configured")
}
if pc.Timeout == 0 {
pc.Timeout = mqttTimeout
}
if pc.Multiplier == 0 {
pc.Multiplier = 1
}
res = MQTT.IntGetter(pc.Topic, int64(pc.Multiplier), pc.Timeout)
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.IntGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCached(res, pc.Cache).IntGetter()
}
return
}
// NewStringGetterFromConfig creates a StringGetter from config
func NewStringGetterFromConfig(pc *Config) (res StringGetter) {
switch strings.ToLower(pc.Type) {
case "mqtt":
if MQTT == nil {
log.FATAL.Fatal("mqtt not configured")
}
if pc.Timeout == 0 {
pc.Timeout = mqttTimeout
}
res = MQTT.StringGetter(pc.Topic, pc.Timeout)
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.StringGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCached(res, pc.Cache).StringGetter()
}
return
}
// NewBoolGetterFromConfig creates a BoolGetter from config
func NewBoolGetterFromConfig(pc *Config) (res BoolGetter) {
switch strings.ToLower(pc.Type) {
case "mqtt":
if MQTT == nil {
log.FATAL.Fatal("mqtt not configured")
}
if pc.Timeout == 0 {
pc.Timeout = mqttTimeout
}
res = MQTT.BoolGetter(pc.Topic, pc.Timeout)
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
}
exec := NewScriptProvider(pc.Timeout)
res = exec.BoolGetter(pc.Cmd)
default:
log.FATAL.Fatalf("invalid provider type %s", pc.Type)
}
if pc.Cache > 0 {
res = NewCached(res, pc.Cache).BoolGetter()
}
return
}
// NewBoolSetterFromConfig creates a BoolSetter from config
func NewBoolSetterFromConfig(param string, pc *Config) (res BoolSetter) {
switch pc.Type {
switch strings.ToLower(pc.Type) {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout
@ -110,7 +158,7 @@ func NewBoolSetterFromConfig(param string, pc *Config) (res BoolSetter) {
// NewIntSetterFromConfig creates a IntSetter from config
func NewIntSetterFromConfig(param string, pc *Config) (res IntSetter) {
switch pc.Type {
switch strings.ToLower(pc.Type) {
case "script":
if pc.Timeout == 0 {
pc.Timeout = execTimeout

View file

@ -14,10 +14,6 @@ import (
var log = api.NewLogger("exec")
func truish(s string) bool {
return s == "1" || strings.ToLower(s) == "true" || strings.ToLower(s) == "on"
}
// Script implements shell script-based providers and setters
type Script struct {
timeout time.Duration

View file

@ -9,6 +9,11 @@ import (
var re = regexp.MustCompile(`\${(\w+)(:([a-zA-Z0-9%.]+))?}`)
// truish returns true if value is truish (true/1/on)
func truish(s string) bool {
return s == "1" || strings.ToLower(s) == "true" || strings.ToLower(s) == "on"
}
// replaceFormatted replaces all occurrances of ${key} with val from the kv map.
// All keys of kv must exist inside the string to apply replacements to
func replaceFormatted(s string, kv map[string]interface{}) (string, error) {

View file

@ -5,6 +5,28 @@ import (
"testing"
)
func TestTruish(t *testing.T) {
cases := []struct {
k string
v bool
}{
{"", false},
{"false", false},
{"0", false},
{"off", false},
{"true", true},
{"1", true},
{"on", true},
}
for _, c := range cases {
b := truish(c.k)
if b != c.v {
t.Errorf("expected %v got %v", c.v, b)
}
}
}
func TestReplace(t *testing.T) {
cases := []struct {
k string

View file

@ -115,14 +115,37 @@ func (m *MqttClient) FloatGetter(topic string, multiplier float64, timeout time.
}
// IntGetter creates handler for int64 from MQTT topic that returns cached value
func (m *MqttClient) IntGetter(topic string, timeout time.Duration) IntGetter {
func (m *MqttClient) IntGetter(topic string, multiplier int64, timeout time.Duration) IntGetter {
h := &msgHandler{
topic: topic,
multiplier: float64(multiplier),
timeout: timeout,
}
m.Listen(topic, h.Receive)
return h.intGetter
}
// StringGetter creates handler for string from MQTT topic that returns cached value
func (m *MqttClient) StringGetter(topic string, timeout time.Duration) StringGetter {
h := &msgHandler{
topic: topic,
timeout: timeout,
}
m.Listen(topic, h.Receive)
return h.intGetter
return h.stringGetter
}
// BoolGetter creates handler for string from MQTT topic that returns cached value
func (m *MqttClient) BoolGetter(topic string, timeout time.Duration) BoolGetter {
h := &msgHandler{
topic: topic,
timeout: timeout,
}
m.Listen(topic, h.Receive)
return h.boolGetter
}
// WaitForToken synchronously waits until token operation completed
@ -209,5 +232,29 @@ func (h *msgHandler) intGetter() (int64, error) {
return 0, fmt.Errorf("%s invalid: '%s'", h.topic, h.payload)
}
return val, nil
return int64(h.multiplier) * val, nil
}
func (h *msgHandler) stringGetter() (string, error) {
h.once.Do(h.waitForInitialValue)
h.mux.Lock()
defer h.mux.Unlock()
if elapsed := time.Since(h.updated); elapsed > h.timeout {
return "", fmt.Errorf("%s outdated: %v", h.topic, elapsed.Truncate(time.Second))
}
return string(h.payload), nil
}
func (h *msgHandler) boolGetter() (bool, error) {
h.once.Do(h.waitForInitialValue)
h.mux.Lock()
defer h.mux.Unlock()
if elapsed := time.Since(h.updated); elapsed > h.timeout {
return false, fmt.Errorf("%s outdated: %v", h.topic, elapsed.Truncate(time.Second))
}
return truish(string(h.payload)), nil
}