Files
koolnova2mqtt/kn/zone.go
2020-12-22 01:22:00 +01:00

154 lines
3.4 KiB
Go

package kn
import (
"math"
average "github.com/RobinUS2/golang-moving-average"
)
type Watcher interface {
ReadRegister(address uint16) (value uint16)
WriteRegister(address uint16, value uint16) error
RegisterCallback(address uint16, callback func(address uint16))
}
type ZoneConfig struct {
ZoneNumber int
Watcher Watcher
}
// Zone is a driver to interact with climate zones
type Zone struct {
ZoneConfig
OnEnabledChange func()
OnCurrentTempChange func(newTemp float32)
OnTargetTempChange func(newTemp float32)
OnFanModeChange func(newMode FanMode)
OnKnModeChange func(newMode KnMode)
lastTemp float32
temp *average.MovingAverage
}
// 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() {
if z.OnEnabledChange != nil {
z.OnEnabledChange()
}
})
z.registerCallback(REG_TARGET_TEMP, func() {
if z.OnTargetTempChange == nil {
return
}
temp := z.getTargetTemperature()
z.OnTargetTempChange(temp)
})
z.registerCallback(REG_MODE, func() {
fanMode := z.getFanMode()
hvacMode := z.getKnMode()
if z.OnFanModeChange != nil {
z.OnFanModeChange(fanMode)
}
if z.OnKnModeChange != nil {
z.OnKnModeChange(hvacMode)
}
})
return z
}
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 {
return z.Watcher.ReadRegister(uint16((z.ZoneNumber-1)*REG_PER_ZONE + num))
}
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)
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
}
func (z *Zone) getCurrentTemperature() float32 {
r4 := z.readRegister(REG_CURRENT_TEMP)
return reg2temp(r4)
}
func (z *Zone) getAverageCurrentTemperature() float32 {
return float32(math.Round(z.temp.Avg()*10) / 10)
}
func (z *Zone) sampleTemperature() {
sample := z.getCurrentTemperature()
z.temp.Add(float64(sample))
if z.OnCurrentTempChange != nil {
t := z.getAverageCurrentTemperature()
if t != z.lastTemp {
z.lastTemp = t
z.OnCurrentTempChange(t)
}
}
}
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) 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
fm := (uint16(fanMode) & 0x000F) << 4
return z.writeRegister(REG_MODE, r2|fm)
}
func (z *Zone) getKnMode() KnMode {
r2 := z.readRegister(REG_MODE)
return (KnMode)(r2 & 0x000F)
}
func reg2temp(r uint16) float32 {
return float32(0x00FF&r) / 2.0
}
func temp2reg(t float32) uint16 {
return uint16(t * 2)
}