From 4174d627fb84d0ea09044733c010cc415c5dbc92 Mon Sep 17 00:00:00 2001 From: Nicolas Duhamel Date: Sun, 11 Dec 2022 17:39:21 +0100 Subject: [PATCH] TVR: Add valve state and local temperature --- main.go | 1 + pkg/device/device.go | 51 +++++++++++++++++++++-------- pkg/device/device_test.go | 2 +- pkg/device/settings.go | 67 ++++++++++++++++++++++++++++++++++----- 4 files changed, 98 insertions(+), 23 deletions(-) diff --git a/main.go b/main.go index 50514fc..65f33a9 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ func (app *App) subscribe( ) context.CancelFunc { ctx, cancelFunc := context.WithCancel(app.ctx) + ctx = context.WithValue(ctx, "pubchan", app.PubChan) log := zerolog.Ctx(ctx) diff --git a/pkg/device/device.go b/pkg/device/device.go index 1e0669c..c9508cb 100644 --- a/pkg/device/device.go +++ b/pkg/device/device.go @@ -7,7 +7,6 @@ import ( "time" "git.quimbo.fr/nicolas/mqtt" - "github.com/ohler55/ojg/jp" "github.com/ohler55/ojg/oj" "github.com/rs/zerolog" // "reflect" @@ -22,11 +21,13 @@ type Error string func (e Error) Error() string { return string(e) } type DeviceState struct { - Mode string `json:"mode"` - Setpoint int `json:"setpoint"` - Time time.Time `json:"time"` - ProgramName string `json:"program_name"` - UntilTime time.Time `json:"until_time"` + Mode string `json:"mode"` + Setpoint int `json:"setpoint"` + Time time.Time `json:"time"` + ProgramName string `json:"program_name"` + UntilTime time.Time `json:"until_time"` + ValveState string `json:"valve_state"` + LocalTemperature int `json:"local_temperature"` } func (s *DeviceState) Equivalent(state DeviceState) bool { @@ -136,12 +137,15 @@ func (d *Device) SetSetpoint(value int, pubchan chan Message) error { } func (d *Device) SetState(log zerolog.Logger, state DeviceState, pubchan chan Message) error { + // Called on mqtt state/set cmd received // If same state do nothing - // else use checksetpoint for changing state if d.State.Equivalent(state) { log.Debug().Msg("same state no change") return nil } + // keep tvr state + state.LocalTemperature = d.State.LocalTemperature + state.ValveState = d.State.ValveState d.State = state @@ -213,19 +217,38 @@ func (d *Device) onMessage(ctx context.Context, msg mqtt.Message) { log.Error().Err(err).Msg("during payload parse") return } - - x, err := jp.ParseString(d.Settings.TVR.Setpoint_state_jp) + // Setpoint + setpoint, err := d.Settings.TVR.ParseSetpoint(obj) if err != nil { - log.Error().Err(err).Msg("while parsing payload") + log.Error().Err(err).Msg("while parsing setpoint") return } + d.CurrentSetpoint = setpoint - r := x.First(obj) + // LocalTemperature + localTemperature, err := d.Settings.TVR.ParseLocalTemperature(obj) + if err != nil { + log.Error().Err(err).Msg("while parsing LocalTemperature") + return + } + d.State.LocalTemperature = localTemperature - if v, ok := r.(int64); ok { - d.CurrentSetpoint = int(v) + // Valve + valve, err := d.Settings.TVR.ParseValve(obj) + if err != nil { + log.Error().Err(err).Msg("while parsing Valve") + return + } + d.State.ValveState = valve + + if v := ctx.Value("pubchan"); v != nil { + if pubchan, ok := v.(chan Message); ok { + d.publishState(pubchan) + } else { + log.Error().Msg("invalid pubchan ctx type") + } } else { - log.Error().Err(err).Interface("parsing payload", r).Msg("while parsing payload") + log.Error().Msg("pubchan not found in ctx") } } diff --git a/pkg/device/device_test.go b/pkg/device/device_test.go index a85e0d8..edef25a 100644 --- a/pkg/device/device_test.go +++ b/pkg/device/device_test.go @@ -417,7 +417,7 @@ func TestUpdate(t *testing.T) { go func(result chan Message, changechan chan bool, errchan chan error) { logger := zerolog.New(ioutil.Discard).With().Timestamp().Logger() - change, err := tt.device.update(&logger, result) + change, err := tt.device.update(logger, result) errchan <- err changechan <- change close(rchan) diff --git a/pkg/device/settings.go b/pkg/device/settings.go index 86f828b..d747d1c 100644 --- a/pkg/device/settings.go +++ b/pkg/device/settings.go @@ -5,6 +5,8 @@ import ( "fmt" "text/template" "time" + + "github.com/ohler55/ojg/jp" ) type DayOfWeek int @@ -149,10 +151,57 @@ type Preset struct { } type TVRSettings struct { - Setpoint_topic string `json:"setpoint_topic"` - Setpoint_payload string `json:"setpoint_payload"` - Setpoint_state_topic string `json:"setpoint_state_topic"` - Setpoint_state_jp string `json:"setpoint_state_jp"` + Setpoint_topic string `json:"setpoint_topic"` + Setpoint_payload string `json:"setpoint_payload"` + Setpoint_state_topic string `json:"setpoint_state_topic"` + Setpoint_state_jp string `json:"setpoint_state_jp"` + StateLocalTemperatureJP string `json:"state_local_temperature_jp"` + StateValveJP string `json:"state_valve_jp"` +} + +func (s TVRSettings) ParseSetpoint(obj interface{}) (int, error) { + jp, err := jp.ParseString(s.Setpoint_state_jp) + if err != nil { + return 0, err + } + + r := jp.First(obj) + + if v, ok := r.(int64); ok { + return int(v), nil + } else { + return 0, Error("Unexpected type") + } +} + +func (s TVRSettings) ParseLocalTemperature(obj interface{}) (int, error) { + jp, err := jp.ParseString(s.StateLocalTemperatureJP) + if err != nil { + return 0, err + } + + r := jp.First(obj) + + if v, ok := r.(int64); ok { + return int(v), nil + } else { + return 0, Error("Unexpected type") + } +} + +func (s TVRSettings) ParseValve(obj interface{}) (string, error) { + jp, err := jp.ParseString(s.StateValveJP) + if err != nil { + return "", err + } + + r := jp.First(obj) + + if v, ok := r.(string); ok { + return string(v), nil + } else { + return "", Error("Unexpected type") + } } func (s TVRSettings) FormatTopicState(device_name string) (string, error) { @@ -242,10 +291,12 @@ var DefaultPrograms = Programs{ } var DefaultTVRSettings = TVRSettings{ - Setpoint_topic: "zigbee2mqtt/TVR/{{.Device}}/set", - Setpoint_payload: "{\"current_heating_setpoint\": {{.Setpoint}}}", - Setpoint_state_topic: "zigbee2mqtt/TVR/{{.Device}}", - Setpoint_state_jp: "$.current_heating_setpoint", + Setpoint_topic: "zigbee2mqtt/TVR/{{.Device}}/set", + Setpoint_payload: "{\"current_heating_setpoint\": {{.Setpoint}}}", + Setpoint_state_topic: "zigbee2mqtt/TVR/{{.Device}}", + Setpoint_state_jp: "$.current_heating_setpoint", + StateLocalTemperatureJP: "$.local_temperature", + StateValveJP: "$.valve_state", } var DefaultDeviceSettings = DeviceSettings{