Introduce SetCharacteristicValueFunc to the Mapping

This function allows either replacing or modifying the usual
Exposed->Characteristic value propagation.

It also adds a CurrentValue to the mapping to monitor for
changes/updates, regardless of whether the value was updated into the
Characteristic. This will be the source of truth.
This commit is contained in:
Darell Tan
2024-11-24 15:52:40 +08:00
parent 8d5dc24d7d
commit e9b0e4b0f9
2 changed files with 70 additions and 3 deletions

View File

@@ -697,9 +697,34 @@ func (br *Bridge) UpdateAccessoryState(devName string, payload []byte) {
if BRIDGE_DEVMODE {
log.Printf("updating %q to %+v", prop, newVal)
}
_, errCode := mapping.SetCharacteristicValue(newVal)
// convert to Characteristic value to determine if it has changed
// since we don't know what the Exposed -> Characteristic mapping would be
newCv, err := mapping.ToCharacteristicValue(newVal)
if err != nil {
log.Printf("unable to convert characteristic value for %q: %v", prop, err)
continue
}
oldCv := mapping.SetCurrentValue(newCv)
changed := oldCv != newCv
// call the Set func if defined
doDefault := true
setFunc := mapping.SetCharacteristicValueFunc
if setFunc != nil {
doDefault, err = setFunc(mapping, newCv, changed)
if err != nil {
log.Printf("SetCharacteristicValueFunc for %s error: %+v", mapping, err)
continue
}
}
if doDefault {
_, errCode := mapping.Characteristic.SetValueRequest(newCv, nil)
if errCode != 0 {
log.Printf("unable to update characteristic value for %q: %d", prop, errCode)
}
}
}
}

42
z2m.go
View File

@@ -5,6 +5,7 @@ import (
"fmt"
"regexp"
"strings"
"sync/atomic"
"reflect"
"runtime"
@@ -228,6 +229,9 @@ func uniq[T comparable](items []T) []T {
//////////////////////////////
// sentinel nil value for atomic.Value
var nilValue = &struct{}{}
// Maps a zigbee2mqtt device property into a HAP characteristic.
// Contains convenience methods to translate values between the two systems.
// z2m "binary" types are inherently translated to bool using the ValueOn/Off properties defined,
@@ -240,6 +244,15 @@ type ExposeMapping struct {
Characteristic *characteristic.C
Translator MappingTranslator
// Hook function for setting Characteristic value.
// It returns a bool to allow/prevent the normal setting of Characteristic value.
// If an error is returned, the handler stops any further processing for this mapping.
SetCharacteristicValueFunc func(m *ExposeMapping, newVal any, changed bool) (doDefault bool, err error)
// keeps track of current value, regardless of whether it's been
// transferred to Characteristic or not
currentVal atomic.Value
}
// Creates a new ExposeMapping
@@ -262,6 +275,11 @@ func (m *ExposeMapping) ToExposedValue(v any) (any, error) {
return m.Translator.ToExposedValue(v)
}
// Converts an Exposed value to a Characteristic value
func (m *ExposeMapping) ToCharacteristicValue(v any) (any, error) {
return m.Translator.ToCharacteristicValue(v)
}
// Calls c.SetValueRequest() with the translated exposed value
// if the error code is -1, there was a translation error.
// Otherwise it's a HAP error code
@@ -274,6 +292,30 @@ func (m *ExposeMapping) SetCharacteristicValue(v any) (any, int) {
return m.Characteristic.SetValueRequest(cv, nil)
}
// Gets the current value
func (m *ExposeMapping) CurrentValue() any {
v := m.currentVal.Load()
if v == nilValue {
return nil
}
return v
}
// Sets the current value for this mapping.
// This value is persisted and used for detecting changes, regardless of
// whether it is propagated to the Characteristic
func (m *ExposeMapping) SetCurrentValue(v any) any {
if v == nil {
v = nilValue
}
old := m.currentVal.Swap(v)
if old == nilValue {
old = nil
}
return old
}
//////////////////////////////
// Function that creates services from a z2m Device, invoked by createAccessory()