diff --git a/internal/drivers/hue/bridge.go b/internal/drivers/hue/bridge.go index cff00ec..b8b64a2 100644 --- a/internal/drivers/hue/bridge.go +++ b/internal/drivers/hue/bridge.go @@ -76,9 +76,10 @@ func (b *Bridge) Refresh(ctx context.Context) error { if state == nil { state = &hueSensorState{ - index: index, - uniqueID: sensor.UniqueID, - externalID: -1, + index: index, + uniqueID: sensor.UniqueID, + externalID: -1, + presenceCooldown: -2, } b.sensorStates = append(b.sensorStates, state) diff --git a/internal/drivers/hue/state.go b/internal/drivers/hue/state.go index 2883b81..288eaa0 100644 --- a/internal/drivers/hue/state.go +++ b/internal/drivers/hue/state.go @@ -92,7 +92,7 @@ type hueSensorState struct { uniqueID string prevData *SensorData prevTime time.Time - presenceCooldown bool + presenceCooldown int } func (state *hueSensorState) Update(newData SensorData) *models.Event { @@ -153,26 +153,36 @@ func (state *hueSensorState) Update(newData SensorData) *models.Event { case "ZLLPresence": { if state.prevData != nil && state.prevData.State.Presence != newData.State.Presence { - name := models.ENSensorPresenceStarted - if !newData.State.Presence { - name = models.ENSensorPresenceEnding - state.presenceCooldown = true + if newData.State.Presence { + state.presenceCooldown = -1 + + return &models.Event{ + Name: models.ENSensorPresenceStarted, + Payload: map[string]string{ + "deviceId": strconv.Itoa(state.externalID), + "deviceInternalId": newData.UniqueID, + }, + } + } else { + state.presenceCooldown = 0 } + } + + if state.presenceCooldown == -2 { + state.presenceCooldown = int(time.Since(stateTime) / time.Minute) + } + + nextEventWait := time.Minute * time.Duration(state.presenceCooldown) + if state.presenceCooldown != -1 && !newData.State.Presence && time.Since(stateTime) > nextEventWait { + state.presenceCooldown += 1 - return &models.Event{ - Name: name, - Payload: map[string]string{ - "deviceId": strconv.Itoa(state.externalID), - "deviceInternalId": newData.UniqueID, - }, - } - } else if state.presenceCooldown && !newData.State.Presence && time.Since(stateTime) > time.Minute { - state.presenceCooldown = false return &models.Event{ Name: models.ENSensorPresenceEnded, Payload: map[string]string{ "deviceId": strconv.Itoa(state.externalID), "deviceInternalId": newData.UniqueID, + "minutesElapsed": strconv.Itoa(state.presenceCooldown - 1), + "secondsElapsed": strconv.Itoa((state.presenceCooldown - 1) * 60), }, } } diff --git a/models/eventhandler.go b/models/eventhandler.go index 81866e9..31a2b7e 100644 --- a/models/eventhandler.go +++ b/models/eventhandler.go @@ -173,11 +173,11 @@ func (c *EventCondition) checkDevice(key string, device Device) (matches bool, s } } -var numRegex = regexp.MustCompile("^{-[0-9].}+$") +var numRegex = regexp.MustCompile("^-*[0-9]+(.[0-9]+)*$") func (c *EventCondition) matches(value string) bool { if numRegex.MatchString(value) { - numValue, _ := strconv.ParseFloat(c.LT, 64) + numValue, _ := strconv.ParseFloat(value, 64) stillAlive := true if c.LT != "" { @@ -197,7 +197,7 @@ func (c *EventCondition) matches(value string) bool { if stillAlive && c.GTE != "" { gte, _ := strconv.ParseFloat(c.GTE, 64) - stillAlive = numValue == gte + stillAlive = numValue >= gte } if stillAlive && c.GT != "" { diff --git a/models/timeofday.go b/models/timeofday.go index 780d067..58b26fd 100644 --- a/models/timeofday.go +++ b/models/timeofday.go @@ -75,7 +75,7 @@ func (t TimeOfDay) IsBetween(from TimeOfDay, to TimeOfDay) bool { if from == to { return t == from } else if from > to { - return t >= to || t <= from + return t >= from || t <= to } else { return t >= from && t <= to } diff --git a/models/timeofday_test.go b/models/timeofday_test.go new file mode 100644 index 0000000..db0b644 --- /dev/null +++ b/models/timeofday_test.go @@ -0,0 +1,40 @@ +package models + +import ( + "fmt" + "testing" +) + +func tod(s string) TimeOfDay { + tod, err := ParseTimeOfDay(s) + if err != nil { + panic(err) + } + + return tod +} + +func TestTimeOfDay_IsBetween(t *testing.T) { + table := []struct{ + Value TimeOfDay + From TimeOfDay + To TimeOfDay + Expected bool + }{ + { tod("16:13:11"), tod("07"), tod("21:30"), true }, + { tod("16:13:31"), tod("21:30"), tod("07"), false }, + { tod("23:15:32"), tod("21:30"), tod("07"), true }, + { tod("04:13:57"), tod("21:30"), tod("07"), true }, + { tod("16:14:43"), tod("15:30"), tod("16:00"), false }, + { tod("16:14:43"), tod("15:30"), tod("16:15"), true }, + } + + for i, row := range table { + t.Run(fmt.Sprintf("row_%d", i), func (t *testing.T) { + if row.Value.IsBetween(row.From, row.To) != row.Expected { + t.Log(row.Value, row.From, row.To) + t.Fail() + } + }) + } +}