add sensor scenes for Motion and Temperature.
continuous-integration/drone/push Build is passing

This commit is contained in:
2021-10-30 14:38:14 +02:00
parent b191455753
commit 147231e4fd
7 changed files with 165 additions and 23 deletions
+11 -9
View File
@@ -2,7 +2,6 @@ package models
import (
"context"
"math"
"strings"
"time"
)
@@ -156,6 +155,17 @@ func (d *Device) HasTag(tags ...string) bool {
return false
}
func (d *Device) IsOnlySensor() bool {
return !d.HasCapability(
DCPower,
DCColorHS,
DCColorHSK,
DCColorKelvin,
DCIntensity,
DCTemperatureControl,
)
}
func (d *Device) HasCapability(capabilities ...DeviceCapability) bool {
for _, c := range d.Capabilities {
for _, c2 := range capabilities {
@@ -198,14 +208,6 @@ func (s *NewDeviceState) RelativeTo(device Device) NewDeviceState {
intensity := device.State.Intensity * *s.Intensity
n.Intensity = &intensity
}
if s.Color != nil {
c, err := ParseColorValue(*s.Color)
if err == nil {
c.Hue = math.Mod(device.State.Color.Hue+c.Hue, 360)
c.Saturation *= device.State.Color.Saturation
c.Kelvin += device.State.Color.Kelvin
}
}
return n
}
-1
View File
@@ -24,7 +24,6 @@ var (
ENBridgeDisconnected = "BridgeDisconnected"
ENButtonPressed = "ButtonPressed"
ENSensorPresenceStarted = "SensorPresenceStarted"
ENSensorPresenceEnding = "SensorPresenceEnding"
ENSensorPresenceEnded = "SensorPresenceEnded"
ENSensorTemperature = "SensorTemperature"
)
+71 -10
View File
@@ -61,24 +61,38 @@ const (
SEGradient SceneEffect = "Gradient"
SEWalkingGradient SceneEffect = "WalkingGradient"
SETransition SceneEffect = "Transition"
SEMotion SceneEffect = "Motion"
SETemperature SceneEffect = "Temperature"
)
type SceneRole struct {
Effect SceneEffect `json:"effect"`
PowerMode ScenePowerMode `json:"powerMode"`
TargetKind ReferenceKind `json:"targetKind"`
TargetValue string `json:"targetValue"`
Interpolate bool `json:"interpolate"`
Relative bool `json:"relative"`
Order string `json:"order"`
States []NewDeviceState `json:"states"`
Effect SceneEffect `json:"effect"`
MotionSeconds float64 `json:"motionSeconds"`
MinTemperature int `json:"minTemperature"`
MaxTemperature int `json:"maxTemperature"`
PowerMode ScenePowerMode `json:"powerMode"`
TargetKind ReferenceKind `json:"targetKind"`
TargetValue string `json:"targetValue"`
Interpolate bool `json:"interpolate"`
Relative bool `json:"relative"`
Order string `json:"order"`
States []NewDeviceState `json:"states"`
}
type SceneRunContext struct {
CurrentTime time.Time
Index int
Length int
IntervalNumber int64
IntervalMax int64
Sensors []SceneSensor
}
type SceneSensor struct {
ID int
UpdateTime time.Time
Temperature *float64
Presence *bool
}
func (d *SceneRunContext) PositionFacShifted() float64 {
@@ -120,7 +134,7 @@ func (r *SceneRole) Validate() error {
}
switch r.Effect {
case SEStatic, SERandom, SEGradient, SEWalkingGradient, SETransition:
case SEStatic, SERandom, SEGradient, SEWalkingGradient, SETransition, SEMotion, SETemperature:
default:
return ErrSceneRoleUnknownEffect
}
@@ -178,6 +192,53 @@ func (r *SceneRole) ApplyEffect(device *Device, c SceneRunContext) (newState New
newState = r.State(c.PositionFacShifted())
case SETransition:
newState = r.State(c.IntervalFac())
case SEMotion:
presence := false
absenceTime := time.Time{}
for _, sensors := range c.Sensors {
if sensors.Presence == nil {
continue
}
if *sensors.Presence {
presence = true
} else {
if sensors.UpdateTime.After(absenceTime) {
absenceTime = sensors.UpdateTime
}
}
}
if presence {
newState = r.State(0.0)
} else {
fac := c.CurrentTime.Sub(absenceTime).Seconds() / r.MotionSeconds
if fac < 0 {
fac = 0
} else if fac > 1 {
fac = 1
}
newState = r.State(fac)
}
case SETemperature:
avg := 0.0
count := 0
for _, sensor := range c.Sensors {
if sensor.Temperature == nil {
continue
}
if count == 0 {
avg = *sensor.Temperature
count = 1
} else {
avg = ((avg * float64(count)) + *sensor.Temperature) / float64(count+1)
count += 1
}
}
}
if r.Relative {
@@ -188,7 +249,7 @@ func (r *SceneRole) ApplyEffect(device *Device, c SceneRunContext) (newState New
case SPDevice:
newState.Power = nil
case SPScene:
// Do nothing
// Do nothing
case SPBoth:
if newState.Power != nil {
powerIntersection := *newState.Power && device.State.Power