Skip to content

Commit a1f2eb4

Browse files
authored
Move trigger-specific fields into options in new-style triggers (home-assistant#151314)
1 parent c4ddc03 commit a1f2eb4

File tree

7 files changed

+571
-225
lines changed

7 files changed

+571
-225
lines changed

homeassistant/components/zwave_js/device_trigger.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
CONF_DEVICE_ID,
1717
CONF_DOMAIN,
1818
CONF_ENTITY_ID,
19+
CONF_OPTIONS,
1920
CONF_PLATFORM,
2021
CONF_TYPE,
2122
)
@@ -434,12 +435,13 @@ async def async_attach_trigger(
434435

435436
if trigger_platform == VALUE_UPDATED_PLATFORM_TYPE:
436437
zwave_js_config = {
437-
state.CONF_PLATFORM: trigger_platform,
438-
CONF_DEVICE_ID: config[CONF_DEVICE_ID],
438+
CONF_OPTIONS: {
439+
CONF_DEVICE_ID: config[CONF_DEVICE_ID],
440+
},
439441
}
440442
copy_available_params(
441443
config,
442-
zwave_js_config,
444+
zwave_js_config[CONF_OPTIONS],
443445
[
444446
ATTR_COMMAND_CLASS,
445447
ATTR_PROPERTY,
@@ -453,7 +455,7 @@ async def async_attach_trigger(
453455
hass, zwave_js_config
454456
)
455457
return await attach_value_updated_trigger(
456-
hass, zwave_js_config, action, trigger_info
458+
hass, zwave_js_config[CONF_OPTIONS], action, trigger_info
457459
)
458460

459461
raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")

homeassistant/components/zwave_js/triggers/event.py

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ATTR_CONFIG_ENTRY_ID,
1616
ATTR_DEVICE_ID,
1717
ATTR_ENTITY_ID,
18+
CONF_OPTIONS,
1819
CONF_PLATFORM,
1920
)
2021
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
@@ -25,6 +26,7 @@
2526
TriggerActionType,
2627
TriggerData,
2728
TriggerInfo,
29+
move_top_level_schema_fields_to_options,
2830
)
2931
from homeassistant.helpers.typing import ConfigType
3032

@@ -95,55 +97,37 @@ def validate_event_data(obj: dict) -> dict:
9597
return obj
9698

9799

98-
TRIGGER_SCHEMA = vol.All(
99-
cv.TRIGGER_BASE_SCHEMA.extend(
100-
{
101-
vol.Required(CONF_PLATFORM): PLATFORM_TYPE,
102-
vol.Optional(ATTR_CONFIG_ENTRY_ID): str,
103-
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
104-
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
105-
vol.Required(ATTR_EVENT_SOURCE): vol.In(["controller", "driver", "node"]),
106-
vol.Required(ATTR_EVENT): cv.string,
107-
vol.Optional(ATTR_EVENT_DATA): dict,
108-
vol.Optional(ATTR_PARTIAL_DICT_MATCH, default=False): bool,
109-
},
110-
),
111-
validate_event_name,
112-
validate_event_data,
113-
vol.Any(
114-
validate_non_node_event_source,
115-
cv.has_at_least_one_key(ATTR_DEVICE_ID, ATTR_ENTITY_ID),
116-
),
117-
)
118-
119-
120-
async def async_validate_trigger_config(
121-
hass: HomeAssistant, config: ConfigType
122-
) -> ConfigType:
123-
"""Validate config."""
124-
config = TRIGGER_SCHEMA(config)
125-
126-
if ATTR_CONFIG_ENTRY_ID in config:
127-
entry_id = config[ATTR_CONFIG_ENTRY_ID]
128-
if hass.config_entries.async_get_entry(entry_id) is None:
129-
raise vol.Invalid(f"Config entry '{entry_id}' not found")
130-
131-
if async_bypass_dynamic_config_validation(hass, config):
132-
return config
133-
134-
if config[ATTR_EVENT_SOURCE] == "node" and not async_get_nodes_from_targets(
135-
hass, config
136-
):
137-
raise vol.Invalid(
138-
f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s."
100+
_OPTIONS_SCHEMA_DICT = {
101+
vol.Optional(ATTR_CONFIG_ENTRY_ID): str,
102+
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
103+
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
104+
vol.Required(ATTR_EVENT_SOURCE): vol.In(["controller", "driver", "node"]),
105+
vol.Required(ATTR_EVENT): cv.string,
106+
vol.Optional(ATTR_EVENT_DATA): dict,
107+
vol.Optional(ATTR_PARTIAL_DICT_MATCH, default=False): bool,
108+
}
109+
110+
_CONFIG_SCHEMA = vol.Schema(
111+
{
112+
vol.Required(CONF_OPTIONS): vol.All(
113+
_OPTIONS_SCHEMA_DICT,
114+
validate_event_name,
115+
validate_event_data,
116+
vol.Any(
117+
validate_non_node_event_source,
118+
cv.has_at_least_one_key(ATTR_DEVICE_ID, ATTR_ENTITY_ID),
119+
),
139120
)
140-
141-
return config
121+
}
122+
)
142123

143124

144125
class EventTrigger(Trigger):
145126
"""Z-Wave JS event trigger."""
146127

128+
_hass: HomeAssistant
129+
_options: ConfigType
130+
147131
_event_source: str
148132
_event_name: str
149133
_event_data_filter: dict
@@ -153,17 +137,43 @@ class EventTrigger(Trigger):
153137

154138
_platform_type = PLATFORM_TYPE
155139

156-
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
157-
"""Initialize trigger."""
158-
self._config = config
159-
self._hass = hass
140+
@classmethod
141+
async def async_validate_complete_config(
142+
cls, hass: HomeAssistant, config: ConfigType
143+
) -> ConfigType:
144+
"""Validate complete config."""
145+
config = move_top_level_schema_fields_to_options(config, _OPTIONS_SCHEMA_DICT)
146+
return await super().async_validate_complete_config(hass, config)
160147

161148
@classmethod
162149
async def async_validate_config(
163150
cls, hass: HomeAssistant, config: ConfigType
164151
) -> ConfigType:
165152
"""Validate config."""
166-
return await async_validate_trigger_config(hass, config)
153+
config = _CONFIG_SCHEMA(config)
154+
options = config[CONF_OPTIONS]
155+
156+
if ATTR_CONFIG_ENTRY_ID in options:
157+
entry_id = options[ATTR_CONFIG_ENTRY_ID]
158+
if hass.config_entries.async_get_entry(entry_id) is None:
159+
raise vol.Invalid(f"Config entry '{entry_id}' not found")
160+
161+
if async_bypass_dynamic_config_validation(hass, options):
162+
return config
163+
164+
if options[ATTR_EVENT_SOURCE] == "node" and not async_get_nodes_from_targets(
165+
hass, options
166+
):
167+
raise vol.Invalid(
168+
f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s."
169+
)
170+
171+
return config
172+
173+
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
174+
"""Initialize trigger."""
175+
self._hass = hass
176+
self._options = config[CONF_OPTIONS]
167177

168178
async def async_attach(
169179
self,
@@ -172,17 +182,17 @@ async def async_attach(
172182
) -> CALLBACK_TYPE:
173183
"""Attach a trigger."""
174184
dev_reg = dr.async_get(self._hass)
175-
config = self._config
176-
if config[ATTR_EVENT_SOURCE] == "node" and not async_get_nodes_from_targets(
177-
self._hass, config, dev_reg=dev_reg
185+
options = self._options
186+
if options[ATTR_EVENT_SOURCE] == "node" and not async_get_nodes_from_targets(
187+
self._hass, options, dev_reg=dev_reg
178188
):
179189
raise ValueError(
180190
f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s."
181191
)
182192

183-
self._event_source = config[ATTR_EVENT_SOURCE]
184-
self._event_name = config[ATTR_EVENT]
185-
self._event_data_filter = config.get(ATTR_EVENT_DATA, {})
193+
self._event_source = options[ATTR_EVENT_SOURCE]
194+
self._event_name = options[ATTR_EVENT]
195+
self._event_data_filter = options.get(ATTR_EVENT_DATA, {})
186196
self._job = HassJob(action)
187197
self._trigger_data = trigger_info["trigger_data"]
188198
self._unsubs: list[Callable] = []
@@ -199,7 +209,7 @@ def _async_on_event(
199209
if key not in event_data:
200210
return
201211
if (
202-
self._config[ATTR_PARTIAL_DICT_MATCH]
212+
self._options[ATTR_PARTIAL_DICT_MATCH]
203213
and isinstance(event_data[key], dict)
204214
and isinstance(val, dict)
205215
):
@@ -255,10 +265,10 @@ def _create_zwave_listeners(self) -> None:
255265
dev_reg = dr.async_get(self._hass)
256266
if not (
257267
nodes := async_get_nodes_from_targets(
258-
self._hass, self._config, dev_reg=dev_reg
268+
self._hass, self._options, dev_reg=dev_reg
259269
)
260270
):
261-
entry_id = self._config[ATTR_CONFIG_ENTRY_ID]
271+
entry_id = self._options[ATTR_CONFIG_ENTRY_ID]
262272
entry = self._hass.config_entries.async_get_entry(entry_id)
263273
assert entry
264274
client = entry.runtime_data.client

homeassistant/components/zwave_js/triggers/value_updated.py

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@
1010
from zwave_js_server.model.driver import Driver
1111
from zwave_js_server.model.value import Value, get_value_id_str
1212

13-
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_PLATFORM, MATCH_ALL
13+
from homeassistant.const import (
14+
ATTR_DEVICE_ID,
15+
ATTR_ENTITY_ID,
16+
CONF_OPTIONS,
17+
CONF_PLATFORM,
18+
MATCH_ALL,
19+
)
1420
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
1521
from homeassistant.helpers import config_validation as cv, device_registry as dr
1622
from homeassistant.helpers.dispatcher import async_dispatcher_connect
17-
from homeassistant.helpers.trigger import Trigger, TriggerActionType, TriggerInfo
23+
from homeassistant.helpers.trigger import (
24+
Trigger,
25+
TriggerActionType,
26+
TriggerInfo,
27+
move_top_level_schema_fields_to_options,
28+
)
1829
from homeassistant.helpers.typing import ConfigType
1930

2031
from ..config_validation import VALUE_SCHEMA
@@ -46,40 +57,40 @@
4657
ATTR_FROM = "from"
4758
ATTR_TO = "to"
4859

49-
TRIGGER_SCHEMA = vol.All(
50-
cv.TRIGGER_BASE_SCHEMA.extend(
51-
{
52-
vol.Required(CONF_PLATFORM): PLATFORM_TYPE,
53-
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
54-
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
55-
vol.Required(ATTR_COMMAND_CLASS): vol.In(
56-
{cc.value: cc.name for cc in CommandClass}
57-
),
58-
vol.Required(ATTR_PROPERTY): vol.Any(vol.Coerce(int), cv.string),
59-
vol.Optional(ATTR_ENDPOINT): vol.Coerce(int),
60-
vol.Optional(ATTR_PROPERTY_KEY): vol.Any(vol.Coerce(int), cv.string),
61-
vol.Optional(ATTR_FROM, default=MATCH_ALL): vol.Any(
62-
VALUE_SCHEMA, [VALUE_SCHEMA]
63-
),
64-
vol.Optional(ATTR_TO, default=MATCH_ALL): vol.Any(
65-
VALUE_SCHEMA, [VALUE_SCHEMA]
66-
),
67-
},
60+
_OPTIONS_SCHEMA_DICT = {
61+
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
62+
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
63+
vol.Required(ATTR_COMMAND_CLASS): vol.In(
64+
{cc.value: cc.name for cc in CommandClass}
6865
),
69-
cv.has_at_least_one_key(ATTR_ENTITY_ID, ATTR_DEVICE_ID),
66+
vol.Required(ATTR_PROPERTY): vol.Any(vol.Coerce(int), cv.string),
67+
vol.Optional(ATTR_ENDPOINT): vol.Coerce(int),
68+
vol.Optional(ATTR_PROPERTY_KEY): vol.Any(vol.Coerce(int), cv.string),
69+
vol.Optional(ATTR_FROM, default=MATCH_ALL): vol.Any(VALUE_SCHEMA, [VALUE_SCHEMA]),
70+
vol.Optional(ATTR_TO, default=MATCH_ALL): vol.Any(VALUE_SCHEMA, [VALUE_SCHEMA]),
71+
}
72+
73+
_CONFIG_SCHEMA = vol.Schema(
74+
{
75+
vol.Required(CONF_OPTIONS): vol.All(
76+
_OPTIONS_SCHEMA_DICT,
77+
cv.has_at_least_one_key(ATTR_ENTITY_ID, ATTR_DEVICE_ID),
78+
),
79+
},
7080
)
7181

7282

7383
async def async_validate_trigger_config(
7484
hass: HomeAssistant, config: ConfigType
7585
) -> ConfigType:
7686
"""Validate config."""
77-
config = TRIGGER_SCHEMA(config)
87+
config = _CONFIG_SCHEMA(config)
88+
options = config[CONF_OPTIONS]
7889

79-
if async_bypass_dynamic_config_validation(hass, config):
90+
if async_bypass_dynamic_config_validation(hass, options):
8091
return config
8192

82-
if not async_get_nodes_from_targets(hass, config):
93+
if not async_get_nodes_from_targets(hass, options):
8394
raise vol.Invalid(
8495
f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s."
8596
)
@@ -88,25 +99,25 @@ async def async_validate_trigger_config(
8899

89100
async def async_attach_trigger(
90101
hass: HomeAssistant,
91-
config: ConfigType,
102+
options: ConfigType,
92103
action: TriggerActionType,
93104
trigger_info: TriggerInfo,
94105
*,
95106
platform_type: str = PLATFORM_TYPE,
96107
) -> CALLBACK_TYPE:
97108
"""Listen for state changes based on configuration."""
98109
dev_reg = dr.async_get(hass)
99-
if not async_get_nodes_from_targets(hass, config, dev_reg=dev_reg):
110+
if not async_get_nodes_from_targets(hass, options, dev_reg=dev_reg):
100111
raise ValueError(
101112
f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s."
102113
)
103114

104-
from_value = config[ATTR_FROM]
105-
to_value = config[ATTR_TO]
106-
command_class = config[ATTR_COMMAND_CLASS]
107-
property_ = config[ATTR_PROPERTY]
108-
endpoint = config.get(ATTR_ENDPOINT)
109-
property_key = config.get(ATTR_PROPERTY_KEY)
115+
from_value = options[ATTR_FROM]
116+
to_value = options[ATTR_TO]
117+
command_class = options[ATTR_COMMAND_CLASS]
118+
property_ = options[ATTR_PROPERTY]
119+
endpoint = options.get(ATTR_ENDPOINT)
120+
property_key = options.get(ATTR_PROPERTY_KEY)
110121
unsubs: list[Callable] = []
111122
job = HassJob(action)
112123

@@ -174,7 +185,7 @@ def _create_zwave_listeners() -> None:
174185
# Nodes list can come from different drivers and we will need to listen to
175186
# server connections for all of them.
176187
drivers: set[Driver] = set()
177-
for node in async_get_nodes_from_targets(hass, config, dev_reg=dev_reg):
188+
for node in async_get_nodes_from_targets(hass, options, dev_reg=dev_reg):
178189
driver = node.client.driver
179190
assert driver is not None # The node comes from the driver.
180191
drivers.add(driver)
@@ -210,10 +221,16 @@ def _create_zwave_listeners() -> None:
210221
class ValueUpdatedTrigger(Trigger):
211222
"""Z-Wave JS value updated trigger."""
212223

213-
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
214-
"""Initialize trigger."""
215-
self._config = config
216-
self._hass = hass
224+
_hass: HomeAssistant
225+
_options: ConfigType
226+
227+
@classmethod
228+
async def async_validate_complete_config(
229+
cls, hass: HomeAssistant, config: ConfigType
230+
) -> ConfigType:
231+
"""Validate complete config."""
232+
config = move_top_level_schema_fields_to_options(config, _OPTIONS_SCHEMA_DICT)
233+
return await super().async_validate_complete_config(hass, config)
217234

218235
@classmethod
219236
async def async_validate_config(
@@ -222,12 +239,17 @@ async def async_validate_config(
222239
"""Validate config."""
223240
return await async_validate_trigger_config(hass, config)
224241

242+
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
243+
"""Initialize trigger."""
244+
self._hass = hass
245+
self._options = config[CONF_OPTIONS]
246+
225247
async def async_attach(
226248
self,
227249
action: TriggerActionType,
228250
trigger_info: TriggerInfo,
229251
) -> CALLBACK_TYPE:
230252
"""Attach a trigger."""
231253
return await async_attach_trigger(
232-
self._hass, self._config, action, trigger_info
254+
self._hass, self._options, action, trigger_info
233255
)

0 commit comments

Comments
 (0)