map hvac /hold modes. use modbus module

This commit is contained in:
Javier Peletier
2020-12-16 00:08:42 +01:00
parent 10594498c5
commit ca479304f2
6 changed files with 150 additions and 70 deletions

View File

@@ -3,6 +3,7 @@ package kn
import (
"encoding/json"
"fmt"
"koolnova2mqtt/modbus"
"koolnova2mqtt/watcher"
"log"
"strconv"
@@ -12,14 +13,13 @@ type Publish func(topic string, qos byte, retained bool, payload string)
type Subscribe func(topic string, callback func(message string)) error
type Config struct {
ModuleName string
SlaveID byte
Publish Publish
Subscribe Subscribe
TopicPrefix string
HassPrefix string
ReadRegister watcher.ReadRegister
WriteRegister watcher.WriteRegister
ModuleName string
SlaveID byte
Publish Publish
Subscribe Subscribe
TopicPrefix string
HassPrefix string
Modbus modbus.Modbus
}
type Bridge struct {
@@ -54,8 +54,7 @@ func NewBridge(config *Config) *Bridge {
Quantity: TOTAL_ZONE_REGISTERS,
RegisterSize: 2,
SlaveID: config.SlaveID,
Read: config.ReadRegister,
Write: config.WriteRegister,
Modbus: config.Modbus,
})
sysw := watcher.New(&watcher.Config{
@@ -63,8 +62,7 @@ func NewBridge(config *Config) *Bridge {
Quantity: TOTAL_SYS_REGISTERS,
RegisterSize: 2,
SlaveID: config.SlaveID,
Read: config.ReadRegister,
Write: config.WriteRegister,
Modbus: config.Modbus,
})
b := &Bridge{
@@ -146,8 +144,18 @@ func (b *Bridge) Start() error {
fanModeTopic := b.getZoneTopic(zone.ZoneNumber, "fanMode")
fanModeSetTopic := fanModeTopic + "/set"
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
hvacModeTopicSet := hvacModeTopic + "/set"
hvacModeSetTopic := hvacModeTopic + "/set"
zone.OnEnabledChange = func() {
hvacModeTopic := b.getZoneTopic(zone.ZoneNumber, "hvacMode")
var mode string
if zone.IsOn() {
mode = getHVACMode()
} else {
mode = HVAC_MODE_OFF
}
b.Publish(hvacModeTopic, 0, true, mode)
}
zone.OnCurrentTempChange = func(currentTemp float32) {
b.Publish(currentTempTopic, 0, true, fmt.Sprintf("%g", currentTemp))
}
@@ -190,6 +198,42 @@ func (b *Bridge) Start() error {
return err
}
err = b.Subscribe(hvacModeSetTopic, func(message string) {
if message == HVAC_MODE_OFF {
err := zone.SetOn(false)
if err != nil {
log.Printf("Cannot set zone %d to off", zone.ZoneNumber)
}
return
}
err := zone.SetOn(true)
if err != nil {
log.Printf("Cannot set zone %d to on", zone.ZoneNumber)
return
}
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)
}
})
if err != nil {
return err
}
err = b.Subscribe(holdModeSetTopic, func(message string) {
knMode := sys.GetSystemKNMode()
knMode = ApplyHoldMode(knMode, message)
err := sys.SetSystemKNMode(knMode)
if err != nil {
log.Printf("Cannot set knmode mode to %x in zone %d", knMode, zone.ZoneNumber)
}
})
if err != nil {
return err
}
name := fmt.Sprintf("%s_zone%d", b.ModuleName, zone.ZoneNumber)
config := map[string]interface{}{
"name": name,
@@ -204,7 +248,7 @@ func (b *Bridge) Start() error {
"max_temp": 35,
"modes": []string{HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF},
"mode_state_topic": hvacModeTopic,
"mode_command_topic": hvacModeTopicSet,
"mode_command_topic": hvacModeSetTopic,
"fan_modes": []string{"auto", "low", "medium", "high"},
"fan_mode_state_topic": fanModeTopic,
"fan_mode_command_topic": fanModeSetTopic,

View File

@@ -99,3 +99,47 @@ func KnMode2Str(hm KnMode) string {
}
return st.(string)
}
func ApplyHvacMode(knMode KnMode, hvacMode string) KnMode {
switch knMode {
case MODE_AIR_COOLING:
if hvacMode == HVAC_MODE_HEAT {
return MODE_AIR_HEATING
}
case MODE_AIR_HEATING:
if hvacMode == HVAC_MODE_COOL {
return MODE_AIR_COOLING
}
case MODE_UNDERFLOOR_AIR_COOLING:
if hvacMode == HVAC_MODE_HEAT {
return MODE_UNDERFLOOR_AIR_HEATING
}
case MODE_UNDERFLOOR_AIR_HEATING, MODE_UNDERFLOOR_HEATING:
if hvacMode == HVAC_MODE_COOL {
return MODE_UNDERFLOOR_AIR_COOLING
}
}
return knMode
}
func ApplyHoldMode(knMode KnMode, holdMode string) KnMode {
cool := knMode == MODE_AIR_COOLING || knMode == MODE_UNDERFLOOR_AIR_COOLING
switch holdMode {
case HOLD_MODE_FAN_ONLY:
if cool {
return MODE_AIR_COOLING
}
return MODE_AIR_HEATING
case HOLD_MODE_UNDERFLOOR_ONLY:
if cool {
return MODE_UNDERFLOOR_AIR_COOLING
}
return MODE_UNDERFLOOR_HEATING
case HOLD_MODE_UNDERFLOOR_AND_FAN:
if cool {
return MODE_UNDERFLOOR_AIR_COOLING
}
return MODE_UNDERFLOOR_AIR_HEATING
}
return knMode
}

View File

@@ -68,6 +68,10 @@ func (s *Sys) ReadRegister(n int) int {
return int(r[1])
}
func (s *Sys) WriteRegister(n int, value uint16) error {
return s.Watcher.WriteRegister(uint16(n), value)
}
func (s *Sys) GetAirflow(ac ACMachine) int {
r := s.ReadRegister(REG_AIRFLOW + int(ac) - 1)
return r
@@ -124,3 +128,7 @@ func (s *Sys) GetSystemKNMode() KnMode {
r := s.ReadRegister(REG_SYS_KN_MODE)
return KnMode(r)
}
func (s *Sys) SetSystemKNMode(knMode KnMode) error {
return s.WriteRegister(REG_SYS_KN_MODE, uint16(knMode))
}

View File

@@ -17,6 +17,7 @@ type ZoneConfig struct {
type Zone struct {
ZoneConfig
OnEnabledChange func()
OnCurrentTempChange func(newTemp float32)
OnTargetTempChange func(newTemp float32)
OnFanModeChange func(newMode FanMode)
@@ -27,6 +28,11 @@ func NewZone(config *ZoneConfig) *Zone {
z := &Zone{
ZoneConfig: *config,
}
z.RegisterCallback(REG_ENABLED, func() {
if z.OnEnabledChange != nil {
z.OnEnabledChange()
}
})
z.RegisterCallback(REG_CURRENT_TEMP, func() {
if z.OnCurrentTempChange == nil {
return
@@ -45,7 +51,7 @@ func NewZone(config *ZoneConfig) *Zone {
})
z.RegisterCallback(REG_MODE, func() {
fanMode := z.GetFanMode()
hvacMode := z.GetHvacMode()
hvacMode := z.GetKnMode()
if z.OnFanModeChange != nil {
z.OnFanModeChange(fanMode)
}
@@ -76,6 +82,16 @@ func (z *Zone) IsOn() bool {
return r1&0x1 != 0
}
func (z *Zone) SetOn(on bool) error {
var r1 uint16
if on {
r1 = 0x3
} else {
r1 = 0x2
}
return z.WriteRegister(REG_ENABLED, r1)
}
func (z *Zone) IsPresent() bool {
r1 := z.ReadRegister(REG_ENABLED)
return r1&0x2 != 0
@@ -106,7 +122,7 @@ func (z *Zone) SetFanMode(fanMode FanMode) error {
return z.WriteRegister(REG_MODE, r2|fm)
}
func (z *Zone) GetHvacMode() KnMode {
func (z *Zone) GetKnMode() KnMode {
r2 := z.ReadRegister(REG_MODE)
return (KnMode)(r2 & 0x000F)
}

65
main.go
View File

@@ -6,41 +6,19 @@ import (
"flag"
"fmt"
"koolnova2mqtt/kn"
"koolnova2mqtt/watcher"
"koolnova2mqtt/modbus"
"log"
"os"
"os/signal"
"regexp"
"strconv"
"strings"
"sync"
"syscall"
"time"
MQTT "github.com/eclipse/paho.mqtt.golang"
"github.com/goburrow/modbus"
)
func buildReader(handler *modbus.RTUClientHandler, client modbus.Client, lock *sync.RWMutex) watcher.ReadRegister {
return func(slaveID byte, address uint16, quantity uint16) (results []byte, err error) {
lock.Lock()
defer lock.Unlock()
handler.SlaveId = slaveID
results, err = client.ReadHoldingRegisters(address-1, quantity)
return results, err
}
}
func buildWriter(handler *modbus.RTUClientHandler, client modbus.Client, lock *sync.RWMutex) watcher.WriteRegister {
return func(slaveID byte, address uint16, value uint16) (results []byte, err error) {
lock.Lock()
defer lock.Unlock()
handler.SlaveId = slaveID
results, err = client.WriteSingleRegister(address-1, value)
return results, err
}
}
func generateNodeName(slaveID string, port string) string {
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
@@ -79,24 +57,18 @@ func main() {
flag.Parse()
handler := modbus.NewRTUClientHandler(*modbusPort)
handler.BaudRate = *modbusPortBaudRate
handler.DataBits = *modbusDataBits
handler.Parity = *modbusPortParity
handler.StopBits = *modbusStopBits
handler.Timeout = 5 * time.Second
err := handler.Connect()
mb, err := modbus.New(&modbus.Config{
Port: *modbusPort,
BaudRate: *modbusPortBaudRate,
DataBits: *modbusDataBits,
Parity: *modbusPortParity,
StopBits: *modbusStopBits,
Timeout: 5 * time.Second,
})
if err != nil {
log.Fatalf("Error connecting modbus: %s", err)
log.Fatalf("Error initializing modbus: %s", err)
}
defer handler.Close()
modbusClient := modbus.NewClient(handler)
lock := &sync.RWMutex{}
registerReader := buildReader(handler, modbusClient, lock)
registerWriter := buildWriter(handler, modbusClient, lock)
defer mb.Close()
var mqttClient MQTT.Client
publish := func(topic string, qos byte, retained bool, payload string) {
@@ -147,14 +119,13 @@ func main() {
log.Fatalf("Error parsing slaveID list")
}
bridge := kn.NewBridge(&kn.Config{
ModuleName: slaveName,
SlaveID: byte(slaveID),
Publish: publish,
Subscribe: subscribe,
TopicPrefix: *prefix,
HassPrefix: *hassPrefix,
ReadRegister: registerReader,
WriteRegister: registerWriter,
ModuleName: slaveName,
SlaveID: byte(slaveID),
Publish: publish,
Subscribe: subscribe,
TopicPrefix: *prefix,
HassPrefix: *hassPrefix,
Modbus: mb,
})
bridges = append(bridges, bridge)
}

View File

@@ -3,18 +3,15 @@ package watcher
import (
"bytes"
"errors"
"koolnova2mqtt/modbus"
"sync"
)
type ReadRegister func(slaveID byte, address uint16, quantity uint16) (results []byte, err error)
type WriteRegister func(slaveID byte, address uint16, value uint16) (results []byte, err error)
type Config struct {
Address uint16
Quantity uint16
SlaveID byte
Read ReadRegister
Write WriteRegister
Modbus modbus.Modbus
RegisterSize int
}
@@ -43,7 +40,7 @@ func (w *Watcher) RegisterCallback(address uint16, callback func(address uint16)
func (w *Watcher) Poll() error {
w.lock.Lock()
newState, err := w.Read(w.SlaveID, w.Address, w.Quantity)
newState, err := w.Modbus.ReadRegister(w.SlaveID, w.Address, w.Quantity)
if err != nil {
w.lock.Unlock()
return err
@@ -100,7 +97,7 @@ func (w *Watcher) ReadRegister(address uint16) (value []byte) {
func (w *Watcher) WriteRegister(address uint16, value uint16) error {
w.lock.Lock()
results, err := w.Write(w.SlaveID, address, value)
results, err := w.Modbus.WriteRegister(w.SlaveID, address, value)
if err != nil {
w.lock.Unlock()
return err