mirror of
https://github.com/Xevion/go-ha.git
synced 2025-12-06 01:15:10 -06:00
181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package gomeassistant
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang-module/carbon"
|
|
|
|
"saml.dev/gome-assistant/internal"
|
|
ws "saml.dev/gome-assistant/internal/websocket"
|
|
)
|
|
|
|
type EventListener struct {
|
|
eventTypes []string
|
|
callback EventListenerCallback
|
|
betweenStart string
|
|
betweenEnd string
|
|
throttle time.Duration
|
|
lastRan carbon.Carbon
|
|
|
|
exceptionDates []time.Time
|
|
exceptionRanges []timeRange
|
|
|
|
enabledEntities []internal.EnabledDisabledInfo
|
|
disabledEntities []internal.EnabledDisabledInfo
|
|
}
|
|
|
|
type EventListenerCallback func(*Service, State, EventData)
|
|
|
|
type EventData struct {
|
|
Type string
|
|
RawEventJSON []byte
|
|
}
|
|
|
|
/* Methods */
|
|
|
|
func NewEventListener() eventListenerBuilder1 {
|
|
return eventListenerBuilder1{EventListener{
|
|
lastRan: carbon.Now().StartOfCentury(),
|
|
}}
|
|
}
|
|
|
|
type eventListenerBuilder1 struct {
|
|
eventListener EventListener
|
|
}
|
|
|
|
func (b eventListenerBuilder1) EventTypes(ets ...string) eventListenerBuilder2 {
|
|
b.eventListener.eventTypes = ets
|
|
return eventListenerBuilder2(b)
|
|
}
|
|
|
|
type eventListenerBuilder2 struct {
|
|
eventListener EventListener
|
|
}
|
|
|
|
func (b eventListenerBuilder2) Call(callback EventListenerCallback) eventListenerBuilder3 {
|
|
b.eventListener.callback = callback
|
|
return eventListenerBuilder3(b)
|
|
}
|
|
|
|
type eventListenerBuilder3 struct {
|
|
eventListener EventListener
|
|
}
|
|
|
|
func (b eventListenerBuilder3) OnlyBetween(start string, end string) eventListenerBuilder3 {
|
|
b.eventListener.betweenStart = start
|
|
b.eventListener.betweenEnd = end
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) OnlyAfter(start string) eventListenerBuilder3 {
|
|
b.eventListener.betweenStart = start
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) OnlyBefore(end string) eventListenerBuilder3 {
|
|
b.eventListener.betweenEnd = end
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) Throttle(s DurationString) eventListenerBuilder3 {
|
|
d := internal.ParseDuration(string(s))
|
|
b.eventListener.throttle = d
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) ExceptionDates(t time.Time, tl ...time.Time) eventListenerBuilder3 {
|
|
b.eventListener.exceptionDates = append(tl, t)
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) ExceptionRange(start, end time.Time) eventListenerBuilder3 {
|
|
b.eventListener.exceptionRanges = append(b.eventListener.exceptionRanges, timeRange{start, end})
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Enable this listener only when the current state of {entityId} matches {state}.
|
|
If there is a network error while retrieving state, the listener runs if {runOnNetworkError} is true.
|
|
*/
|
|
func (b eventListenerBuilder3) EnabledWhen(entityId, state string, runOnNetworkError bool) eventListenerBuilder3 {
|
|
if entityId == "" {
|
|
panic(fmt.Sprintf("entityId is empty in eventListener EnabledWhen entityId='%s' state='%s' runOnNetworkError='%t'", entityId, state, runOnNetworkError))
|
|
}
|
|
i := internal.EnabledDisabledInfo{
|
|
Entity: entityId,
|
|
State: state,
|
|
RunOnError: runOnNetworkError,
|
|
}
|
|
b.eventListener.enabledEntities = append(b.eventListener.enabledEntities, i)
|
|
return b
|
|
}
|
|
|
|
/*
|
|
Disable this listener when the current state of {entityId} matches {state}.
|
|
If there is a network error while retrieving state, the listener runs if {runOnNetworkError} is true.
|
|
*/
|
|
func (b eventListenerBuilder3) DisabledWhen(entityId, state string, runOnNetworkError bool) eventListenerBuilder3 {
|
|
if entityId == "" {
|
|
panic(fmt.Sprintf("entityId is empty in eventListener EnabledWhen entityId='%s' state='%s' runOnNetworkError='%t'", entityId, state, runOnNetworkError))
|
|
}
|
|
i := internal.EnabledDisabledInfo{
|
|
Entity: entityId,
|
|
State: state,
|
|
RunOnError: runOnNetworkError,
|
|
}
|
|
b.eventListener.disabledEntities = append(b.eventListener.disabledEntities, i)
|
|
return b
|
|
}
|
|
|
|
func (b eventListenerBuilder3) Build() EventListener {
|
|
return b.eventListener
|
|
}
|
|
|
|
type BaseEventMsg struct {
|
|
Event struct {
|
|
EventType string `json:"event_type"`
|
|
} `json:"event"`
|
|
}
|
|
|
|
/* Functions */
|
|
func callEventListeners(app *App, msg ws.ChanMsg) {
|
|
baseEventMsg := BaseEventMsg{}
|
|
_ = json.Unmarshal(msg.Raw, &baseEventMsg)
|
|
listeners, ok := app.eventListeners[baseEventMsg.Event.EventType]
|
|
if !ok {
|
|
// no listeners registered for this event type
|
|
return
|
|
}
|
|
|
|
for _, l := range listeners {
|
|
// Check conditions
|
|
if c := checkWithinTimeRange(l.betweenStart, l.betweenEnd); c.fail {
|
|
continue
|
|
}
|
|
if c := checkThrottle(l.throttle, l.lastRan); c.fail {
|
|
continue
|
|
}
|
|
if c := checkExceptionDates(l.exceptionDates); c.fail {
|
|
continue
|
|
}
|
|
if c := checkExceptionRanges(l.exceptionRanges); c.fail {
|
|
continue
|
|
}
|
|
if c := checkEnabledEntity(app.state, l.enabledEntities); c.fail {
|
|
continue
|
|
}
|
|
if c := checkDisabledEntity(app.state, l.disabledEntities); c.fail {
|
|
continue
|
|
}
|
|
|
|
eventData := EventData{
|
|
Type: baseEventMsg.Event.EventType,
|
|
RawEventJSON: msg.Raw,
|
|
}
|
|
go l.callback(app.service, app.state, eventData)
|
|
l.lastRan = carbon.Now()
|
|
}
|
|
}
|