Files
hapz2m/s_motion_sensor.go
Darell Tan 9eeab32c8a Added delayed-off for motion sensors
This ensures that the "motion detected" event doesn't flip flop, even if
the motion sensor itself was set to a low timeout like 10-30s. It stays
in the Detected state until no further detections occur within the
timeout period (hardcoded to 1 minute).
2024-11-24 16:03:24 +08:00

72 lines
1.5 KiB
Go

package hapz2m
import (
"sync"
"time"
"github.com/brutella/hap/accessory"
"github.com/brutella/hap/service"
)
// there isn't a distinction between motion and occupancy sensors in z2m,
// but most sensors are PIR, so they are technically motion sensors
func setupDelayedOff(m *ExposeMapping, delay time.Duration) {
// lock makes sure Timer and setFunc aren't stepping over each other
lock := &sync.Mutex{}
tm := time.AfterFunc(1*time.Minute, func() {
lock.Lock()
defer lock.Unlock()
m.Characteristic.SetValueRequest( /*motion*/ false, nil)
})
tm.Stop()
m.SetCharacteristicValueFunc = func(m *ExposeMapping, newVal any, changed bool) (bool, error) {
lock.Lock()
defer lock.Unlock()
if newVal == true { // motion detected
tm.Stop()
} else { // hold off transitions to no motion
if changed {
tm.Reset(delay)
}
return false, nil
}
return true, nil
}
}
func createMotionServices(dev *Device) (byte, []*service.S, []*ExposeMapping, error) {
var svcs []*service.S
var exposes []*ExposeMapping
for _, exp := range dev.Definition.Exposes {
if exp.Ignored() {
continue
}
switch {
case exp.Type == "binary" && exp.Name == "occupancy":
exp := exp // create a copy
s := service.NewMotionSensor()
m := NewExposeMapping(&exp, s.MotionDetected.C)
setupDelayedOff(m, 1*time.Minute)
svcs = append(svcs, s.S)
exposes = append(exposes, m)
}
}
return accessory.TypeSensor, svcs, exposes, nil
}
func init() {
RegisterCreateServiceHandler(createMotionServices)
}