1 """Provides device triggers for Z-Wave JS."""
3 from __future__
import annotations
7 import voluptuous
as vol
8 from zwave_js_server.const
import CommandClass
11 DEVICE_TRIGGER_BASE_SCHEMA,
12 InvalidDeviceAutomationConfig,
25 config_validation
as cv,
26 device_registry
as dr,
27 entity_registry
as er,
33 from .config_validation
import VALUE_SCHEMA
48 ZWAVE_JS_NOTIFICATION_EVENT,
49 ZWAVE_JS_VALUE_NOTIFICATION_EVENT,
51 from .device_automation_helpers
import (
54 async_bypass_dynamic_config_validation,
55 generate_config_parameter_subtype,
57 from .helpers
import (
58 async_get_node_from_device_id,
59 async_get_node_status_sensor_entity_id,
60 check_type_schema_map,
61 copy_available_params,
62 get_value_state_schema,
63 get_zwave_value_from_config,
64 remove_keys_with_empty_values,
66 from .triggers.value_updated
import (
69 PLATFORM_TYPE
as VALUE_UPDATED_PLATFORM_TYPE,
73 ENTRY_CONTROL_NOTIFICATION =
"event.notification.entry_control"
74 NOTIFICATION_NOTIFICATION =
"event.notification.notification"
75 BASIC_VALUE_NOTIFICATION =
"event.value_notification.basic"
76 CENTRAL_SCENE_VALUE_NOTIFICATION =
"event.value_notification.central_scene"
77 SCENE_ACTIVATION_VALUE_NOTIFICATION =
"event.value_notification.scene_activation"
78 CONFIG_PARAMETER_VALUE_UPDATED = f
"{VALUE_UPDATED_PLATFORM_TYPE}.config_parameter"
79 VALUE_VALUE_UPDATED = f
"{VALUE_UPDATED_PLATFORM_TYPE}.value"
80 NODE_STATUS =
"state.node_status"
83 NOTIFICATION_EVENT_CC_MAPPINGS = (
84 (ENTRY_CONTROL_NOTIFICATION, CommandClass.ENTRY_CONTROL),
85 (NOTIFICATION_NOTIFICATION, CommandClass.NOTIFICATION),
89 BASE_EVENT_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
91 vol.Required(ATTR_COMMAND_CLASS): vol.In([cc.value
for cc
in CommandClass]),
95 NOTIFICATION_NOTIFICATION_SCHEMA = BASE_EVENT_SCHEMA.extend(
97 vol.Required(CONF_TYPE): NOTIFICATION_NOTIFICATION,
98 vol.Optional(f
"{ATTR_TYPE}."): vol.Coerce(int),
99 vol.Optional(ATTR_LABEL): cv.string,
100 vol.Optional(ATTR_EVENT): vol.Coerce(int),
101 vol.Optional(ATTR_EVENT_LABEL): cv.string,
105 ENTRY_CONTROL_NOTIFICATION_SCHEMA = BASE_EVENT_SCHEMA.extend(
107 vol.Required(CONF_TYPE): ENTRY_CONTROL_NOTIFICATION,
108 vol.Optional(ATTR_EVENT_TYPE): vol.Coerce(int),
109 vol.Optional(ATTR_DATA_TYPE): vol.Coerce(int),
113 BASE_VALUE_NOTIFICATION_EVENT_SCHEMA = BASE_EVENT_SCHEMA.extend(
115 vol.Required(ATTR_PROPERTY): vol.Any(int, str),
116 vol.Optional(ATTR_PROPERTY_KEY): vol.Any(int, str),
117 vol.Required(ATTR_ENDPOINT): vol.Coerce(int),
118 vol.Optional(ATTR_VALUE): vol.Coerce(int),
119 vol.Required(CONF_SUBTYPE): cv.string,
123 BASIC_VALUE_NOTIFICATION_SCHEMA = BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
125 vol.Required(CONF_TYPE): BASIC_VALUE_NOTIFICATION,
129 CENTRAL_SCENE_VALUE_NOTIFICATION_SCHEMA = BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
131 vol.Required(CONF_TYPE): CENTRAL_SCENE_VALUE_NOTIFICATION,
135 SCENE_ACTIVATION_VALUE_NOTIFICATION_SCHEMA = (
136 BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
138 vol.Required(CONF_TYPE): SCENE_ACTIVATION_VALUE_NOTIFICATION,
144 BASE_STATE_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
146 vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
150 NODE_STATUS_SCHEMA = BASE_STATE_SCHEMA.extend(
152 vol.Required(CONF_TYPE): NODE_STATUS,
153 vol.Optional(state.CONF_FROM): vol.In(NODE_STATUSES),
154 vol.Optional(state.CONF_TO): vol.In(NODE_STATUSES),
155 vol.Optional(state.CONF_FOR): cv.positive_time_period_dict,
160 BASE_VALUE_UPDATED_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
162 vol.Required(ATTR_COMMAND_CLASS): vol.In([cc.value
for cc
in CommandClass]),
163 vol.Required(ATTR_PROPERTY): vol.Any(int, str),
164 vol.Optional(ATTR_PROPERTY_KEY): vol.Any(
None, vol.Coerce(int), str),
165 vol.Optional(ATTR_ENDPOINT, default=0): vol.Any(
None, vol.Coerce(int)),
166 vol.Optional(ATTR_FROM): VALUE_SCHEMA,
167 vol.Optional(ATTR_TO): VALUE_SCHEMA,
171 CONFIG_PARAMETER_VALUE_UPDATED_SCHEMA = BASE_VALUE_UPDATED_SCHEMA.extend(
173 vol.Required(CONF_TYPE): CONFIG_PARAMETER_VALUE_UPDATED,
174 vol.Required(CONF_SUBTYPE): cv.string,
178 VALUE_VALUE_UPDATED_SCHEMA = BASE_VALUE_UPDATED_SCHEMA.extend(
180 vol.Required(CONF_TYPE): VALUE_VALUE_UPDATED,
185 ENTRY_CONTROL_NOTIFICATION: ENTRY_CONTROL_NOTIFICATION_SCHEMA,
186 NOTIFICATION_NOTIFICATION: NOTIFICATION_NOTIFICATION_SCHEMA,
187 BASIC_VALUE_NOTIFICATION: BASIC_VALUE_NOTIFICATION_SCHEMA,
188 CENTRAL_SCENE_VALUE_NOTIFICATION: CENTRAL_SCENE_VALUE_NOTIFICATION_SCHEMA,
189 SCENE_ACTIVATION_VALUE_NOTIFICATION: SCENE_ACTIVATION_VALUE_NOTIFICATION_SCHEMA,
190 CONFIG_PARAMETER_VALUE_UPDATED: CONFIG_PARAMETER_VALUE_UPDATED_SCHEMA,
191 VALUE_VALUE_UPDATED: VALUE_VALUE_UPDATED_SCHEMA,
192 NODE_STATUS: NODE_STATUS_SCHEMA,
196 TRIGGER_TYPE_SCHEMA = vol.Schema(
197 {vol.Required(CONF_TYPE): vol.In(TYPE_SCHEMA_MAP)}, extra=vol.ALLOW_EXTRA
200 TRIGGER_SCHEMA = vol.All(
201 remove_keys_with_empty_values,
208 hass: HomeAssistant, config: ConfigType
210 """Validate config."""
217 hass, config[CONF_DEVICE_ID]
219 except ValueError
as err:
221 f
"Device {config[CONF_DEVICE_ID]} not found"
224 if bypass_dynamic_config_validation:
227 trigger_type = config[CONF_TYPE]
232 except vol.Invalid
as err:
239 """Get trigger platform from Z-Wave JS trigger type."""
240 trigger_split = trigger_type.split(
".")
243 if (trigger_platform := trigger_split[0]) == DOMAIN:
244 return ".".join(trigger_split[:2])
245 return trigger_platform
249 hass: HomeAssistant, device_id: str
250 ) -> list[dict[str, Any]]:
251 """List device triggers for Z-Wave JS devices."""
252 triggers: list[dict] = []
254 CONF_PLATFORM:
"device",
255 CONF_DEVICE_ID: device_id,
259 dev_reg = dr.async_get(hass)
262 if node.client.driver
and node.client.driver.controller.own_node == node:
266 ent_reg = er.async_get(hass)
268 hass, device_id, ent_reg, dev_reg
272 and (entity := ent_reg.async_get(entity_id))
is not None
273 and not entity.disabled
276 {**base_trigger, CONF_TYPE: NODE_STATUS, CONF_ENTITY_ID: entity.id}
282 {**base_trigger, CONF_TYPE: event_type, ATTR_COMMAND_CLASS: command_class}
283 for event_type, command_class
in NOTIFICATION_EVENT_CC_MAPPINGS
284 if any(cc.id == command_class
for cc
in node.command_classes)
293 CONF_TYPE: CENTRAL_SCENE_VALUE_NOTIFICATION,
294 ATTR_PROPERTY: value.property_,
295 ATTR_PROPERTY_KEY: value.property_key,
296 ATTR_ENDPOINT: value.endpoint,
297 ATTR_COMMAND_CLASS: CommandClass.CENTRAL_SCENE,
298 CONF_SUBTYPE: f
"Endpoint {value.endpoint} Scene {value.property_key}",
300 for value
in node.get_command_class_values(
301 CommandClass.CENTRAL_SCENE
303 if value.property_ ==
"scene"
312 CONF_TYPE: SCENE_ACTIVATION_VALUE_NOTIFICATION,
313 ATTR_PROPERTY: value.property_,
314 ATTR_PROPERTY_KEY: value.property_key,
315 ATTR_ENDPOINT: value.endpoint,
316 ATTR_COMMAND_CLASS: CommandClass.SCENE_ACTIVATION,
317 CONF_SUBTYPE: f
"Endpoint {value.endpoint}",
319 for value
in node.get_command_class_values(
320 CommandClass.SCENE_ACTIVATION
322 if value.property_ ==
"sceneId"
328 if node.device_config.compat.get(
"treatBasicSetAsEvent",
False):
333 CONF_TYPE: BASIC_VALUE_NOTIFICATION,
334 ATTR_PROPERTY: value.property_,
335 ATTR_PROPERTY_KEY: value.property_key,
336 ATTR_ENDPOINT: value.endpoint,
337 ATTR_COMMAND_CLASS: CommandClass.BASIC,
338 CONF_SUBTYPE: f
"Endpoint {value.endpoint}",
340 for value
in node.get_command_class_values(CommandClass.BASIC).values()
341 if value.property_ ==
"event"
346 triggers.append({**base_trigger, CONF_TYPE: VALUE_VALUE_UPDATED})
353 CONF_TYPE: CONFIG_PARAMETER_VALUE_UPDATED,
354 ATTR_PROPERTY: config_value.property_,
355 ATTR_PROPERTY_KEY: config_value.property_key,
356 ATTR_ENDPOINT: config_value.endpoint,
357 ATTR_COMMAND_CLASS: config_value.command_class,
360 for config_value
in node.get_configuration_values().values()
370 action: TriggerActionType,
371 trigger_info: TriggerInfo,
373 """Attach a trigger."""
374 trigger_type = config[CONF_TYPE]
379 if trigger_platform ==
"event":
380 event_data = {CONF_DEVICE_ID: config[CONF_DEVICE_ID]}
382 event.CONF_PLATFORM:
"event",
383 event.CONF_EVENT_DATA: event_data,
386 if ATTR_COMMAND_CLASS
in config:
387 event_data[ATTR_COMMAND_CLASS] = config[ATTR_COMMAND_CLASS]
389 if trigger_type == ENTRY_CONTROL_NOTIFICATION:
390 event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_NOTIFICATION_EVENT
392 elif trigger_type == NOTIFICATION_NOTIFICATION:
393 event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_NOTIFICATION_EVENT
395 config, event_data, [ATTR_LABEL, ATTR_EVENT_LABEL, ATTR_EVENT]
397 if (val := config.get(f
"{ATTR_TYPE}."))
not in (
"",
None):
398 event_data[ATTR_TYPE] = val
399 elif trigger_type
in (
400 BASIC_VALUE_NOTIFICATION,
401 CENTRAL_SCENE_VALUE_NOTIFICATION,
402 SCENE_ACTIVATION_VALUE_NOTIFICATION,
404 event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_VALUE_NOTIFICATION_EVENT
406 config, event_data, [ATTR_PROPERTY, ATTR_PROPERTY_KEY, ATTR_ENDPOINT]
408 if ATTR_VALUE
in config:
409 event_data[ATTR_VALUE_RAW] = config[ATTR_VALUE]
413 event_config = event.TRIGGER_SCHEMA(event_config)
414 return await event.async_attach_trigger(
415 hass, event_config, action, trigger_info, platform_type=
"device"
418 if trigger_platform ==
"state":
419 if trigger_type == NODE_STATUS:
420 state_config = {state.CONF_PLATFORM:
"state"}
422 state_config[state.CONF_ENTITY_ID] = config[CONF_ENTITY_ID]
424 config, state_config, [state.CONF_FOR, state.CONF_FROM, state.CONF_TO]
429 state_config = await state.async_validate_trigger_config(hass, state_config)
430 return await state.async_attach_trigger(
431 hass, state_config, action, trigger_info, platform_type=
"device"
434 if trigger_platform == VALUE_UPDATED_PLATFORM_TYPE:
436 state.CONF_PLATFORM: trigger_platform,
437 CONF_DEVICE_ID: config[CONF_DEVICE_ID],
451 zwave_js_config = await trigger.async_validate_trigger_config(
452 hass, zwave_js_config
454 return await trigger.async_attach_trigger(
455 hass, zwave_js_config, action, trigger_info
462 hass: HomeAssistant, config: ConfigType
463 ) -> dict[str, vol.Schema]:
464 """List trigger capabilities."""
465 trigger_type = config[CONF_TYPE]
470 if trigger_type == NOTIFICATION_NOTIFICATION:
472 "extra_fields": vol.Schema(
474 vol.Optional(f
"{ATTR_TYPE}."): cv.string,
475 vol.Optional(ATTR_LABEL): cv.string,
476 vol.Optional(ATTR_EVENT): cv.string,
477 vol.Optional(ATTR_EVENT_LABEL): cv.string,
482 if trigger_type == ENTRY_CONTROL_NOTIFICATION:
484 "extra_fields": vol.Schema(
486 vol.Optional(ATTR_EVENT_TYPE): cv.string,
487 vol.Optional(ATTR_DATA_TYPE): cv.string,
492 if trigger_type == NODE_STATUS:
494 "extra_fields": vol.Schema(
496 vol.Optional(state.CONF_FROM): vol.In(NODE_STATUSES),
497 vol.Optional(state.CONF_TO): vol.In(NODE_STATUSES),
498 vol.Optional(state.CONF_FOR): cv.positive_time_period_dict,
504 BASIC_VALUE_NOTIFICATION,
505 CENTRAL_SCENE_VALUE_NOTIFICATION,
506 SCENE_ACTIVATION_VALUE_NOTIFICATION,
514 return {
"extra_fields": vol.Schema({vol.Optional(ATTR_VALUE): value_schema})}
516 if trigger_type == CONFIG_PARAMETER_VALUE_UPDATED:
521 "extra_fields": vol.Schema(
523 vol.Optional(ATTR_FROM): value_schema,
524 vol.Optional(ATTR_TO): value_schema,
529 if trigger_type == VALUE_VALUE_UPDATED:
533 "extra_fields": vol.Schema(
535 vol.Required(ATTR_COMMAND_CLASS): vol.In(
537 CommandClass(cc.id).value: cc.name
539 node.command_classes, key=
lambda cc: cc.name
541 if cc.id != CommandClass.CONFIGURATION
544 vol.Required(ATTR_PROPERTY): cv.string,
545 vol.Optional(ATTR_PROPERTY_KEY): cv.string,
546 vol.Optional(ATTR_ENDPOINT): cv.string,
547 vol.Optional(ATTR_FROM): cv.string,
548 vol.Optional(ATTR_TO): cv.string,
str generate_config_parameter_subtype(ConfigurationValue config_value)
bool async_bypass_dynamic_config_validation(HomeAssistant hass, str device_id)
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
ConfigType async_validate_trigger_config(HomeAssistant hass, ConfigType config)
dict[str, vol.Schema] async_get_trigger_capabilities(HomeAssistant hass, ConfigType config)
str get_trigger_platform_from_type(str trigger_type)
list[dict[str, Any]] async_get_triggers(HomeAssistant hass, str device_id)
str|None async_get_node_status_sensor_entity_id(HomeAssistant hass, str device_id, er.EntityRegistry|None ent_reg=None, dr.DeviceRegistry|None dev_reg=None)
None copy_available_params(dict[str, Any] input_dict, dict[str, Any] output_dict, list[str] params)
Callable[[ConfigType], ConfigType] check_type_schema_map(dict[str, vol.Schema] schema_map)
VolSchemaType|vol.Coerce|vol.In|None get_value_state_schema(ZwaveValue value)
ZwaveValue get_zwave_value_from_config(ZwaveNode node, ConfigType config)
ZwaveNode async_get_node_from_device_id(HomeAssistant hass, str device_id, dr.DeviceRegistry|None dev_reg=None)