mirror of
https://github.com/jpeletier/koolnova2mqtt.git
synced 2026-01-11 15:11:43 +00:00
map hvac /hold modes. use modbus module
This commit is contained in:
72
kn/bridge.go
72
kn/bridge.go
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
20
kn/zone.go
20
kn/zone.go
@@ -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
65
main.go
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user