mirror of
https://github.com/Xevion/go-ha.git
synced 2025-12-06 07:15:09 -06:00
add enable/disable function to every builder
This commit is contained in:
@@ -86,6 +86,38 @@ func checkExceptionRanges(eList []timeRange) conditionCheck {
|
|||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkEnabledEntity(s *State, eid, expectedState string, runOnNetworkError bool) conditionCheck {
|
||||||
|
cc := conditionCheck{fail: false}
|
||||||
|
if eid == "" || expectedState == "" {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := s.Equals(eid, expectedState)
|
||||||
|
if err != nil {
|
||||||
|
cc.fail = !runOnNetworkError
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.fail = !matches
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDisabledEntity(s *State, eid, expectedState string, runOnNetworkError bool) conditionCheck {
|
||||||
|
cc := conditionCheck{fail: false}
|
||||||
|
if eid == "" || expectedState == "" {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := s.Equals(eid, expectedState)
|
||||||
|
if err != nil {
|
||||||
|
cc.fail = !runOnNetworkError
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.fail = matches
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
func checkAllowlistDates(eList []time.Time) conditionCheck {
|
func checkAllowlistDates(eList []time.Time) conditionCheck {
|
||||||
if len(eList) == 0 {
|
if len(eList) == 0 {
|
||||||
return conditionCheck{fail: false}
|
return conditionCheck{fail: false}
|
||||||
@@ -2,6 +2,7 @@ package gomeassistant
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-module/carbon"
|
"github.com/golang-module/carbon"
|
||||||
@@ -27,6 +28,13 @@ type EntityListener struct {
|
|||||||
|
|
||||||
runOnStartup bool
|
runOnStartup bool
|
||||||
runOnStartupCompleted bool
|
runOnStartupCompleted bool
|
||||||
|
|
||||||
|
enabledEntity string
|
||||||
|
enabledEntityState string
|
||||||
|
enabledEntityRunOnError bool
|
||||||
|
disabledEntity string
|
||||||
|
disabledEntityState string
|
||||||
|
disabledEntityRunOnError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntityListenerCallback func(*Service, *State, EntityData)
|
type EntityListenerCallback func(*Service, *State, EntityData)
|
||||||
@@ -148,6 +156,40 @@ func (b elBuilder3) RunOnStartup() elBuilder3 {
|
|||||||
return b
|
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 elBuilder3) EnabledEntity(entityId, state string, runOnNetworkError bool) elBuilder3 {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if b.entityListener.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting EnabledEntity on an entity listener with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
b.entityListener.enabledEntity = entityId
|
||||||
|
b.entityListener.enabledEntityState = state
|
||||||
|
b.entityListener.enabledEntityRunOnError = runOnNetworkError
|
||||||
|
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 elBuilder3) DisabledEntity(entityId, state string, runOnNetworkError bool) elBuilder3 {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if b.entityListener.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting DisabledEntity on an entity listener with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
b.entityListener.disabledEntity = entityId
|
||||||
|
b.entityListener.disabledEntityState = state
|
||||||
|
b.entityListener.disabledEntityRunOnError = runOnNetworkError
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (b elBuilder3) Build() EntityListener {
|
func (b elBuilder3) Build() EntityListener {
|
||||||
return b.entityListener
|
return b.entityListener
|
||||||
}
|
}
|
||||||
@@ -195,6 +237,12 @@ func callEntityListeners(app *App, msgBytes []byte) {
|
|||||||
if c := checkExceptionRanges(l.exceptionRanges); c.fail {
|
if c := checkExceptionRanges(l.exceptionRanges); c.fail {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if c := checkEnabledEntity(app.state, l.enabledEntity, l.enabledEntityState, l.enabledEntityRunOnError); c.fail {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c := checkDisabledEntity(app.state, l.disabledEntity, l.disabledEntityState, l.disabledEntityRunOnError); c.fail {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
entityData := EntityData{
|
entityData := EntityData{
|
||||||
TriggerEntityId: eid,
|
TriggerEntityId: eid,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package gomeassistant
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-module/carbon"
|
"github.com/golang-module/carbon"
|
||||||
@@ -19,6 +20,13 @@ type EventListener struct {
|
|||||||
|
|
||||||
exceptionDates []time.Time
|
exceptionDates []time.Time
|
||||||
exceptionRanges []timeRange
|
exceptionRanges []timeRange
|
||||||
|
|
||||||
|
enabledEntity string
|
||||||
|
enabledEntityState string
|
||||||
|
enabledEntityRunOnError bool
|
||||||
|
disabledEntity string
|
||||||
|
disabledEntityState string
|
||||||
|
disabledEntityRunOnError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventListenerCallback func(*Service, *State, EventData)
|
type EventListenerCallback func(*Service, *State, EventData)
|
||||||
@@ -90,6 +98,40 @@ func (b eventListenerBuilder3) ExceptionRange(start, end time.Time) eventListene
|
|||||||
return b
|
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) EnabledEntity(entityId, state string, runOnNetworkError bool) eventListenerBuilder3 {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in eventListener.EnabledEntity entityId='%s' state='%s' runOnNetworkError='%t'", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
if b.eventListener.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting EnabledEntity entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
b.eventListener.enabledEntity = entityId
|
||||||
|
b.eventListener.enabledEntityState = state
|
||||||
|
b.eventListener.enabledEntityRunOnError = runOnNetworkError
|
||||||
|
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) DisabledEntity(entityId, state string, runOnNetworkError bool) eventListenerBuilder3 {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in eventListener.EnabledEntity entityId='%s' state='%s' runOnNetworkError='%t'", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
if b.eventListener.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting DisabledEntity entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
b.eventListener.disabledEntity = entityId
|
||||||
|
b.eventListener.disabledEntityState = state
|
||||||
|
b.eventListener.disabledEntityRunOnError = runOnNetworkError
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (b eventListenerBuilder3) Build() EventListener {
|
func (b eventListenerBuilder3) Build() EventListener {
|
||||||
return b.eventListener
|
return b.eventListener
|
||||||
}
|
}
|
||||||
@@ -124,6 +166,12 @@ func callEventListeners(app *App, msg ws.ChanMsg) {
|
|||||||
if c := checkExceptionRanges(l.exceptionRanges); c.fail {
|
if c := checkExceptionRanges(l.exceptionRanges); c.fail {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if c := checkEnabledEntity(app.state, l.enabledEntity, l.enabledEntityState, l.enabledEntityRunOnError); c.fail {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c := checkDisabledEntity(app.state, l.disabledEntity, l.disabledEntityState, l.disabledEntityRunOnError); c.fail {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
eventData := EventData{
|
eventData := EventData{
|
||||||
Type: baseEventMsg.Event.EventType,
|
Type: baseEventMsg.Event.EventType,
|
||||||
|
|||||||
83
interval.go
83
interval.go
@@ -18,6 +18,13 @@ type Interval struct {
|
|||||||
|
|
||||||
exceptionDates []time.Time
|
exceptionDates []time.Time
|
||||||
exceptionRanges []timeRange
|
exceptionRanges []timeRange
|
||||||
|
|
||||||
|
enabledEntity string
|
||||||
|
enabledEntityState string
|
||||||
|
enabledEntityRunOnError bool
|
||||||
|
disabledEntity string
|
||||||
|
disabledEntityState string
|
||||||
|
disabledEntityRunOnError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Interval) Hash() string {
|
func (i Interval) Hash() string {
|
||||||
@@ -69,38 +76,72 @@ func formatStartOrEndString(s TimeString, isStart bool) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb intervalBuilder) Call(callback IntervalCallback) intervalBuilderCall {
|
func (ib intervalBuilder) Call(callback IntervalCallback) intervalBuilderCall {
|
||||||
sb.interval.callback = callback
|
ib.interval.callback = callback
|
||||||
return intervalBuilderCall(sb)
|
return intervalBuilderCall(ib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a DurationString ("2h", "5m", etc) to set the frequency of the interval.
|
// Takes a DurationString ("2h", "5m", etc) to set the frequency of the interval.
|
||||||
func (sb intervalBuilderCall) Every(s DurationString) intervalBuilderEnd {
|
func (ib intervalBuilderCall) Every(s DurationString) intervalBuilderEnd {
|
||||||
d := internal.ParseDuration(string(s))
|
d := internal.ParseDuration(string(s))
|
||||||
sb.interval.frequency = d
|
ib.interval.frequency = d
|
||||||
return intervalBuilderEnd(sb)
|
return intervalBuilderEnd(ib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a TimeString ("HH:MM") when this interval will start running for the day.
|
// Takes a TimeString ("HH:MM") when this interval will start running for the day.
|
||||||
func (sb intervalBuilderEnd) StartingAt(s TimeString) intervalBuilderEnd {
|
func (ib intervalBuilderEnd) StartingAt(s TimeString) intervalBuilderEnd {
|
||||||
sb.interval.startTime = s
|
ib.interval.startTime = s
|
||||||
return sb
|
return ib
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a TimeString ("HH:MM") when this interval will stop running for the day.
|
// Takes a TimeString ("HH:MM") when this interval will stop running for the day.
|
||||||
func (sb intervalBuilderEnd) EndingAt(s TimeString) intervalBuilderEnd {
|
func (ib intervalBuilderEnd) EndingAt(s TimeString) intervalBuilderEnd {
|
||||||
sb.interval.endTime = s
|
ib.interval.endTime = s
|
||||||
return sb
|
return ib
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb intervalBuilderEnd) ExceptionDates(t time.Time, tl ...time.Time) intervalBuilderEnd {
|
func (ib intervalBuilderEnd) ExceptionDates(t time.Time, tl ...time.Time) intervalBuilderEnd {
|
||||||
sb.interval.exceptionDates = append(tl, t)
|
ib.interval.exceptionDates = append(tl, t)
|
||||||
return sb
|
return ib
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb intervalBuilderEnd) ExceptionRange(start, end time.Time) intervalBuilderEnd {
|
func (ib intervalBuilderEnd) ExceptionRange(start, end time.Time) intervalBuilderEnd {
|
||||||
sb.interval.exceptionRanges = append(sb.interval.exceptionRanges, timeRange{start, end})
|
ib.interval.exceptionRanges = append(ib.interval.exceptionRanges, timeRange{start, end})
|
||||||
return sb
|
return ib
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enable this interval only when the current state of {entityId} matches {state}.
|
||||||
|
If there is a network error while retrieving state, the interval runs if {runOnNetworkError} is true.
|
||||||
|
*/
|
||||||
|
func (ib intervalBuilderEnd) EnabledEntity(entityId, state string, runOnNetworkError bool) intervalBuilderEnd {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if ib.interval.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting EnabledEntity on an entity listener with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
ib.interval.enabledEntity = entityId
|
||||||
|
ib.interval.enabledEntityState = state
|
||||||
|
ib.interval.enabledEntityRunOnError = runOnNetworkError
|
||||||
|
return ib
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Disable this interval when the current state of {entityId} matches {state}.
|
||||||
|
If there is a network error while retrieving state, the interval runs if {runOnNetworkError} is true.
|
||||||
|
*/
|
||||||
|
func (ib intervalBuilderEnd) DisabledEntity(entityId, state string, runOnNetworkError bool) intervalBuilderEnd {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if ib.interval.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting DisabledEntity on an entity listener with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
ib.interval.disabledEntity = entityId
|
||||||
|
ib.interval.disabledEntityState = state
|
||||||
|
ib.interval.disabledEntityRunOnError = runOnNetworkError
|
||||||
|
return ib
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb intervalBuilderEnd) Build() Interval {
|
func (sb intervalBuilderEnd) Build() Interval {
|
||||||
@@ -143,6 +184,12 @@ func (i Interval) maybeRunCallback(a *App) {
|
|||||||
if c := checkExceptionRanges(i.exceptionRanges); c.fail {
|
if c := checkExceptionRanges(i.exceptionRanges); c.fail {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c := checkEnabledEntity(a.state, i.enabledEntity, i.enabledEntityState, i.enabledEntityRunOnError); c.fail {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c := checkDisabledEntity(a.state, i.disabledEntity, i.disabledEntityState, i.disabledEntityRunOnError); c.fail {
|
||||||
|
return
|
||||||
|
}
|
||||||
go i.callback(a.service, a.state)
|
go i.callback(a.service, a.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
schedule.go
47
schedule.go
@@ -26,6 +26,13 @@ type DailySchedule struct {
|
|||||||
|
|
||||||
exceptionDates []time.Time
|
exceptionDates []time.Time
|
||||||
allowlistDates []time.Time
|
allowlistDates []time.Time
|
||||||
|
|
||||||
|
enabledEntity string
|
||||||
|
enabledEntityState string
|
||||||
|
enabledEntityRunOnError bool
|
||||||
|
disabledEntity string
|
||||||
|
disabledEntityState string
|
||||||
|
disabledEntityRunOnError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DailySchedule) Hash() string {
|
func (s DailySchedule) Hash() string {
|
||||||
@@ -110,6 +117,40 @@ func (sb scheduleBuilderEnd) OnlyOnDates(t time.Time, tl ...time.Time) scheduleB
|
|||||||
return sb
|
return sb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enable this schedule only when the current state of {entityId} matches {state}.
|
||||||
|
If there is a network error while retrieving state, the schedule runs if {runOnNetworkError} is true.
|
||||||
|
*/
|
||||||
|
func (sb scheduleBuilderEnd) EnabledEntity(entityId, state string, runOnNetworkError bool) scheduleBuilderEnd {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if sb.schedule.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting EnabledEntity on a schedule with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
sb.schedule.enabledEntity = entityId
|
||||||
|
sb.schedule.enabledEntityState = state
|
||||||
|
sb.schedule.enabledEntityRunOnError = runOnNetworkError
|
||||||
|
return sb
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Disable this schedule when the current state of {entityId} matches {state}.
|
||||||
|
If there is a network error while retrieving state, the schedule runs if {runOnNetworkError} is true.
|
||||||
|
*/
|
||||||
|
func (sb scheduleBuilderEnd) DisabledEntity(entityId, state string, runOnNetworkError bool) scheduleBuilderEnd {
|
||||||
|
if entityId == "" || state == "" {
|
||||||
|
panic(fmt.Sprintf("Either entityId or state is empty in EnabledEntity entityId='%s' state='%s'", entityId, state))
|
||||||
|
}
|
||||||
|
if sb.schedule.enabledEntity != "" {
|
||||||
|
panic(fmt.Sprintf("You can't use EnabledEntity and DisabledEntity together. Error occurred while setting DisabledEntity on a schedule with params entityId=%s state=%s runOnNetworkError=%t", entityId, state, runOnNetworkError))
|
||||||
|
}
|
||||||
|
sb.schedule.disabledEntity = entityId
|
||||||
|
sb.schedule.disabledEntityState = state
|
||||||
|
sb.schedule.disabledEntityRunOnError = runOnNetworkError
|
||||||
|
return sb
|
||||||
|
}
|
||||||
|
|
||||||
func (sb scheduleBuilderEnd) Build() DailySchedule {
|
func (sb scheduleBuilderEnd) Build() DailySchedule {
|
||||||
return sb.schedule
|
return sb.schedule
|
||||||
}
|
}
|
||||||
@@ -145,6 +186,12 @@ func (s DailySchedule) maybeRunCallback(a *App) {
|
|||||||
if c := checkAllowlistDates(s.allowlistDates); c.fail {
|
if c := checkAllowlistDates(s.allowlistDates); c.fail {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c := checkEnabledEntity(a.state, s.enabledEntity, s.enabledEntityState, s.enabledEntityRunOnError); c.fail {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c := checkDisabledEntity(a.state, s.disabledEntity, s.disabledEntityState, s.disabledEntityRunOnError); c.fail {
|
||||||
|
return
|
||||||
|
}
|
||||||
go s.callback(a.service, a.state)
|
go s.callback(a.service, a.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
state.go
2
state.go
@@ -36,7 +36,7 @@ func newState(c *http.HttpClient, homeZoneEntityId string) (*State, error) {
|
|||||||
func (s *State) getLatLong(c *http.HttpClient, homeZoneEntityId string) error {
|
func (s *State) getLatLong(c *http.HttpClient, homeZoneEntityId string) error {
|
||||||
resp, err := s.Get(homeZoneEntityId)
|
resp, err := s.Get(homeZoneEntityId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("couldn't get latitude/longitude from home assistant entity '%s'. Did you type it correctly? It should be a zone like 'zone.home'.\n", homeZoneEntityId))
|
return fmt.Errorf("couldn't get latitude/longitude from home assistant entity '%s'. Did you type it correctly? It should be a zone like 'zone.home'", homeZoneEntityId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Attributes["latitude"] != nil {
|
if resp.Attributes["latitude"] != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user