cleanup, comments, tests

This commit is contained in:
Javier Peletier
2020-12-21 18:35:58 +01:00
parent e1984ddf62
commit 73bb3cae89
16 changed files with 7517 additions and 657 deletions
+100 -75
View File
@@ -3,43 +3,46 @@ package kn
import (
"encoding/json"
"fmt"
"koolnova2mqtt/modbus"
"koolnova2mqtt/watcher"
"log"
"strconv"
)
// MqttClient defines the expected MQTT client pub sub interface
type MqttClient interface {
Publish(topic string, qos byte, retained bool, payload string) error
Subscribe(topic string, callback func(message string)) error
}
// Config defines de Modbus<>MQTT bridge configuration
type Config struct {
ModuleName string
SlaveID byte
Mqtt MqttClient
TopicPrefix string
HassPrefix string
Modbus modbus.Modbus
ModuleName string // name of the module the modbus interface is connected to
SlaveID byte // SlaveID of the module in the bus
TopicPrefix string // MQTT topic prefix to publish information
HassPrefix string // Home Assistant sensor discovery prefix
Mqtt MqttClient // MQTT client
Modbus watcher.Modbus // Modbus client
}
// Bridge bridges Modbus and MQTT protocols
type Bridge struct {
Config
zw *watcher.Watcher
sysw *watcher.Watcher
refresh func()
zones []*Zone
Config // embedded configuration
zw *watcher.Watcher // watcher to detect register changes in zones
sysw *watcher.Watcher // watcher to detect register changes in system registers
zones []*Zone // List of present zones in this module
sys *SysDriver
}
// getActiveZones returns the list of active zones in this module
func getActiveZones(w Watcher) ([]*Zone, error) {
var zones []*Zone
for n := 0; n < NUM_ZONES; n++ {
zone := NewZone(&ZoneConfig{
zone := newZone(&ZoneConfig{
ZoneNumber: n + 1,
Watcher: w,
})
isPresent := zone.IsPresent()
isPresent := zone.isPresent()
if isPresent {
zones = append(zones, zone)
}
@@ -47,6 +50,7 @@ func getActiveZones(w Watcher) ([]*Zone, error) {
return zones, nil
}
// NewBridge returns a new Modbus<>MQTT bridge
func NewBridge(config *Config) *Bridge {
b := &Bridge{
Config: *config,
@@ -54,8 +58,11 @@ func NewBridge(config *Config) *Bridge {
return b
}
// Start starts the bridge, publishes the configuration to Home Assistant topics and publishes
// the current state. Call Start() every time MQTT gets disconnected.
func (b *Bridge) Start() error {
// Define a watcher to watch the zone registers
zw := watcher.New(&watcher.Config{
Address: FIRST_ZONE_REGISTER,
Quantity: TOTAL_ZONE_REGISTERS,
@@ -63,17 +70,20 @@ func (b *Bridge) Start() error {
Modbus: b.Modbus,
})
// Define a watcher to watch the system registers
sysw := watcher.New(&watcher.Config{
Address: FIRST_SYS_REGISTER,
Quantity: TOTAL_SYS_REGISTERS,
SlaveID: b.SlaveID,
Modbus: b.Modbus,
})
b.zw = zw
b.sysw = sysw
sys := NewSys(&SysConfig{
Watcher: b.sysw,
})
b.sys = sys
log.Printf("Starting bridge for %s\n", b.ModuleName)
err := b.poll()
@@ -81,49 +91,18 @@ func (b *Bridge) Start() error {
return err
}
// Get Active zones
zones, err := getActiveZones(b.zw)
b.zones = zones
log.Printf("%d zones are present in %s\n", len(zones), b.ModuleName)
// Downsize watched range to the required number of registers
b.zw.Resize(len(zones) * REG_PER_ZONE)
getHVACMode := func() string {
if !sys.GetSystemEnabled() {
return HVAC_MODE_OFF
}
switch sys.GetSystemKNMode() {
case MODE_AIR_COOLING, MODE_UNDERFLOOR_AIR_COOLING:
return HVAC_MODE_COOL
case MODE_AIR_HEATING, MODE_UNDERFLOOR_HEATING, MODE_UNDERFLOOR_AIR_HEATING:
return HVAC_MODE_HEAT
}
return "unknown"
}
getHoldMode := func() string {
switch sys.GetSystemKNMode() {
case MODE_AIR_COOLING, MODE_AIR_HEATING:
return HOLD_MODE_FAN_ONLY
case MODE_UNDERFLOOR_HEATING:
return HOLD_MODE_UNDERFLOOR_ONLY
case MODE_UNDERFLOOR_AIR_COOLING, MODE_UNDERFLOOR_AIR_HEATING:
return HOLD_MODE_UNDERFLOOR_AND_FAN
}
return "unknown"
}
publishHvacMode := func() {
for _, zone := range zones {
if zone.IsOn() {
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
mode := getHVACMode()
b.Mqtt.Publish(hvacModeTopic, 0, true, mode)
}
}
}
holdModeTopic := b.getSysTopic("holdMode")
holdModeSetTopic := holdModeTopic + "/set"
// configure publishing when modbus registers change
for _, zone := range zones {
zone := zone
currentTempTopic := b.getZoneTopic(zone.ZoneNumber, "currentTemp")
@@ -134,36 +113,45 @@ func (b *Bridge) Start() error {
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
hvacModeSetTopic := hvacModeTopic + "/set"
// In HA there is three HVAC modes: "cool", "heat" and "off". Therefore,
// publish "OFF" if we detect the REG_ENABLED change is off
// Otherwise, publish "heat" or "cool" depending on REG_MODE
zone.OnEnabledChange = func() {
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
var mode string
if zone.IsOn() {
mode = getHVACMode()
if zone.isOn() {
mode = sys.HVACMode()
} else {
mode = HVAC_MODE_OFF
}
b.Mqtt.Publish(hvacModeTopic, 0, true, mode)
}
// if the current temperature changes, forward value to the
// correspondig MQTT topic
zone.OnCurrentTempChange = func(currentTemp float32) {
b.Mqtt.Publish(currentTempTopic, 0, true, fmt.Sprintf("%g", currentTemp))
}
// if the target temperature changes, publish it to MQTT
// this is fired when the target is set over MQTT or via a thermostat
zone.OnTargetTempChange = func(targetTemp float32) {
b.Mqtt.Publish(targetTempTopic, 0, true, fmt.Sprintf("%g", targetTemp))
}
// Publish changes to the fan mode
zone.OnFanModeChange = func(fanMode FanMode) {
b.Mqtt.Publish(fanModeTopic, 0, true, FanMode2Str(fanMode))
}
zone.OnKnModeChange = func(knMode KnMode) {
}
// Subscribe to target temperature set topic in MQTT
err = b.Mqtt.Subscribe(targetTempSetTopic, func(message string) {
targetTemp, err := strconv.ParseFloat(message, 32)
if err != nil {
log.Printf("Error parsing targetTemperature in topic %s: %s", targetTempSetTopic, err)
return
}
err = zone.SetTargetTemperature(float32(targetTemp))
err = zone.setTargetTemperature(float32(targetTemp))
if err != nil {
log.Printf("Cannot set target temperature to %g in zone %d", targetTemp, zone.ZoneNumber)
}
@@ -172,12 +160,13 @@ func (b *Bridge) Start() error {
return err
}
// Subscribe to fan mode set topic in MQTT
err = b.Mqtt.Subscribe(fanModeSetTopic, func(message string) {
fm, err := Str2FanMode(message)
if err != nil {
log.Printf("Unknown fan mode %q in message to zone %d", message, zone.ZoneNumber)
}
err = zone.SetFanMode(fm)
err = zone.setFanMode(fm)
if err != nil {
log.Printf("Cannot set fan mode to %s in zone %d", message, zone.ZoneNumber)
}
@@ -186,21 +175,24 @@ func (b *Bridge) Start() error {
return err
}
// Subscribe to HVAC Mode set topic in MQTT
err = b.Mqtt.Subscribe(hvacModeSetTopic, func(message string) {
// If user sets the Home Assistant HVAC mode to off, turn off this zone
if message == HVAC_MODE_OFF {
err := zone.SetOn(false)
err := zone.setOn(false) // turn zone off (REG_ENABLED)
if err != nil {
log.Printf("Cannot set zone %d to off", zone.ZoneNumber)
}
return
}
// Translate HA HVAC mode to Koolnova's
knMode := sys.GetSystemKNMode()
knMode = ApplyHvacMode(knMode, message)
err = sys.SetSystemKNMode(knMode)
if err != nil {
log.Printf("Cannot set knmode mode to %x in zone %d", knMode, zone.ZoneNumber)
}
err := zone.SetOn(true)
err := zone.setOn(true)
if err != nil {
log.Printf("Cannot set zone %d to on", zone.ZoneNumber)
return
@@ -210,7 +202,9 @@ func (b *Bridge) Start() error {
return err
}
// Subscribe to changes in hold mode:
err = b.Mqtt.Subscribe(holdModeSetTopic, func(message string) {
// Translate HA's hold mode to Koolnova's
knMode := sys.GetSystemKNMode()
knMode = ApplyHoldMode(knMode, message)
err := sys.SetSystemKNMode(knMode)
@@ -222,8 +216,9 @@ func (b *Bridge) Start() error {
return err
}
// Define a Home Assistant thermostat
name := fmt.Sprintf("%s_zone%d", b.ModuleName, zone.ZoneNumber)
config := map[string]interface{}{
b.publishComponent(HA_COMPONENT_CLIMATE, fmt.Sprintf("zone%d", zone.ZoneNumber), map[string]interface{}{
"name": name,
"current_temperature_topic": currentTempTopic,
"precision": 0.1,
@@ -243,62 +238,74 @@ func (b *Bridge) Start() error {
"hold_modes": []string{HOLD_MODE_UNDERFLOOR_ONLY, HOLD_MODE_FAN_ONLY, HOLD_MODE_UNDERFLOOR_AND_FAN},
"hold_state_topic": holdModeTopic,
"hold_command_topic": holdModeSetTopic,
}
})
configJSON, _ := json.Marshal(config)
// <discovery_prefix>/<component>/[<node_id>/]<object_id>/config
b.Mqtt.Publish(fmt.Sprintf("%s/climate/%s/zone%d/config", b.HassPrefix, b.ModuleName, zone.ZoneNumber), 0, true, string(configJSON))
// temperature sensor configuration:
// Define a current temperature sensor:
name = fmt.Sprintf("%s_zone%d_temp", b.ModuleName, zone.ZoneNumber)
config = map[string]interface{}{
b.publishComponent(HA_COMPONENT_SENSOR, fmt.Sprintf("zone%d_temp", zone.ZoneNumber), map[string]interface{}{
"name": name,
"device_class": "temperature",
"state_topic": currentTempTopic,
"unit_of_measurement": "ºC",
"unique_id": name,
}
})
configJSON, _ = json.Marshal(config)
b.Mqtt.Publish(fmt.Sprintf("%s/sensor/%s/zone%d_temp/config", b.HassPrefix, b.ModuleName, zone.ZoneNumber), 0, true, string(configJSON))
// Define a target temperature sensor:
name = fmt.Sprintf("%s_zone%d_target_temp", b.ModuleName, zone.ZoneNumber)
b.publishComponent(HA_COMPONENT_SENSOR, fmt.Sprintf("zone%d_target_temp", zone.ZoneNumber), map[string]interface{}{
"name": name,
"device_class": "temperature",
"state_topic": targetTempTopic,
"unit_of_measurement": "ºC",
"unique_id": name,
})
}
// Publish changes to system registers:
sys.OnACAirflowChange = func(ac ACMachine) {
airflow := sys.GetAirflow(ac)
b.Mqtt.Publish(b.getACTopic(ac, "airflow"), 0, true, strconv.Itoa(airflow))
}
sys.OnACTargetTempChange = func(ac ACMachine) {
targetTemp := sys.GetMachineTargetTemp(ac)
b.Mqtt.Publish(b.getACTopic(ac, "targetTemp"), 0, true, fmt.Sprintf("%g", targetTemp))
}
sys.OnACTargetFanModeChange = func(ac ACMachine) {
targetAirflow := sys.GetTargetFanMode(ac)
b.Mqtt.Publish(b.getACTopic(ac, "fanMode"), 0, true, FanMode2Str(targetAirflow))
}
sys.OnEfficiencyChange = func() {
efficiency := sys.GetEfficiency()
b.Mqtt.Publish(b.getSysTopic("efficiency"), 0, true, strconv.Itoa(efficiency))
}
sys.OnSystemEnabledChange = func() {
enabled := sys.GetSystemEnabled()
b.Mqtt.Publish(b.getSysTopic("enabled"), 0, true, fmt.Sprintf("%t", enabled))
publishHvacMode()
}
sys.OnKnModeChange = func() {
publishHvacMode()
b.Mqtt.Publish(holdModeTopic, 0, true, getHoldMode())
b.publishHvacMode()
}
sys.OnKnModeChange = func() {
b.publishHvacMode()
b.Mqtt.Publish(holdModeTopic, 0, true, sys.HoldMode())
}
// Trigger a callback on all registers so the MQTT broker is updated on connect:
b.zw.TriggerCallbacks()
b.sysw.TriggerCallbacks()
// Publish one-off static information
b.Mqtt.Publish(b.getSysTopic("serialBaud"), 0, true, strconv.Itoa(sys.GetBaudRate()))
b.Mqtt.Publish(b.getSysTopic("serialParity"), 0, true, sys.GetParity())
b.Mqtt.Publish(b.getSysTopic("slaveId"), 0, true, strconv.Itoa(sys.GetSlaveID()))
return nil
}
// poll polls modbus for changes
func (b *Bridge) poll() error {
err := b.zw.Poll()
if err != nil {
@@ -314,13 +321,15 @@ func (b *Bridge) poll() error {
return nil
}
// Tick must be invoked periodically to refesh registers from modbus
// it also samples temperature and calculates a moving average of read temperatures
func (b *Bridge) Tick() error {
err := b.poll()
if err != nil {
return err
}
for _, z := range b.zones {
z.SampleTemperature()
z.sampleTemperature()
}
return nil
}
@@ -336,3 +345,19 @@ func (b *Bridge) getSysTopic(subtopic string) string {
func (b *Bridge) getACTopic(ac ACMachine, subtopic string) string {
return b.getSysTopic(fmt.Sprintf("ac%d/%s", ac, subtopic))
}
func (b *Bridge) publishHvacMode() {
for _, zone := range b.zones {
if zone.isOn() {
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
mode := b.sys.HVACMode()
b.Mqtt.Publish(hvacModeTopic, 0, true, mode)
}
}
}
// publishComponent publishes a Home Assistant component configuration for autodiscovery
func (b *Bridge) publishComponent(component, ObjectID string, config map[string]interface{}) {
configJSON, _ := json.Marshal(config)
b.Mqtt.Publish(fmt.Sprintf("%s/%s/%s/%s/config", b.HassPrefix, component, b.ModuleName, ObjectID), 0, true, string(configJSON))
}
+193
View File
@@ -0,0 +1,193 @@
package kn_test
import (
"encoding/json"
"fmt"
"koolnova2mqtt/kn"
"koolnova2mqtt/modbus"
"sort"
"strconv"
"strings"
"testing"
"github.com/epiclabs-io/ut"
)
type Message struct {
Topic string
Payload interface{}
}
type MqttClientMock struct {
subscriptions map[string]func(message string)
messages []Message
}
func NewMqttClientMock() *MqttClientMock {
return &MqttClientMock{
subscriptions: make(map[string]func(string)),
}
}
func (m *MqttClientMock) Publish(topic string, qos byte, retained bool, payload string) error {
var jsonObject map[string]interface{}
var p interface{}
err := json.Unmarshal([]byte(payload), &jsonObject)
if err == nil {
p = jsonObject
} else {
p = payload
}
m.messages = append(m.messages, Message{
Topic: topic,
Payload: p,
})
return nil
}
func (m *MqttClientMock) simulateMessage(topic string, payload string) {
callback := m.subscriptions[topic]
if callback != nil {
callback(payload)
}
}
func (m *MqttClientMock) LastMessage() *Message {
if len(m.messages) == 0 {
return nil
}
return &m.messages[len(m.messages)-1]
}
func (m *MqttClientMock) Clear() {
m.messages = nil
}
func (m *MqttClientMock) Subscribe(topic string, callback func(message string)) error {
m.subscriptions[topic] = callback
return nil
}
func getKeys(funcMap map[string]func(string)) []string {
keys := make([]string, 0, len(funcMap))
for k := range funcMap {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return strings.Compare(keys[i], keys[j]) < 0
})
return keys
}
type DiffValue struct {
Address uint16
Old uint16
New uint16
}
type TestMessage struct {
ID int
Topic string
Payload string
Diffs []DiffValue
}
func diffState(old, new []uint16) []DiffValue {
if len(old) != len(new) {
panic("not the same length")
}
var diffs []DiffValue
for n := 0; n < len(old); n++ {
if old[n] != new[n] {
diffs = append(diffs, DiffValue{
Old: old[n],
New: new[n],
Address: uint16(n + 1),
})
}
}
return diffs
}
func TestBridge(tx *testing.T) {
t := ut.BeginTest(tx, false)
defer t.FinishTest()
var err error
mqttClient := NewMqttClientMock()
modbusClient := modbus.NewMock()
b := kn.NewBridge(&kn.Config{
ModuleName: "TestModule",
SlaveID: 49,
TopicPrefix: "topicPrefix",
HassPrefix: "hassPrefix",
Mqtt: mqttClient,
Modbus: modbusClient,
})
// Check the correct subscriptions and messages are sent on connect:
err = b.Start()
t.Ok(err)
t.EqualsFile("subscriptions.json", getKeys(mqttClient.subscriptions))
sort.Slice(mqttClient.messages, func(i, j int) bool {
return strings.Compare(mqttClient.messages[i].Topic, mqttClient.messages[j].Topic) < 0
})
t.EqualsFile("connect-messages.json", mqttClient.messages)
mqttClient.Clear()
// Check modbus state is altered when various control messages are received over MQTT
var messages []TestMessage
simulateMessage := func(topic, payload string) {
state := append([]uint16(nil), modbusClient.State[49]...)
mqttClient.simulateMessage(topic, payload)
messages = append(messages, TestMessage{
ID: len(messages) + 1,
Topic: topic,
Payload: payload,
Diffs: diffState(state, modbusClient.State[49]),
})
}
simulateMessage("topicPrefix/TestModule/sys/holdMode/set", kn.HOLD_MODE_FAN_ONLY)
simulateMessage("topicPrefix/TestModule/sys/holdMode/set", kn.HOLD_MODE_UNDERFLOOR_AND_FAN)
simulateMessage("topicPrefix/TestModule/sys/holdMode/set", kn.HOLD_MODE_UNDERFLOOR_ONLY)
simulateMessage("topicPrefix/TestModule/sys/holdMode/set", "bad mode")
hvacModes := []string{kn.HVAC_MODE_COOL, kn.HVAC_MODE_HEAT, kn.HVAC_MODE_OFF}
holdModes := []string{kn.HOLD_MODE_UNDERFLOOR_ONLY, kn.HOLD_MODE_FAN_ONLY, kn.HOLD_MODE_UNDERFLOOR_AND_FAN}
for z := 1; z < 11; z++ {
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/fanMode/set", z), kn.FanMode2Str(kn.FAN_HIGH))
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/fanMode/set", z), kn.FanMode2Str(kn.FAN_MED))
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/fanMode/set", z), kn.FanMode2Str(kn.FAN_LOW))
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/fanMode/set", z), kn.FanMode2Str(kn.FAN_AUTO))
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/fanMode/set", z), "bad mode")
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/targetTemp/set", z), strconv.Itoa(20+z))
for i := 0; i < len(hvacModes); i++ {
simulateMessage(fmt.Sprintf("topicPrefix/TestModule/zone%d/hvacMode/set", z), hvacModes[i])
for j := 0; j < len(holdModes); j++ {
simulateMessage("topicPrefix/TestModule/sys/holdMode/set", holdModes[j])
}
}
}
// diffs.json will contain a list of changes. Each item in the array is the result
// of each simulateMessage call above.
t.EqualsFile("diffs.json", messages)
t.EqualsFile("messages.json", mqttClient.messages)
// simulate changes in temperature by writing random values to temperature registers
mqttClient.Clear()
n := 0
for i := 0; i < 20; i++ {
for z := 1; z < 11; z++ {
t := (i + 15) * 2
n++
modbusClient.WriteRegister(49, uint16((z-1)*kn.REG_PER_ZONE+kn.REG_CURRENT_TEMP), uint16(t))
}
b.Tick()
}
t.EqualsFile("current-temp.json", mqttClient.messages)
}
+28 -32
View File
@@ -2,7 +2,6 @@ package kn
import (
"errors"
"koolnova2mqtt/bimap"
)
const NUM_ZONES = 16
@@ -43,22 +42,6 @@ const MODE_UNDERFLOOR_HEATING KnMode = 0x04
const MODE_UNDERFLOOR_AIR_COOLING KnMode = 0x05
const MODE_UNDERFLOOR_AIR_HEATING KnMode = 0x06
var FanModes = bimap.New(map[interface{}]interface{}{
"off": FAN_OFF,
"low": FAN_LOW,
"medium": FAN_MED,
"high": FAN_HIGH,
"auto": FAN_AUTO,
})
var KnModes = bimap.New(map[interface{}]interface{}{
"air cooling": MODE_AIR_COOLING,
"air heating": MODE_AIR_HEATING,
"underfloor heating": MODE_UNDERFLOOR_HEATING,
"underfloor air cooling": MODE_UNDERFLOOR_AIR_COOLING,
"underfloor air heating": MODE_UNDERFLOOR_AIR_HEATING,
})
const HOLD_MODE_UNDERFLOOR_ONLY = "underfloor"
const HOLD_MODE_FAN_ONLY = "fan"
const HOLD_MODE_UNDERFLOOR_AND_FAN = "underfloor and fan"
@@ -76,28 +59,41 @@ const AC2 ACMachine = 2
const AC3 ACMachine = 3
const AC4 ACMachine = 4
const HA_COMPONENT_SENSOR = "sensor"
const HA_COMPONENT_CLIMATE = "climate"
func FanMode2Str(fm FanMode) string {
st, ok := FanModes.GetInverse(fm)
if !ok {
st = "unknown"
switch fm {
case FAN_OFF:
return "off"
case FAN_LOW:
return "low"
case FAN_MED:
return "medium"
case FAN_HIGH:
return "high"
case FAN_AUTO:
return "auto"
default:
return "unknown"
}
return st.(string)
}
func Str2FanMode(st string) (FanMode, error) {
fm, ok := FanModes.Get(st)
if !ok {
switch st {
case "off":
return FAN_OFF, nil
case "low":
return FAN_LOW, nil
case "medium":
return FAN_MED, nil
case "high":
return FAN_HIGH, nil
case "auto":
return FAN_AUTO, nil
default:
return FAN_OFF, errors.New("Unknown fan mode")
}
return fm.(FanMode), nil
}
func KnMode2Str(hm KnMode) string {
st, ok := KnModes.GetInverse(hm)
if !ok {
st = "unknown"
}
return st.(string)
}
func ApplyHvacMode(knMode KnMode, hvacMode string) KnMode {
+47 -15
View File
@@ -6,7 +6,10 @@ type SysConfig struct {
Watcher Watcher
}
type Sys struct {
// SysDriver watches system registers and allows
// to read/change the module configuration
// notifies callbacks when specific system registers change
type SysDriver struct {
SysConfig
OnACAirflowChange func(ac ACMachine)
OnACTargetTempChange func(ac ACMachine)
@@ -18,8 +21,8 @@ type Sys struct {
var ErrUnknownSerialConfig = errors.New("Uknown serial configuration")
func NewSys(config *SysConfig) *Sys {
s := &Sys{
func NewSys(config *SysConfig) *SysDriver {
s := &SysDriver{
SysConfig: *config,
}
for n := byte(0); n < ACMachines; n++ {
@@ -63,31 +66,31 @@ func NewSys(config *SysConfig) *Sys {
return s
}
func (s *Sys) ReadRegister(n int) int {
func (s *SysDriver) ReadRegister(n int) int {
r := s.Watcher.ReadRegister(uint16(n))
return int(r)
}
func (s *Sys) WriteRegister(n int, value uint16) error {
func (s *SysDriver) WriteRegister(n int, value uint16) error {
return s.Watcher.WriteRegister(uint16(n), value)
}
func (s *Sys) GetAirflow(ac ACMachine) int {
func (s *SysDriver) GetAirflow(ac ACMachine) int {
r := s.ReadRegister(REG_AIRFLOW + int(ac) - 1)
return r
}
func (s *Sys) GetMachineTargetTemp(ac ACMachine) float32 {
func (s *SysDriver) GetMachineTargetTemp(ac ACMachine) float32 {
r := s.ReadRegister(REG_AC_TARGET_TEMP + int(ac) - 1)
return reg2temp(uint16(r))
}
func (s *Sys) GetTargetFanMode(ac ACMachine) FanMode {
func (s *SysDriver) GetTargetFanMode(ac ACMachine) FanMode {
r := s.ReadRegister(REG_AC_TARGET_FAN_MODE + int(ac) - 1)
return FanMode(r)
}
func (s *Sys) GetBaudRate() int {
func (s *SysDriver) GetBaudRate() int {
r := s.ReadRegister(REG_SERIAL_CONFIG)
switch r {
case 2, 6:
@@ -98,7 +101,7 @@ func (s *Sys) GetBaudRate() int {
return 0
}
func (s *Sys) GetParity() string {
func (s *SysDriver) GetParity() string {
r := s.ReadRegister(REG_SERIAL_CONFIG)
switch r {
case 2, 3:
@@ -109,26 +112,55 @@ func (s *Sys) GetParity() string {
return "unknown"
}
func (s *Sys) GetSlaveID() int {
func (s *SysDriver) GetSlaveID() int {
r := s.ReadRegister(REG_SLAVE_ID)
return r
}
func (s *Sys) GetEfficiency() int {
func (s *SysDriver) GetEfficiency() int {
r := s.ReadRegister(REG_EFFICIENCY)
return r
}
func (s *Sys) GetSystemEnabled() bool {
func (s *SysDriver) GetSystemEnabled() bool {
r := s.ReadRegister(REG_SYSTEM_ENABLED)
return r != 0
}
func (s *Sys) GetSystemKNMode() KnMode {
func (s *SysDriver) GetSystemKNMode() KnMode {
r := s.ReadRegister(REG_SYS_KN_MODE)
return KnMode(r)
}
func (s *Sys) SetSystemKNMode(knMode KnMode) error {
func (s *SysDriver) SetSystemKNMode(knMode KnMode) error {
return s.WriteRegister(REG_SYS_KN_MODE, uint16(knMode))
}
// HVACMode returns the HA HVAC mode based on the
// module state
func (s *SysDriver) HVACMode() string {
if !s.GetSystemEnabled() {
return HVAC_MODE_OFF
}
switch s.GetSystemKNMode() {
case MODE_AIR_COOLING, MODE_UNDERFLOOR_AIR_COOLING:
return HVAC_MODE_COOL
case MODE_AIR_HEATING, MODE_UNDERFLOOR_HEATING, MODE_UNDERFLOOR_AIR_HEATING:
return HVAC_MODE_HEAT
}
return "unknown"
}
// HoldMode returns the HA Hold Mode based on the
// module state
func (s *SysDriver) HoldMode() string {
switch s.GetSystemKNMode() {
case MODE_AIR_COOLING, MODE_AIR_HEATING:
return HOLD_MODE_FAN_ONLY
case MODE_UNDERFLOOR_HEATING:
return HOLD_MODE_UNDERFLOOR_ONLY
case MODE_UNDERFLOOR_AIR_COOLING, MODE_UNDERFLOOR_AIR_HEATING:
return HOLD_MODE_UNDERFLOOR_AND_FAN
}
return "unknown"
}
+844
View File
@@ -0,0 +1,844 @@
[
{
"Topic": "hassPrefix/climate/TestModule/zone1/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone1/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone1/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone1/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone1/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone1/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone1",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone1/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone1/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone1"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone10/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone10/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone10/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone10/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone10/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone10/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone10",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone10/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone10/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone10"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone2/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone2/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone2/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone2/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone2/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone2/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone2",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone2/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone2/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone2"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone3/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone3/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone3/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone3/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone3/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone3/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone3",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone3/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone3/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone3"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone4/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone4/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone4/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone4/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone4/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone4/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone4",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone4/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone4/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone4"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone5/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone5/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone5/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone5/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone5/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone5/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone5",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone5/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone5/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone5"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone6/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone6/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone6/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone6/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone6/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone6/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone6",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone6/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone6/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone6"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone7/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone7/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone7/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone7/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone7/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone7/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone7",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone7/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone7/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone7"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone8/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone8/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone8/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone8/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone8/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone8/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone8",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone8/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone8/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone8"
}
},
{
"Topic": "hassPrefix/climate/TestModule/zone9/config",
"Payload": {
"current_temperature_topic": "topicPrefix/TestModule/zone9/currentTemp",
"fan_mode_command_topic": "topicPrefix/TestModule/zone9/fanMode/set",
"fan_mode_state_topic": "topicPrefix/TestModule/zone9/fanMode",
"fan_modes": [
"auto",
"low",
"medium",
"high"
],
"hold_command_topic": "topicPrefix/TestModule/sys/holdMode/set",
"hold_modes": [
"underfloor",
"fan",
"underfloor and fan"
],
"hold_state_topic": "topicPrefix/TestModule/sys/holdMode",
"max_temp": 35,
"min_temp": 15,
"mode_command_topic": "topicPrefix/TestModule/zone9/hvacMode/set",
"mode_state_topic": "topicPrefix/TestModule/zone9/hvacMode",
"modes": [
"cool",
"heat",
"off"
],
"name": "TestModule_zone9",
"precision": 0.1,
"temp_step": 0.5,
"temperature_command_topic": "topicPrefix/TestModule/zone9/targetTemp/set",
"temperature_state_topic": "topicPrefix/TestModule/zone9/targetTemp",
"temperature_unit": "C",
"unique_id": "TestModule_zone9"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone10_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone10_target_temp",
"state_topic": "topicPrefix/TestModule/zone10/targetTemp",
"unique_id": "TestModule_zone10_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone10_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone10_temp",
"state_topic": "topicPrefix/TestModule/zone10/currentTemp",
"unique_id": "TestModule_zone10_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone1_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone1_target_temp",
"state_topic": "topicPrefix/TestModule/zone1/targetTemp",
"unique_id": "TestModule_zone1_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone1_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone1_temp",
"state_topic": "topicPrefix/TestModule/zone1/currentTemp",
"unique_id": "TestModule_zone1_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone2_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone2_target_temp",
"state_topic": "topicPrefix/TestModule/zone2/targetTemp",
"unique_id": "TestModule_zone2_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone2_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone2_temp",
"state_topic": "topicPrefix/TestModule/zone2/currentTemp",
"unique_id": "TestModule_zone2_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone3_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone3_target_temp",
"state_topic": "topicPrefix/TestModule/zone3/targetTemp",
"unique_id": "TestModule_zone3_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone3_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone3_temp",
"state_topic": "topicPrefix/TestModule/zone3/currentTemp",
"unique_id": "TestModule_zone3_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone4_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone4_target_temp",
"state_topic": "topicPrefix/TestModule/zone4/targetTemp",
"unique_id": "TestModule_zone4_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone4_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone4_temp",
"state_topic": "topicPrefix/TestModule/zone4/currentTemp",
"unique_id": "TestModule_zone4_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone5_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone5_target_temp",
"state_topic": "topicPrefix/TestModule/zone5/targetTemp",
"unique_id": "TestModule_zone5_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone5_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone5_temp",
"state_topic": "topicPrefix/TestModule/zone5/currentTemp",
"unique_id": "TestModule_zone5_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone6_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone6_target_temp",
"state_topic": "topicPrefix/TestModule/zone6/targetTemp",
"unique_id": "TestModule_zone6_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone6_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone6_temp",
"state_topic": "topicPrefix/TestModule/zone6/currentTemp",
"unique_id": "TestModule_zone6_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone7_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone7_target_temp",
"state_topic": "topicPrefix/TestModule/zone7/targetTemp",
"unique_id": "TestModule_zone7_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone7_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone7_temp",
"state_topic": "topicPrefix/TestModule/zone7/currentTemp",
"unique_id": "TestModule_zone7_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone8_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone8_target_temp",
"state_topic": "topicPrefix/TestModule/zone8/targetTemp",
"unique_id": "TestModule_zone8_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone8_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone8_temp",
"state_topic": "topicPrefix/TestModule/zone8/currentTemp",
"unique_id": "TestModule_zone8_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone9_target_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone9_target_temp",
"state_topic": "topicPrefix/TestModule/zone9/targetTemp",
"unique_id": "TestModule_zone9_target_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "hassPrefix/sensor/TestModule/zone9_temp/config",
"Payload": {
"device_class": "temperature",
"name": "TestModule_zone9_temp",
"state_topic": "topicPrefix/TestModule/zone9/currentTemp",
"unique_id": "TestModule_zone9_temp",
"unit_of_measurement": "ºC"
}
},
{
"Topic": "topicPrefix/TestModule/sys/ac1/airflow",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac1/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/sys/ac1/targetTemp",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac2/airflow",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac2/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/sys/ac2/targetTemp",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac3/airflow",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac3/fanMode",
"Payload": "high"
},
{
"Topic": "topicPrefix/TestModule/sys/ac3/targetTemp",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac4/airflow",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/ac4/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/sys/ac4/targetTemp",
"Payload": "0"
},
{
"Topic": "topicPrefix/TestModule/sys/efficiency",
"Payload": "3"
},
{
"Topic": "topicPrefix/TestModule/sys/enabled",
"Payload": "true"
},
{
"Topic": "topicPrefix/TestModule/sys/holdMode",
"Payload": "underfloor"
},
{
"Topic": "topicPrefix/TestModule/sys/serialBaud",
"Payload": "9600"
},
{
"Topic": "topicPrefix/TestModule/sys/serialParity",
"Payload": "even"
},
{
"Topic": "topicPrefix/TestModule/sys/slaveId",
"Payload": "49"
},
{
"Topic": "topicPrefix/TestModule/zone1/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone1/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone1/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone1/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone1/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone10/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone10/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone10/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone10/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone2/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone2/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone2/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone2/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone3/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone3/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone3/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone3/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone4/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone4/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone4/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone4/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone5/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone5/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone5/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone5/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/fanMode",
"Payload": "high"
},
{
"Topic": "topicPrefix/TestModule/zone6/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone6/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone6/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone6/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/fanMode",
"Payload": "high"
},
{
"Topic": "topicPrefix/TestModule/zone7/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone7/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone7/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone7/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone8/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone8/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone8/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone8/targetTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/fanMode",
"Payload": "auto"
},
{
"Topic": "topicPrefix/TestModule/zone9/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone9/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone9/hvacMode",
"Payload": "heat"
},
{
"Topic": "topicPrefix/TestModule/zone9/targetTemp",
"Payload": "20.5"
}
]
+802
View File
@@ -0,0 +1,802 @@
[
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "15"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "15.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "16"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "16.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "17"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "17.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "18"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "18.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "19"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "19.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "20"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "20.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "21"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "21.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "22"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "22.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "23"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "23.5"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "24"
},
{
"Topic": "topicPrefix/TestModule/zone1/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone2/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone3/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone4/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone5/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone6/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone7/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone8/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone9/currentTemp",
"Payload": "24.5"
},
{
"Topic": "topicPrefix/TestModule/zone10/currentTemp",
"Payload": "24.5"
}
]
+2132
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
[
"topicPrefix/TestModule/sys/holdMode/set",
"topicPrefix/TestModule/zone1/fanMode/set",
"topicPrefix/TestModule/zone1/hvacMode/set",
"topicPrefix/TestModule/zone1/targetTemp/set",
"topicPrefix/TestModule/zone10/fanMode/set",
"topicPrefix/TestModule/zone10/hvacMode/set",
"topicPrefix/TestModule/zone10/targetTemp/set",
"topicPrefix/TestModule/zone2/fanMode/set",
"topicPrefix/TestModule/zone2/hvacMode/set",
"topicPrefix/TestModule/zone2/targetTemp/set",
"topicPrefix/TestModule/zone3/fanMode/set",
"topicPrefix/TestModule/zone3/hvacMode/set",
"topicPrefix/TestModule/zone3/targetTemp/set",
"topicPrefix/TestModule/zone4/fanMode/set",
"topicPrefix/TestModule/zone4/hvacMode/set",
"topicPrefix/TestModule/zone4/targetTemp/set",
"topicPrefix/TestModule/zone5/fanMode/set",
"topicPrefix/TestModule/zone5/hvacMode/set",
"topicPrefix/TestModule/zone5/targetTemp/set",
"topicPrefix/TestModule/zone6/fanMode/set",
"topicPrefix/TestModule/zone6/hvacMode/set",
"topicPrefix/TestModule/zone6/targetTemp/set",
"topicPrefix/TestModule/zone7/fanMode/set",
"topicPrefix/TestModule/zone7/hvacMode/set",
"topicPrefix/TestModule/zone7/targetTemp/set",
"topicPrefix/TestModule/zone8/fanMode/set",
"topicPrefix/TestModule/zone8/hvacMode/set",
"topicPrefix/TestModule/zone8/targetTemp/set",
"topicPrefix/TestModule/zone9/fanMode/set",
"topicPrefix/TestModule/zone9/hvacMode/set",
"topicPrefix/TestModule/zone9/targetTemp/set"
]
+36 -31
View File
@@ -17,6 +17,7 @@ type ZoneConfig struct {
Watcher Watcher
}
// Zone is a driver to interact with climate zones
type Zone struct {
ZoneConfig
OnEnabledChange func()
@@ -28,27 +29,31 @@ type Zone struct {
temp *average.MovingAverage
}
func NewZone(config *ZoneConfig) *Zone {
// newZone creates a new climate zone with the supplied configuration
// Invokes callbacks when specific registers change
// reads registers to return current state
// sets the appropriate registers when set* methods are invoked
func newZone(config *ZoneConfig) *Zone {
z := &Zone{
ZoneConfig: *config,
temp: average.New(300),
}
z.RegisterCallback(REG_ENABLED, func() {
z.registerCallback(REG_ENABLED, func() {
if z.OnEnabledChange != nil {
z.OnEnabledChange()
}
})
z.RegisterCallback(REG_TARGET_TEMP, func() {
z.registerCallback(REG_TARGET_TEMP, func() {
if z.OnTargetTempChange == nil {
return
}
temp := z.GetTargetTemperature()
temp := z.getTargetTemperature()
z.OnTargetTempChange(temp)
})
z.RegisterCallback(REG_MODE, func() {
fanMode := z.GetFanMode()
hvacMode := z.GetKnMode()
z.registerCallback(REG_MODE, func() {
fanMode := z.getFanMode()
hvacMode := z.getKnMode()
if z.OnFanModeChange != nil {
z.OnFanModeChange(fanMode)
}
@@ -59,54 +64,54 @@ func NewZone(config *ZoneConfig) *Zone {
return z
}
func (z *Zone) RegisterCallback(num int, f func()) {
func (z *Zone) registerCallback(num int, f func()) {
z.Watcher.RegisterCallback(uint16((z.ZoneNumber-1)*REG_PER_ZONE+num), func(address uint16) {
f()
})
}
func (z *Zone) ReadRegister(num int) uint16 {
func (z *Zone) readRegister(num int) uint16 {
return z.Watcher.ReadRegister(uint16((z.ZoneNumber-1)*REG_PER_ZONE + num))
}
func (z *Zone) WriteRegister(num int, value uint16) error {
func (z *Zone) writeRegister(num int, value uint16) error {
return z.Watcher.WriteRegister(uint16((z.ZoneNumber-1)*REG_PER_ZONE+num), value)
}
func (z *Zone) IsOn() bool {
r1 := z.ReadRegister(REG_ENABLED)
func (z *Zone) isOn() bool {
r1 := z.readRegister(REG_ENABLED)
return r1&0x1 != 0
}
func (z *Zone) SetOn(on bool) error {
func (z *Zone) setOn(on bool) error {
var r1 uint16
if on {
r1 = 0x3
} else {
r1 = 0x2
}
return z.WriteRegister(REG_ENABLED, r1)
return z.writeRegister(REG_ENABLED, r1)
}
func (z *Zone) IsPresent() bool {
r1 := z.ReadRegister(REG_ENABLED)
func (z *Zone) isPresent() bool {
r1 := z.readRegister(REG_ENABLED)
return r1&0x2 != 0
}
func (z *Zone) getCurrentTemperature() float32 {
r4 := z.ReadRegister(REG_CURRENT_TEMP)
r4 := z.readRegister(REG_CURRENT_TEMP)
return reg2temp(r4)
}
func (z *Zone) GetCurrentTemperature() float32 {
func (z *Zone) getAverageCurrentTemperature() float32 {
return float32(math.Round(z.temp.Avg()*10) / 10)
}
func (z *Zone) SampleTemperature() {
func (z *Zone) sampleTemperature() {
sample := z.getCurrentTemperature()
z.temp.Add(float64(sample))
if z.OnCurrentTempChange != nil {
t := z.GetCurrentTemperature()
t := z.getAverageCurrentTemperature()
if t != z.lastTemp {
z.lastTemp = t
z.OnCurrentTempChange(t)
@@ -114,28 +119,28 @@ func (z *Zone) SampleTemperature() {
}
}
func (z *Zone) GetTargetTemperature() float32 {
r3 := z.ReadRegister(REG_TARGET_TEMP)
func (z *Zone) getTargetTemperature() float32 {
r3 := z.readRegister(REG_TARGET_TEMP)
return reg2temp(r3)
}
func (z *Zone) SetTargetTemperature(targetTemp float32) error {
return z.WriteRegister(REG_TARGET_TEMP, temp2reg(targetTemp))
func (z *Zone) setTargetTemperature(targetTemp float32) error {
return z.writeRegister(REG_TARGET_TEMP, temp2reg(targetTemp))
}
func (z *Zone) GetFanMode() FanMode {
r2 := z.ReadRegister(REG_MODE)
func (z *Zone) getFanMode() FanMode {
r2 := z.readRegister(REG_MODE)
return (FanMode)(r2&0x00F0) >> 4
}
func (z *Zone) SetFanMode(fanMode FanMode) error {
r2 := z.ReadRegister(REG_MODE) & 0x000F
func (z *Zone) setFanMode(fanMode FanMode) error {
r2 := z.readRegister(REG_MODE) & 0x000F
fm := (uint16(fanMode) & 0x000F) << 4
return z.WriteRegister(REG_MODE, r2|fm)
return z.writeRegister(REG_MODE, r2|fm)
}
func (z *Zone) GetKnMode() KnMode {
r2 := z.ReadRegister(REG_MODE)
func (z *Zone) getKnMode() KnMode {
r2 := z.readRegister(REG_MODE)
return (KnMode)(r2 & 0x000F)
}