Skip to content

Commit

Permalink
updates for changed automation format, small fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
niels committed Jun 9, 2021
1 parent 03a6460 commit f690cdb
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 50 deletions.
3 changes: 2 additions & 1 deletion custom_components/alarmo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,9 @@ async def async_handle_event(event: str, area_id: str, args: dict = {}):

data = {
"reason": reasons[event],
"command": args["command"].upper()
}
if "command" in args:
data["command"] = args["command"].upper()
if event == const.EVENT_FAILED_TO_ARM:
data["sensors"] = list(args["open_sensors"].keys())

Expand Down
70 changes: 37 additions & 33 deletions custom_components/alarmo/automations.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
)

from homeassistant.const import (
ATTR_STATE,
ATTR_SERVICE,
ATTR_SERVICE_DATA,
ATTR_ENTITY_ID,
CONF_TYPE,
# STATE_UNKNOWN,
# STATE_OPEN,
# STATE_CLOSED,
Expand All @@ -31,6 +31,24 @@
EVENT_ARM_FAILURE = "arm_failure"


def validate_area(trigger, area_id):
if const.ATTR_AREA not in trigger:
return False
elif trigger[const.ATTR_AREA]:
return trigger[const.ATTR_AREA] == area_id
else:
return area_id is None


def validate_modes(trigger, mode):
if const.ATTR_MODES not in trigger:
return False
elif not trigger[const.ATTR_MODES] or not mode:
return True
else:
return mode in trigger[const.ATTR_MODES]


class AutomationHandler:
def __init__(self, hass: HomeAssistant):
self.hass = hass
Expand Down Expand Up @@ -65,20 +83,14 @@ async def async_alarm_state_changed(area_id: str, old_state: str, new_state: str
new_state = "armed"

for automation_id, config in self._config.items():
if (
not config[const.ATTR_ENABLED]
or (config[const.ATTR_AREA] != area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1)
):
continue
elif (
len(config[const.ATTR_MODES]) and alarm_entity.arm_mode
and alarm_entity.arm_mode not in config[const.ATTR_MODES]
):
continue
else:
for trigger in config[const.ATTR_TRIGGERS]:
if ATTR_STATE in trigger and trigger[ATTR_STATE] == new_state:
await self.async_execute_automation(automation_id, alarm_entity)
for trigger in config[const.ATTR_TRIGGERS]:
if (
validate_area(trigger, area_id) and
validate_modes(trigger, alarm_entity.arm_mode) and
const.ATTR_EVENT in trigger and
trigger[const.ATTR_EVENT] == new_state
):
await self.async_execute_automation(automation_id, alarm_entity)

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

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

for automation_id, config in self._config.items():
if (
not config[const.ATTR_ENABLED]
or (config[const.ATTR_AREA] != area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1)
):
continue
elif (
len(config[const.ATTR_MODES]) and alarm_entity.arm_mode
and alarm_entity.arm_mode not in config[const.ATTR_MODES]
):
continue
else:
for trigger in config[const.ATTR_TRIGGERS]:
if const.ATTR_EVENT in trigger and trigger[const.ATTR_EVENT] == EVENT_ARM_FAILURE:
await self.async_execute_automation(automation_id, alarm_entity)
for trigger in config[const.ATTR_TRIGGERS]:
if (
validate_area(trigger, area_id) and
validate_modes(trigger, alarm_entity.arm_mode) and
const.ATTR_EVENT in trigger and
trigger[const.ATTR_EVENT] == EVENT_ARM_FAILURE
):
await self.async_execute_automation(automation_id, alarm_entity)

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

Expand All @@ -121,12 +127,11 @@ async def async_execute_automation(self, automation_id: str, alarm_entity: Alarm
service_call = {
"service": action[ATTR_SERVICE]
}
if ATTR_ENTITY_ID in action:
if ATTR_ENTITY_ID in action and action[ATTR_ENTITY_ID]:
service_call["entity_id"] = action[ATTR_ENTITY_ID]

if (
const.ATTR_IS_NOTIFICATION in self._config[automation_id]
and self._config[automation_id][const.ATTR_IS_NOTIFICATION]
self._config[automation_id][CONF_TYPE] == const.ATTR_NOTIFICATION
and ATTR_MESSAGE in action[ATTR_SERVICE_DATA]
):
data = copy.copy(action[ATTR_SERVICE_DATA])
Expand All @@ -153,7 +158,6 @@ async def async_execute_automation(self, automation_id: str, alarm_entity: Alarm
data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{bypassed_sensors}}", bypassed_sensors)

if "{{arm_mode}}" in data[ATTR_MESSAGE]:
_LOGGER.debug(alarm_entity.arm_mode)
arm_mode = alarm_entity.arm_mode if alarm_entity.arm_mode else ""
arm_mode = " ".join(w.capitalize() for w in arm_mode.split("_"))
data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{arm_mode}}", arm_mode)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alarmo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
ATTR_EVENT = "event"
ATTR_REQUIRE_CODE = "require_code"

ATTR_IS_NOTIFICATION = "is_notification"
ATTR_NOTIFICATION = "notification"
ATTR_VERSION = "version"
ATTR_STATE_PAYLOAD = "state_payload"
ATTR_COMMAND_PAYLOAD = "command_payload"
Expand Down
6 changes: 2 additions & 4 deletions custom_components/alarmo/frontend/src/data/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,11 @@ export const getArmModeOptions = (area: string | number | undefined, areaConfig:
};

export const computeEntityDisplay = (entity_id: string[], hass: HomeAssistant) => {


let data = entity_id.map(e => {
let output = {
value: e,
name: hass.states[e].attributes.friendly_name || computeEntity(e),
icon: hass.states[e].attributes.icon || domainIcon(computeDomain(e)),
name: e in hass.states ? hass.states[e].attributes.friendly_name || computeEntity(e) : e,
icon: e in hass.states ? hass.states[e].attributes.icon || domainIcon(computeDomain(e)) : undefined,
description: e
};
return output;
Expand Down
3 changes: 2 additions & 1 deletion custom_components/alarmo/frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ export type AutomationTrigger = {

export type AutomationAction = {
service?: string;
service_data?: Dictionary<any> & { entity_id?: any, message?: any, title?: any };
entity_id?: string;
service_data?: Dictionary<any> & { message?: any, title?: any };
}

export interface AlarmoAutomation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class AutomationEditorCard extends LitElement {
.items=${computeEntityDisplay(getAutomationEntities(this.hass), this.hass)}
?disabled=${!getAutomationEntities(this.hass).length}
label=${localize('panels.actions.cards.new_action.fields.entity.heading', this.hass.language)}
.value=${Unique(this.config.actions.map(e => e.service_data?.entity_id).filter(isDefined)) || []}
.value=${Unique(this.config.actions.map(e => e.entity_id).filter(isDefined)) || []}
@value-changed=${this._setEntity}
?invalid=${this.errors.entity_id}
></alarmo-selector>
Expand Down Expand Up @@ -315,11 +315,11 @@ export class AutomationEditorCard extends LitElement {
if (actionConfig.length > value.length)
actionConfig = [actionConfig[0], ...actionConfig.slice(1, value.length)];

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

value.forEach((entity, i) => {
let action = actionConfig.length > i ? { ...actionConfig[i] } : {};
action = { ...action, service_data: { ...action.service_data || {}, entity_id: entity } }
action = { ...action, entity_id: entity };
Object.assign(actionConfig, { [i]: action });
});

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

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

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

return (
Expand All @@ -413,7 +413,7 @@ export class AutomationEditorCard extends LitElement {
private _namePlaceholder() {
if (!this._validAction) return "";
const event = this.config.triggers[0].event;
const entities = this.config.actions.map(e => (e.service_data || {}).entity_id).filter(isDefined) as string[];
const entities = this.config.actions.map(e => e.entity_id).filter(isDefined) as string[];
const entity = computeEntityDisplay(entities, this.hass).map(e => e.name).join(", ");
const services = Unique(this.config.actions.map(e => e.service).filter(isDefined).map(e => computeEntity(e)));
let state: string | undefined = undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,9 @@ export class NotificationEditorCard extends LitElement {
if (!isValidString(actionConfig.service_data?.message))
this.errors = { ...this.errors, message: true };

if (!isValidString(actionConfig.service_data?.title))
this.errors = { ...this.errors, title: true };
// title is optional
// if (!isValidString(actionConfig.service_data?.title))
// this.errors = { ...this.errors, title: true };

if (!isValidString(data.name))
this.errors = { ...this.errors, name: true };
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alarmo/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class ActionEntry:
"""Action storage Entry."""

service = attr.ib(type=str, default="")
entity_id = attr.ib(type=str, default="")
entity_id = attr.ib(type=str, default=None)
service_data = attr.ib(type=dict, default={})


Expand Down

0 comments on commit f690cdb

Please sign in to comment.