lucy factory - sensor support
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package handlerfactory
|
package handlerfactory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -12,14 +13,23 @@ type FactoryConfig struct {
|
|||||||
Target string `yaml:"target"`
|
Target string `yaml:"target"`
|
||||||
SceneNames []string `yaml:"scenes"`
|
SceneNames []string `yaml:"scenes"`
|
||||||
TriggerDeviceID string `yaml:"trigger_device_id"`
|
TriggerDeviceID string `yaml:"trigger_device_id"`
|
||||||
|
After []After `yaml:"after"`
|
||||||
|
From string `yaml:"from"`
|
||||||
|
To string `yaml:"to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FCMOde string
|
type FCMOde string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SceneDimmer FCMOde = "SceneDimmer"
|
SceneDimmer FCMOde = "SceneDimmer"
|
||||||
|
MotionSensor FCMOde = "MotionSensor"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type After struct {
|
||||||
|
Minutes int `yaml:"minutes"`
|
||||||
|
NewState models.NewDeviceState `yaml:"new_state"`
|
||||||
|
}
|
||||||
|
|
||||||
func ParseConfigs(reader io.Reader) ([]FactoryConfig, error) {
|
func ParseConfigs(reader io.Reader) ([]FactoryConfig, error) {
|
||||||
readAll, err := ioutil.ReadAll(reader)
|
readAll, err := ioutil.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,3 +50,13 @@ func ParseConfigs(reader io.Reader) ([]FactoryConfig, error) {
|
|||||||
|
|
||||||
return configs, nil
|
return configs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FactoryConfig) FromTime() models.TimeOfDay {
|
||||||
|
tod, _ := models.ParseTimeOfDay(f.From)
|
||||||
|
return tod
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FactoryConfig) ToTime() models.TimeOfDay {
|
||||||
|
tod, _ := models.ParseTimeOfDay(f.To)
|
||||||
|
return tod
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ func CreateHandlers(
|
|||||||
switch config.Mode {
|
switch config.Mode {
|
||||||
case SceneDimmer:
|
case SceneDimmer:
|
||||||
return sceneDimmer(config, scenes, existing)
|
return sceneDimmer(config, scenes, existing)
|
||||||
|
case MotionSensor:
|
||||||
|
return motionSensor(config, existing)
|
||||||
default:
|
default:
|
||||||
return []models.EventHandler{}, []models.EventHandler{}
|
return []models.EventHandler{}, []models.EventHandler{}
|
||||||
}
|
}
|
||||||
@@ -59,8 +61,8 @@ func sceneDimmer(
|
|||||||
TargetKind: kind,
|
TargetKind: kind,
|
||||||
TargetValue: refValue,
|
TargetValue: refValue,
|
||||||
Actions: models.EventAction{SetPower: &myTrue},
|
Actions: models.EventAction{SetPower: &myTrue},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
|
|
||||||
handlers = append(handlers, models.EventHandler{
|
handlers = append(handlers, models.EventHandler{
|
||||||
@@ -74,8 +76,8 @@ func sceneDimmer(
|
|||||||
TargetKind: kind,
|
TargetKind: kind,
|
||||||
TargetValue: refValue,
|
TargetValue: refValue,
|
||||||
Actions: models.EventAction{SetPower: &myFalse},
|
Actions: models.EventAction{SetPower: &myFalse},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
|
|
||||||
for i, scene := range resolvedScenes {
|
for i, scene := range resolvedScenes {
|
||||||
@@ -96,8 +98,8 @@ func sceneDimmer(
|
|||||||
Group: refValue,
|
Group: refValue,
|
||||||
DurationMS: 0,
|
DurationMS: 0,
|
||||||
}},
|
}},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +120,8 @@ func sceneDimmer(
|
|||||||
Group: refValue,
|
Group: refValue,
|
||||||
DurationMS: 0,
|
DurationMS: 0,
|
||||||
}},
|
}},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,8 +141,8 @@ func sceneDimmer(
|
|||||||
Group: refValue,
|
Group: refValue,
|
||||||
DurationMS: 0,
|
DurationMS: 0,
|
||||||
}},
|
}},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
|
|
||||||
handlers = append(handlers, models.EventHandler{
|
handlers = append(handlers, models.EventHandler{
|
||||||
@@ -158,11 +160,75 @@ func sceneDimmer(
|
|||||||
Group: refValue,
|
Group: refValue,
|
||||||
DurationMS: 0,
|
DurationMS: 0,
|
||||||
}},
|
}},
|
||||||
From: -1,
|
From: config.FromTime(),
|
||||||
To: -1,
|
To: config.ToTime(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return handlers, toRemove
|
return optimizeLists(handlers, toRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
func motionSensor(
|
||||||
|
config FactoryConfig,
|
||||||
|
existing []models.EventHandler,
|
||||||
|
) ([]models.EventHandler, []models.EventHandler) {
|
||||||
|
kind, refValue := models.ParseFetchString(config.Target)
|
||||||
|
|
||||||
|
toRemove := make([]models.EventHandler, 0, 8)
|
||||||
|
for _, old := range existing {
|
||||||
|
if old.From != config.FromTime() || old.To != config.ToTime() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if old.Conditions["deviceId"].EQ == config.TriggerDeviceID {
|
||||||
|
toRemove = append(toRemove, old)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers := make([]models.EventHandler, 0, len(config.After))
|
||||||
|
for _, after := range config.After {
|
||||||
|
if after.Minutes == 0 {
|
||||||
|
handlers = append(handlers, models.EventHandler{
|
||||||
|
EventName: "SensorPresenceStarted",
|
||||||
|
Conditions: map[string]models.EventCondition{
|
||||||
|
"deviceId": {EQ: config.TriggerDeviceID},
|
||||||
|
},
|
||||||
|
OneShot: false,
|
||||||
|
Priority: 100,
|
||||||
|
TargetKind: kind,
|
||||||
|
TargetValue: refValue,
|
||||||
|
Actions: models.EventAction{
|
||||||
|
SetPower: after.NewState.Power,
|
||||||
|
SetColor: after.NewState.Color,
|
||||||
|
SetIntensity: after.NewState.Intensity,
|
||||||
|
SetTemperature: after.NewState.Temperature,
|
||||||
|
},
|
||||||
|
From: config.FromTime(),
|
||||||
|
To: config.ToTime(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
handlers = append(handlers, models.EventHandler{
|
||||||
|
EventName: "SensorPresenceEnded",
|
||||||
|
Conditions: map[string]models.EventCondition{
|
||||||
|
"deviceId": {EQ: config.TriggerDeviceID},
|
||||||
|
"minutesElapsed": {EQ: fmt.Sprintf("%d", after.Minutes)},
|
||||||
|
},
|
||||||
|
OneShot: false,
|
||||||
|
Priority: 100,
|
||||||
|
TargetKind: kind,
|
||||||
|
TargetValue: refValue,
|
||||||
|
Actions: models.EventAction{
|
||||||
|
SetPower: after.NewState.Power,
|
||||||
|
SetColor: after.NewState.Color,
|
||||||
|
SetIntensity: after.NewState.Intensity,
|
||||||
|
SetTemperature: after.NewState.Temperature,
|
||||||
|
},
|
||||||
|
From: config.FromTime(),
|
||||||
|
To: config.ToTime(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimizeLists(handlers, toRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
var myTrue = true
|
var myTrue = true
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package handlerfactory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/lucifer/new-server/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func optimizeLists(
|
||||||
|
add []models.EventHandler,
|
||||||
|
del []models.EventHandler,
|
||||||
|
) ([]models.EventHandler, []models.EventHandler) {
|
||||||
|
newAdd := make([]models.EventHandler, 0, len(add))
|
||||||
|
newDel := make([]models.EventHandler, 0, len(del))
|
||||||
|
|
||||||
|
OptimizeLoop1:
|
||||||
|
for _, addItem := range add {
|
||||||
|
for _, delItem := range del {
|
||||||
|
if checkDuplicate(&addItem, &delItem) {
|
||||||
|
continue OptimizeLoop1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newAdd = append(newAdd, addItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
OptimizeLoop2:
|
||||||
|
for _, delItem := range del {
|
||||||
|
for _, addItem := range add {
|
||||||
|
if checkDuplicate(&addItem, &delItem) {
|
||||||
|
continue OptimizeLoop2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newDel = append(newDel, delItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAdd, newDel
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDuplicate(a *models.EventHandler, b *models.EventHandler) bool {
|
||||||
|
check := true
|
||||||
|
check = check && a.EventName == b.EventName
|
||||||
|
check = check && len(a.Conditions) == len(b.Conditions)
|
||||||
|
for k := range a.Conditions {
|
||||||
|
check = check && a.Conditions[k] == b.Conditions[k]
|
||||||
|
}
|
||||||
|
check = check && a.OneShot == b.OneShot
|
||||||
|
check = check && a.Priority == b.Priority
|
||||||
|
check = check && a.TargetKind == b.TargetKind
|
||||||
|
check = check && a.TargetValue == b.TargetValue
|
||||||
|
check = check && a.From == b.From
|
||||||
|
check = check && a.To == b.To
|
||||||
|
|
||||||
|
aPower := a.Actions.SetPower
|
||||||
|
bPower := b.Actions.SetPower
|
||||||
|
powerEq := (aPower == nil && bPower == nil) || (aPower != nil && bPower != nil && *aPower == *bPower)
|
||||||
|
|
||||||
|
aColor := a.Actions.SetColor
|
||||||
|
bColor := b.Actions.SetColor
|
||||||
|
colorEq := (aColor == nil && bColor == nil) || (aColor != nil && bColor != nil && *aColor == *bColor)
|
||||||
|
|
||||||
|
aInt := a.Actions.SetIntensity
|
||||||
|
bInt := b.Actions.SetIntensity
|
||||||
|
intEq := (aInt == nil && bInt == nil) || (aInt != nil && bInt != nil && *aInt == *bInt)
|
||||||
|
|
||||||
|
aTemp := a.Actions.SetTemperature
|
||||||
|
bTemp := b.Actions.SetTemperature
|
||||||
|
tempEq := (aTemp == nil && bTemp == nil) || (aTemp != nil && bTemp != nil && *aTemp == *bTemp)
|
||||||
|
|
||||||
|
check = check && powerEq && colorEq && intEq && tempEq
|
||||||
|
|
||||||
|
return check
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@ func factoryCmd(
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
configs, err := handlerfactory.ParseConfigs(file)
|
configs, err := handlerfactory.ParseConfigs(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to parse file:", err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
toAdd, toRemove := handlerfactory.CreateHandlers(config, scenes, handlers)
|
toAdd, toRemove := handlerfactory.CreateHandlers(config, scenes, handlers)
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,21 @@ scenes:
|
|||||||
- vasking
|
- vasking
|
||||||
trigger_device_id: 14
|
trigger_device_id: 14
|
||||||
---
|
---
|
||||||
|
mode: MotionSensor
|
||||||
|
target: tag:Bod
|
||||||
|
trigger_device_id: 36
|
||||||
|
after:
|
||||||
|
- minutes: 0
|
||||||
|
new_state:
|
||||||
|
power: true
|
||||||
|
color: hs:240,0.6
|
||||||
|
intensity: 0.8
|
||||||
|
- minutes: 1
|
||||||
|
new_state:
|
||||||
|
power: false
|
||||||
|
from: 08:00
|
||||||
|
to: 15:59:59
|
||||||
|
---
|
||||||
(more modes coming soon)
|
(more modes coming soon)
|
||||||
|
|
||||||
WARNING: Similar event handlers will be removed!
|
WARNING: Similar event handlers will be removed!
|
||||||
|
|||||||
Reference in New Issue
Block a user