Skip to content

Commit f690cdb

Browse files
author
niels
committed
updates for changed automation format, small fixes
1 parent 03a6460 commit f690cdb

8 files changed

Lines changed: 55 additions & 50 deletions

File tree

custom_components/alarmo/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,9 @@ async def async_handle_event(event: str, area_id: str, args: dict = {}):
326326

327327
data = {
328328
"reason": reasons[event],
329-
"command": args["command"].upper()
330329
}
330+
if "command" in args:
331+
data["command"] = args["command"].upper()
331332
if event == const.EVENT_FAILED_TO_ARM:
332333
data["sensors"] = list(args["open_sensors"].keys())
333334

custom_components/alarmo/automations.py

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
)
88

99
from homeassistant.const import (
10-
ATTR_STATE,
1110
ATTR_SERVICE,
1211
ATTR_SERVICE_DATA,
1312
ATTR_ENTITY_ID,
13+
CONF_TYPE,
1414
# STATE_UNKNOWN,
1515
# STATE_OPEN,
1616
# STATE_CLOSED,
@@ -31,6 +31,24 @@
3131
EVENT_ARM_FAILURE = "arm_failure"
3232

3333

34+
def validate_area(trigger, area_id):
35+
if const.ATTR_AREA not in trigger:
36+
return False
37+
elif trigger[const.ATTR_AREA]:
38+
return trigger[const.ATTR_AREA] == area_id
39+
else:
40+
return area_id is None
41+
42+
43+
def validate_modes(trigger, mode):
44+
if const.ATTR_MODES not in trigger:
45+
return False
46+
elif not trigger[const.ATTR_MODES] or not mode:
47+
return True
48+
else:
49+
return mode in trigger[const.ATTR_MODES]
50+
51+
3452
class AutomationHandler:
3553
def __init__(self, hass: HomeAssistant):
3654
self.hass = hass
@@ -65,20 +83,14 @@ async def async_alarm_state_changed(area_id: str, old_state: str, new_state: str
6583
new_state = "armed"
6684

6785
for automation_id, config in self._config.items():
68-
if (
69-
not config[const.ATTR_ENABLED]
70-
or (config[const.ATTR_AREA] != area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1)
71-
):
72-
continue
73-
elif (
74-
len(config[const.ATTR_MODES]) and alarm_entity.arm_mode
75-
and alarm_entity.arm_mode not in config[const.ATTR_MODES]
76-
):
77-
continue
78-
else:
79-
for trigger in config[const.ATTR_TRIGGERS]:
80-
if ATTR_STATE in trigger and trigger[ATTR_STATE] == new_state:
81-
await self.async_execute_automation(automation_id, alarm_entity)
86+
for trigger in config[const.ATTR_TRIGGERS]:
87+
if (
88+
validate_area(trigger, area_id) and
89+
validate_modes(trigger, alarm_entity.arm_mode) and
90+
const.ATTR_EVENT in trigger and
91+
trigger[const.ATTR_EVENT] == new_state
92+
):
93+
await self.async_execute_automation(automation_id, alarm_entity)
8294

8395
async_dispatcher_connect(self.hass, "alarmo_state_updated", async_alarm_state_changed)
8496

@@ -94,20 +106,14 @@ async def async_handle_event(event: str, area_id: str, args: dict = {}):
94106
_LOGGER.debug("{} has failed to arm".format(alarm_entity.entity_id))
95107

96108
for automation_id, config in self._config.items():
97-
if (
98-
not config[const.ATTR_ENABLED]
99-
or (config[const.ATTR_AREA] != area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1)
100-
):
101-
continue
102-
elif (
103-
len(config[const.ATTR_MODES]) and alarm_entity.arm_mode
104-
and alarm_entity.arm_mode not in config[const.ATTR_MODES]
105-
):
106-
continue
107-
else:
108-
for trigger in config[const.ATTR_TRIGGERS]:
109-
if const.ATTR_EVENT in trigger and trigger[const.ATTR_EVENT] == EVENT_ARM_FAILURE:
110-
await self.async_execute_automation(automation_id, alarm_entity)
109+
for trigger in config[const.ATTR_TRIGGERS]:
110+
if (
111+
validate_area(trigger, area_id) and
112+
validate_modes(trigger, alarm_entity.arm_mode) and
113+
const.ATTR_EVENT in trigger and
114+
trigger[const.ATTR_EVENT] == EVENT_ARM_FAILURE
115+
):
116+
await self.async_execute_automation(automation_id, alarm_entity)
111117

112118
async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event)
113119

@@ -121,12 +127,11 @@ async def async_execute_automation(self, automation_id: str, alarm_entity: Alarm
121127
service_call = {
122128
"service": action[ATTR_SERVICE]
123129
}
124-
if ATTR_ENTITY_ID in action:
130+
if ATTR_ENTITY_ID in action and action[ATTR_ENTITY_ID]:
125131
service_call["entity_id"] = action[ATTR_ENTITY_ID]
126132

127133
if (
128-
const.ATTR_IS_NOTIFICATION in self._config[automation_id]
129-
and self._config[automation_id][const.ATTR_IS_NOTIFICATION]
134+
self._config[automation_id][CONF_TYPE] == const.ATTR_NOTIFICATION
130135
and ATTR_MESSAGE in action[ATTR_SERVICE_DATA]
131136
):
132137
data = copy.copy(action[ATTR_SERVICE_DATA])
@@ -153,7 +158,6 @@ async def async_execute_automation(self, automation_id: str, alarm_entity: Alarm
153158
data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{bypassed_sensors}}", bypassed_sensors)
154159

155160
if "{{arm_mode}}" in data[ATTR_MESSAGE]:
156-
_LOGGER.debug(alarm_entity.arm_mode)
157161
arm_mode = alarm_entity.arm_mode if alarm_entity.arm_mode else ""
158162
arm_mode = " ".join(w.capitalize() for w in arm_mode.split("_"))
159163
data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{arm_mode}}", arm_mode)

custom_components/alarmo/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
ATTR_EVENT = "event"
125125
ATTR_REQUIRE_CODE = "require_code"
126126

127-
ATTR_IS_NOTIFICATION = "is_notification"
127+
ATTR_NOTIFICATION = "notification"
128128
ATTR_VERSION = "version"
129129
ATTR_STATE_PAYLOAD = "state_payload"
130130
ATTR_COMMAND_PAYLOAD = "command_payload"

custom_components/alarmo/frontend/src/data/actions.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,11 @@ export const getArmModeOptions = (area: string | number | undefined, areaConfig:
168168
};
169169

170170
export const computeEntityDisplay = (entity_id: string[], hass: HomeAssistant) => {
171-
172-
173171
let data = entity_id.map(e => {
174172
let output = {
175173
value: e,
176-
name: hass.states[e].attributes.friendly_name || computeEntity(e),
177-
icon: hass.states[e].attributes.icon || domainIcon(computeDomain(e)),
174+
name: e in hass.states ? hass.states[e].attributes.friendly_name || computeEntity(e) : e,
175+
icon: e in hass.states ? hass.states[e].attributes.icon || domainIcon(computeDomain(e)) : undefined,
178176
description: e
179177
};
180178
return output;

custom_components/alarmo/frontend/src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export type AutomationTrigger = {
7878

7979
export type AutomationAction = {
8080
service?: string;
81-
service_data?: Dictionary<any> & { entity_id?: any, message?: any, title?: any };
81+
entity_id?: string;
82+
service_data?: Dictionary<any> & { message?: any, title?: any };
8283
}
8384

8485
export interface AlarmoAutomation {

custom_components/alarmo/frontend/src/views/actions/automation-editor-card.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export class AutomationEditorCard extends LitElement {
157157
.items=${computeEntityDisplay(getAutomationEntities(this.hass), this.hass)}
158158
?disabled=${!getAutomationEntities(this.hass).length}
159159
label=${localize('panels.actions.cards.new_action.fields.entity.heading', this.hass.language)}
160-
.value=${Unique(this.config.actions.map(e => e.service_data?.entity_id).filter(isDefined)) || []}
160+
.value=${Unique(this.config.actions.map(e => e.entity_id).filter(isDefined)) || []}
161161
@value-changed=${this._setEntity}
162162
?invalid=${this.errors.entity_id}
163163
></alarmo-selector>
@@ -315,11 +315,11 @@ export class AutomationEditorCard extends LitElement {
315315
if (actionConfig.length > value.length)
316316
actionConfig = [actionConfig[0], ...actionConfig.slice(1, value.length)];
317317

318-
if (!value.length) Object.assign(actionConfig, { [0]: { ...actionConfig[0], service_data: { ...omit(actionConfig[0].service_data || {}, 'entity_id') } } });
318+
if (!value.length) Object.assign(actionConfig, { [0]: omit(actionConfig[0], 'entity_id') });
319319

320320
value.forEach((entity, i) => {
321321
let action = actionConfig.length > i ? { ...actionConfig[i] } : {};
322-
action = { ...action, service_data: { ...action.service_data || {}, entity_id: entity } }
322+
action = { ...action, entity_id: entity };
323323
Object.assign(actionConfig, { [i]: action });
324324
});
325325

@@ -332,7 +332,7 @@ export class AutomationEditorCard extends LitElement {
332332
let actionConfig = this.config.actions;
333333

334334
actionConfig.forEach((e, i) => {
335-
const domain = e.service_data?.entity_id ? computeDomain(e.service_data?.entity_id) : 'homeassistant';
335+
const domain = e.entity_id ? computeDomain(e.entity_id) : 'homeassistant';
336336
Object.assign(actionConfig, { [i]: { service: `${domain}.${action}`, ...omit(e, 'service') } });
337337
});
338338
this.config = { ...this.config, actions: actionConfig };
@@ -382,7 +382,7 @@ export class AutomationEditorCard extends LitElement {
382382
if (!services.length || !services.every(e => isValidService(e, this.hass)))
383383
this.errors = { ...this.errors, service: true };
384384

385-
let entities = data.actions.map(e => (e.service_data || {}).entity_id);
385+
let entities = data.actions.map(e => e.entity_id);
386386
if (this.viewMode == ViewMode.Yaml) entities = entities.filter(isDefined);
387387
if (!data.actions.length || !entities.every(e => isValidEntity(e, this.hass)))
388388
this.errors = { ...this.errors, entity_id: true };
@@ -396,7 +396,7 @@ export class AutomationEditorCard extends LitElement {
396396
private _validAction() {
397397
const data = this._parseAutomation();
398398
const services = data.actions.map(e => e.service);
399-
let entities = data.actions.map(e => (e.service_data || {}).entity_id);
399+
let entities = data.actions.map(e => e.entity_id);
400400
if (this.viewMode == ViewMode.Yaml) entities = entities.filter(isDefined);
401401

402402
return (
@@ -413,7 +413,7 @@ export class AutomationEditorCard extends LitElement {
413413
private _namePlaceholder() {
414414
if (!this._validAction) return "";
415415
const event = this.config.triggers[0].event;
416-
const entities = this.config.actions.map(e => (e.service_data || {}).entity_id).filter(isDefined) as string[];
416+
const entities = this.config.actions.map(e => e.entity_id).filter(isDefined) as string[];
417417
const entity = computeEntityDisplay(entities, this.hass).map(e => e.name).join(", ");
418418
const services = Unique(this.config.actions.map(e => e.service).filter(isDefined).map(e => computeEntity(e)));
419419
let state: string | undefined = undefined;

custom_components/alarmo/frontend/src/views/actions/notification-editor-card.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,9 @@ export class NotificationEditorCard extends LitElement {
400400
if (!isValidString(actionConfig.service_data?.message))
401401
this.errors = { ...this.errors, message: true };
402402

403-
if (!isValidString(actionConfig.service_data?.title))
404-
this.errors = { ...this.errors, title: true };
403+
// title is optional
404+
// if (!isValidString(actionConfig.service_data?.title))
405+
// this.errors = { ...this.errors, title: true };
405406

406407
if (!isValidString(data.name))
407408
this.errors = { ...this.errors, name: true };

custom_components/alarmo/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class ActionEntry:
144144
"""Action storage Entry."""
145145

146146
service = attr.ib(type=str, default="")
147-
entity_id = attr.ib(type=str, default="")
147+
entity_id = attr.ib(type=str, default=None)
148148
service_data = attr.ib(type=dict, default={})
149149

150150

0 commit comments

Comments
 (0)