Skip to content

Commit b134f8d

Browse files
authored
Merge pull request #351 from astrandb/DeviceAutomations
Add device automation triggers and conditions
2 parents 6b3420b + 0a8dd01 commit b134f8d

File tree

3 files changed

+188
-4
lines changed

3 files changed

+188
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Provide the device conditions for Miele integration."""
2+
from __future__ import annotations
3+
4+
import voluptuous as vol
5+
6+
from homeassistant.const import (
7+
ATTR_ENTITY_ID,
8+
CONF_CONDITION,
9+
CONF_DEVICE_ID,
10+
CONF_DOMAIN,
11+
CONF_ENTITY_ID,
12+
CONF_TYPE,
13+
)
14+
from homeassistant.core import HomeAssistant, callback
15+
from homeassistant.helpers import (
16+
condition,
17+
config_validation as cv,
18+
entity_registry as er,
19+
)
20+
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
21+
22+
from .const import DOMAIN, STATE_STATUS
23+
24+
CONDITION_TYPES = list(STATE_STATUS.values())
25+
26+
CONDITION_SCHEMA = cv.DEVICE_CONDITION_BASE_SCHEMA.extend(
27+
{
28+
vol.Required(CONF_ENTITY_ID): cv.entity_id,
29+
vol.Required(CONF_TYPE): vol.In(CONDITION_TYPES),
30+
}
31+
)
32+
33+
34+
async def async_get_conditions(
35+
hass: HomeAssistant, device_id: str
36+
) -> list[dict[str, str]]:
37+
"""List device conditions for miele devices."""
38+
registry = er.async_get(hass)
39+
conditions = []
40+
41+
# Get all the integrations entities for this device
42+
for entry in er.async_entries_for_device(registry, device_id):
43+
if entry.translation_key == "status":
44+
# Add conditions for each entity that belongs to this integration
45+
base_condition = {
46+
CONF_CONDITION: "device",
47+
CONF_DEVICE_ID: device_id,
48+
CONF_DOMAIN: DOMAIN,
49+
CONF_ENTITY_ID: entry.entity_id,
50+
}
51+
conditions += [
52+
{**base_condition, CONF_TYPE: cond} for cond in CONDITION_TYPES
53+
]
54+
55+
return conditions
56+
57+
58+
@callback
59+
def async_condition_from_config(
60+
hass: HomeAssistant, config: ConfigType
61+
) -> condition.ConditionCheckerType:
62+
"""Create a function to test a device condition."""
63+
state = config[CONF_TYPE]
64+
65+
@callback
66+
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
67+
"""Test if an entity is a certain state."""
68+
return condition.state(hass, config[ATTR_ENTITY_ID], state)
69+
70+
return test_is_state
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""Provides device triggers for miele integration."""
2+
from __future__ import annotations
3+
4+
from typing import Any
5+
6+
import voluptuous as vol
7+
8+
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
9+
from homeassistant.components.homeassistant.triggers import state as state_trigger
10+
from homeassistant.const import (
11+
CONF_DEVICE_ID,
12+
CONF_DOMAIN,
13+
CONF_ENTITY_ID,
14+
CONF_PLATFORM,
15+
CONF_TYPE,
16+
)
17+
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
18+
from homeassistant.helpers import config_validation as cv, entity_registry as er
19+
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
20+
from homeassistant.helpers.typing import ConfigType
21+
22+
from .const import DOMAIN, STATE_STATUS
23+
24+
TRIGGER_TYPES = list(STATE_STATUS.values())
25+
26+
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
27+
{
28+
vol.Required(CONF_ENTITY_ID): cv.entity_id,
29+
vol.Required(CONF_TYPE): vol.In(TRIGGER_TYPES),
30+
}
31+
)
32+
33+
34+
async def async_get_triggers(
35+
hass: HomeAssistant, device_id: str
36+
) -> list[dict[str, Any]]:
37+
"""List device triggers for Miele devices."""
38+
registry = er.async_get(hass)
39+
triggers = []
40+
41+
# Get all the integrations entities for this device
42+
for entry in er.async_entries_for_device(registry, device_id):
43+
if entry.translation_key == "status":
44+
# Add triggers for each entity that belongs to this integration
45+
base_trigger = {
46+
CONF_PLATFORM: "device",
47+
CONF_DEVICE_ID: device_id,
48+
CONF_DOMAIN: DOMAIN,
49+
CONF_ENTITY_ID: entry.entity_id,
50+
}
51+
for state_value in TRIGGER_TYPES:
52+
triggers.append({**base_trigger, CONF_TYPE: state_value})
53+
54+
return triggers
55+
56+
57+
async def async_attach_trigger(
58+
hass: HomeAssistant,
59+
config: ConfigType,
60+
action: TriggerActionType,
61+
trigger_info: TriggerInfo,
62+
) -> CALLBACK_TYPE:
63+
"""Attach a trigger."""
64+
65+
to_state = config[CONF_TYPE]
66+
state_config = {
67+
state_trigger.CONF_PLATFORM: "state",
68+
CONF_ENTITY_ID: config[CONF_ENTITY_ID],
69+
state_trigger.CONF_TO: to_state,
70+
}
71+
state_config = await state_trigger.async_validate_trigger_config(hass, state_config)
72+
return await state_trigger.async_attach_trigger(
73+
hass, state_config, action, trigger_info, platform_type="device"
74+
)

custom_components/miele/translations/en.json

+44-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,46 @@
2929
}
3030
}
3131
},
32+
"device_automation": {
33+
"condition_type": {
34+
"failure": "{entity_name} is failing",
35+
"idle": "{entity_name} is idle",
36+
"not_connected": "{entity_name} is not connected",
37+
"off": "{entity_name} is off",
38+
"on": "{entity_name} is on",
39+
"pause": "{entity_name} is pausing",
40+
"program_ended": "{entity_name} has ended program",
41+
"program_interrupted": "{entity_name} has interrupted program",
42+
"programmed": "{entity_name} is programmed",
43+
"rinse_hold": "{entity_name} is in rinse hold",
44+
"running": "{entity_name} is running",
45+
"service": "{entity_name} is in service",
46+
"supercooling": "{entity_name} is supercooling",
47+
"supercooling_superfreezing": "{entity_name} is supercooling/superheating",
48+
"superfreezing": "{entity_name} is superfreezing",
49+
"superheating": "{entity_name} is superheating",
50+
"waiting_to_start": "{entity_name} is waiting to start"
51+
},
52+
"trigger_type": {
53+
"failure": "{entity_name} Failure",
54+
"idle": "{entity_name} Idle",
55+
"not_connected": "{entity_name} Not connected",
56+
"off": "{entity_name} Off",
57+
"on": "{entity_name} On",
58+
"pause": "{entity_name} Pause",
59+
"program_ended": "{entity_name} Program ended",
60+
"program_interrupted": "{entity_name} Program interrupted",
61+
"programmed": "{entity_name} Programmed",
62+
"rinse_hold": "{entity_name} Rinse hold",
63+
"running": "{entity_name} Running",
64+
"service": "{entity_name} Service",
65+
"supercooling_superfreezing": "{entity_name} Supercooling/superheating",
66+
"supercooling": "{entity_name} Supercooling",
67+
"superfreezing": "{entity_name} Superfreezing",
68+
"superheating": "{entity_name} Superheating",
69+
"waiting_to_start": "{entity_name} Waiting to start"
70+
}
71+
},
3272
"entity": {
3373
"binary_sensor": {
3474
"door": {
@@ -169,7 +209,7 @@
169209
"descaling": "Appliance descaling",
170210
"down_duvets": "Down duvets",
171211
"down_filled_items": "Down-filled items",
172-
"drain_spin": "Drain\/spin",
212+
"drain_spin": "Drain/spin",
173213
"eco": "ECO",
174214
"eco_40_60": "ECO 40-60",
175215
"eco_fan_heat": "Eco fan heat",
@@ -217,7 +257,7 @@
217257
"rinse": "Rinse",
218258
"rinse_out_lint": "Rinse out lint",
219259
"ristretto": "Ristretto",
220-
"separate_rinse_starch": "Separate rinse\/starch",
260+
"separate_rinse_starch": "Separate rinse/starch",
221261
"shirts": "Shirts",
222262
"silent": "Silent",
223263
"silks": "Silks",
@@ -319,7 +359,7 @@
319359
"name": "Program type",
320360
"state": {
321361
"automatic_program": "Automatic program",
322-
"cleaning_care_program": "Cleaning\/care program",
362+
"cleaning_care_program": "Cleaning/care program",
323363
"maintenance_program": "Maintenance program",
324364
"normal_operation_mode": "Normal operation mode",
325365
"own_program": "Own program"
@@ -356,7 +396,7 @@
356396
"running": "Running",
357397
"service": "Service",
358398
"supercooling": "Supercooling",
359-
"supercooling_superfreezing": "Supercooling\/superfreezing",
399+
"supercooling_superfreezing": "Supercooling/superfreezing",
360400
"superfreezing": "Superfreezing",
361401
"superheating": "Superheating",
362402
"waiting_to_start": "Waiting to start"

0 commit comments

Comments
 (0)