Skip to content

Commit

Permalink
Improve state handling logic
Browse files Browse the repository at this point in the history
  • Loading branch information
claes committed Nov 24, 2024
1 parent ca61cfa commit 21acfd9
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 68 deletions.
70 changes: 19 additions & 51 deletions internal/statemachine.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,59 +26,27 @@ func CreateStateMachineMQTTBridge(name string) StateMachineMQTTBridge {

// Output

func setIkeaTretaktPower(topic string, on bool) []MQTTPublish {
func setIkeaTretaktPower(topic string, on bool) MQTTPublish {
state := "OFF"
if on {
state = "ON"
}
return []MQTTPublish{
{
Topic: topic,
Payload: fmt.Sprintf("{\"state\": \"%s\"}", state),
Qos: 2,
Retained: true,
},
return MQTTPublish{
Topic: topic,
Payload: fmt.Sprintf("{\"state\": \"%s\"}", state),
Qos: 2,
Retained: true,
}
}

func livingroomFloorlampOutput(on bool) []MQTTPublish {
return setIkeaTretaktPower("zigbee2mqtt/livingroom-floorlamp/set", on)
return []MQTTPublish{setIkeaTretaktPower("zigbee2mqtt/livingroom-floorlamp/set", on)}
}

func kitchenAmpPowerOutput(on bool) []MQTTPublish {
return setIkeaTretaktPower("zigbee2mqtt/kitchen-amp/set", on)
return []MQTTPublish{setIkeaTretaktPower("zigbee2mqtt/kitchen-amp/set", on)}
}

// func livingroomFloorlampOutput(on bool) []MQTTPublish {
// state := "OFF"
// if on {
// state = "ON"
// }
// return []MQTTPublish{
// {
// Topic: "zigbee2mqtt/livingroom-floorlamp/set",
// Payload: fmt.Sprintf("{\"state\": \"%s\"}", state),
// Qos: 2,
// Retained: true,
// },
// }
// }

// func kitchenAmpPowerOutput(on bool) []MQTTPublish {
// state := "OFF"
// if on {
// state = "ON"
// }
// return []MQTTPublish{
// {
// Topic: "zigbee2mqtt/kitechen-amp/set",
// Payload: fmt.Sprintf("{\"state\": \"%s\"}", state),
// Qos: 2,
// Retained: true,
// },
// }
// }

func tvPowerOffOutput() []MQTTPublish {
return []MQTTPublish{
{
Expand Down Expand Up @@ -186,47 +154,47 @@ func mpdPlayOutput() []MQTTPublish {
// Guards

func (l *StateMachineMQTTBridge) guardTurnOnLivingroomLamp(_ context.Context, _ ...any) bool {
check := l.stateValueMap.require("phonePresent") &&
l.stateValueMap.require("nighttime") &&
l.stateValueMap.requireRecently("livingroomPresence", 10*time.Minute)
check := l.stateValueMap.requireTrue("phonePresent") &&
l.stateValueMap.requireTrue("nighttime") &&
l.stateValueMap.requireTrueRecently("livingroomPresence", 10*time.Minute)
slog.Info("guardTurnOnLamp", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardTurnOffLivingroomLamp(_ context.Context, _ ...any) bool {
check := l.stateValueMap.requireNot("phonePresent") ||
l.stateValueMap.requireNot("nighttime") ||
l.stateValueMap.requireNotRecently("livingroomPresence", 10*time.Minute)
check := l.stateValueMap.requireFalse("phonePresent") ||
l.stateValueMap.requireFalse("nighttime") ||
l.stateValueMap.requireTrueNotRecently("livingroomPresence", 10*time.Minute)
slog.Info("guardTurnOffLamp", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardStateTvOn(_ context.Context, _ ...any) bool {
check := l.stateValueMap.require("tvpower")
check := l.stateValueMap.requireTrue("tvpower")
slog.Info("guardStateTvOn", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardStateTvOff(_ context.Context, _ ...any) bool {
check := l.stateValueMap.requireNot("tvpower")
check := l.stateValueMap.requireFalse("tvpower")
slog.Info("guardStateTvOff", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardStateTvOffLong(_ context.Context, _ ...any) bool {
check := l.stateValueMap.requireNotRecently("tvpower", 30*time.Minute)
check := l.stateValueMap.requireTrueNotRecently("tvpower", 30*time.Minute)
slog.Info("guardStateTvOff", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardStateKitchenAmpOn(_ context.Context, _ ...any) bool {
check := l.stateValueMap.require("kitchenaudioplaying")
check := l.stateValueMap.requireTrue("kitchenaudioplaying")
slog.Info("guardStateKitchenAmpOn", "check", check)
return check
}

func (l *StateMachineMQTTBridge) guardStateKitchenAmpOff(_ context.Context, _ ...any) bool {
check := l.stateValueMap.requireNotRecently("kitchenaudioplaying", 10*time.Minute)
check := l.stateValueMap.requireTrueNotRecently("kitchenaudioplaying", 10*time.Minute)
slog.Info("guardStateKitchenAmpOn", "check", check)
return check
}
Expand Down
54 changes: 37 additions & 17 deletions internal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ type StateValue struct {
value bool
isDefined bool
lastUpdate time.Time
lastChange time.Time
lastSetTrue time.Time
lastSetFalse time.Time
}
Expand All @@ -146,22 +147,33 @@ func NewStateValueMap() StateValueMap {
func (s *StateValueMap) setState(key string, value bool) {
existingState, exists := s.stateValueMap[key]

if !exists || existingState.value != value {
newState := StateValue{
value: value,
isDefined: true,
lastUpdate: time.Now(),
lastSetTrue: existingState.lastSetTrue,
lastSetFalse: existingState.lastSetFalse,
}
now := time.Now()
if value {
newState.lastSetTrue = now
now := time.Now()
var updatedState StateValue
if exists {
if existingState.value == value {
// don't change value
} else {
newState.lastSetFalse = now
existingState.value = value
existingState.lastChange = now
}
updatedState = existingState
} else {
// Not exists
updatedState = StateValue{
value: value,
isDefined: true,
lastUpdate: now,
lastChange: now,
}
s.stateValueMap[key] = newState
}

if value {
updatedState.lastSetTrue = now
} else {
updatedState.lastSetFalse = now
}
s.stateValueMap[key] = updatedState

}

func (s *StateValueMap) getState(key string) StateValue {
Expand All @@ -170,7 +182,7 @@ func (s *StateValueMap) getState(key string) StateValue {
return stateValue
}

func (s *StateValueMap) require(key string) bool {
func (s *StateValueMap) requireTrue(key string) bool {
stateValue, exists := s.stateValueMap[key]
if !exists {
return false
Expand All @@ -179,7 +191,7 @@ func (s *StateValueMap) require(key string) bool {
}
}

func (s *StateValueMap) requireNot(key string) bool {
func (s *StateValueMap) requireFalse(key string) bool {
stateValue, exists := s.stateValueMap[key]
if !exists {
return false
Expand All @@ -188,7 +200,7 @@ func (s *StateValueMap) requireNot(key string) bool {
}
}

func (s *StateValueMap) requireRecently(key string, duration time.Duration) bool {
func (s *StateValueMap) requireTrueRecently(key string, duration time.Duration) bool {
stateValue, exists := s.stateValueMap[key]
if !exists {
return false
Expand All @@ -197,7 +209,7 @@ func (s *StateValueMap) requireRecently(key string, duration time.Duration) bool
}
}

func (s *StateValueMap) requireNotRecently(key string, duration time.Duration) bool {
func (s *StateValueMap) requireTrueNotRecently(key string, duration time.Duration) bool {
stateValue, exists := s.stateValueMap[key]
if !exists {
return false
Expand Down Expand Up @@ -226,11 +238,19 @@ func (s *StateValueMap) LogState() {
if !stateValue.lastSetFalse.IsZero() {
secondsSinceLastSetFalse = int64(now.Sub(stateValue.lastSetFalse).Seconds())
}

secondsSinceLastChange := int64(-1)
if !stateValue.lastChange.IsZero() {
secondsSinceLastChange = int64(now.Sub(stateValue.lastChange).Seconds())
}

params = append(params, []any{"key", key,
"value", stateValue.value,
"isDefined", stateValue.isDefined,
"lastUpdate", stateValue.lastUpdate,
"secondsSinceLastUpdate", secondsSinceLastUpdate,
"lastChange", stateValue.lastChange,
"secondsSinceLastChange", secondsSinceLastChange,
"lastSetTrue", stateValue.lastSetTrue,
"secondsSinceLastSetTrue", secondsSinceLastSetTrue,
"lastSetFalse", stateValue.lastSetFalse,
Expand Down

0 comments on commit 21acfd9

Please sign in to comment.