1 """Control a MQTT alarm."""
3 from __future__
import annotations
7 import voluptuous
as vol
11 AlarmControlPanelEntityFeature,
12 AlarmControlPanelState,
21 from .
import subscription
22 from .config
import DEFAULT_RETAIN, MQTT_BASE_SCHEMA
24 CONF_COMMAND_TEMPLATE,
28 CONF_SUPPORTED_FEATURES,
31 from .entity
import MqttEntity, async_setup_entity_entry_helper
32 from .models
import MqttCommandTemplate, MqttValueTemplate, ReceiveMessage
33 from .schemas
import MQTT_ENTITY_COMMON_SCHEMA
34 from .util
import valid_publish_topic, valid_subscribe_topic
36 _LOGGER = logging.getLogger(__name__)
40 _SUPPORTED_FEATURES = {
41 "arm_home": AlarmControlPanelEntityFeature.ARM_HOME,
42 "arm_away": AlarmControlPanelEntityFeature.ARM_AWAY,
43 "arm_night": AlarmControlPanelEntityFeature.ARM_NIGHT,
44 "arm_vacation": AlarmControlPanelEntityFeature.ARM_VACATION,
45 "arm_custom_bypass": AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS,
46 "trigger": AlarmControlPanelEntityFeature.TRIGGER,
49 CONF_CODE_ARM_REQUIRED =
"code_arm_required"
50 CONF_CODE_DISARM_REQUIRED =
"code_disarm_required"
51 CONF_CODE_TRIGGER_REQUIRED =
"code_trigger_required"
52 CONF_PAYLOAD_DISARM =
"payload_disarm"
53 CONF_PAYLOAD_ARM_HOME =
"payload_arm_home"
54 CONF_PAYLOAD_ARM_AWAY =
"payload_arm_away"
55 CONF_PAYLOAD_ARM_NIGHT =
"payload_arm_night"
56 CONF_PAYLOAD_ARM_VACATION =
"payload_arm_vacation"
57 CONF_PAYLOAD_ARM_CUSTOM_BYPASS =
"payload_arm_custom_bypass"
58 CONF_PAYLOAD_TRIGGER =
"payload_trigger"
60 MQTT_ALARM_ATTRIBUTES_BLOCKED = frozenset(
62 alarm.ATTR_CHANGED_BY,
63 alarm.ATTR_CODE_ARM_REQUIRED,
64 alarm.ATTR_CODE_FORMAT,
68 DEFAULT_COMMAND_TEMPLATE =
"{{action}}"
69 DEFAULT_ARM_NIGHT =
"ARM_NIGHT"
70 DEFAULT_ARM_VACATION =
"ARM_VACATION"
71 DEFAULT_ARM_AWAY =
"ARM_AWAY"
72 DEFAULT_ARM_HOME =
"ARM_HOME"
73 DEFAULT_ARM_CUSTOM_BYPASS =
"ARM_CUSTOM_BYPASS"
74 DEFAULT_DISARM =
"DISARM"
75 DEFAULT_TRIGGER =
"TRIGGER"
76 DEFAULT_NAME =
"MQTT Alarm"
78 REMOTE_CODE =
"REMOTE_CODE"
79 REMOTE_CODE_TEXT =
"REMOTE_CODE_TEXT"
81 PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend(
83 vol.Optional(CONF_SUPPORTED_FEATURES, default=
list(_SUPPORTED_FEATURES)): [
84 vol.In(_SUPPORTED_FEATURES)
86 vol.Optional(CONF_CODE): cv.string,
87 vol.Optional(CONF_CODE_ARM_REQUIRED, default=
True): cv.boolean,
88 vol.Optional(CONF_CODE_DISARM_REQUIRED, default=
True): cv.boolean,
89 vol.Optional(CONF_CODE_TRIGGER_REQUIRED, default=
True): cv.boolean,
91 CONF_COMMAND_TEMPLATE, default=DEFAULT_COMMAND_TEMPLATE
93 vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic,
94 vol.Optional(CONF_NAME): vol.Any(cv.string,
None),
95 vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
96 vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
97 vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string,
99 CONF_PAYLOAD_ARM_VACATION, default=DEFAULT_ARM_VACATION
102 CONF_PAYLOAD_ARM_CUSTOM_BYPASS, default=DEFAULT_ARM_CUSTOM_BYPASS
104 vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
105 vol.Optional(CONF_PAYLOAD_TRIGGER, default=DEFAULT_TRIGGER): cv.string,
106 vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
107 vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic,
108 vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
110 ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
112 DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)
117 config_entry: ConfigEntry,
118 async_add_entities: AddEntitiesCallback,
120 """Set up MQTT alarm control panel through YAML and through MQTT discovery."""
128 PLATFORM_SCHEMA_MODERN,
133 """Representation of a MQTT alarm status."""
135 _default_name = DEFAULT_NAME
136 _entity_id_format = alarm.ENTITY_ID_FORMAT
137 _attributes_extra_blocked = MQTT_ALARM_ATTRIBUTES_BLOCKED
141 """Return the config schema."""
142 return DISCOVERY_SCHEMA
145 """(Re)Setup the entity."""
147 config.get(CONF_VALUE_TEMPLATE),
149 ).async_render_with_possible_json_value
151 config[CONF_COMMAND_TEMPLATE], entity=self
154 for feature
in self.
_config_config[CONF_SUPPORTED_FEATURES]:
155 self._attr_supported_features |= _SUPPORTED_FEATURES[feature]
157 if (code := self.
_config_config.
get(CONF_CODE))
is None:
159 elif code == REMOTE_CODE
or str(code).isdigit():
166 """Run when new MQTT message has been received."""
168 if not payload.strip():
170 "Ignoring empty payload '%s' after rendering for topic %s",
175 if payload == PAYLOAD_NONE:
179 AlarmControlPanelState.DISARMED,
180 AlarmControlPanelState.ARMED_HOME,
181 AlarmControlPanelState.ARMED_AWAY,
182 AlarmControlPanelState.ARMED_NIGHT,
183 AlarmControlPanelState.ARMED_VACATION,
184 AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
185 AlarmControlPanelState.PENDING,
186 AlarmControlPanelState.ARMING,
187 AlarmControlPanelState.DISARMING,
188 AlarmControlPanelState.TRIGGERED,
190 _LOGGER.warning(
"Received unexpected payload: %s", msg.payload)
192 assert isinstance(payload, str)
197 """(Re)Subscribe to topics."""
203 """(Re)Subscribe to topics."""
204 subscription.async_subscribe_topics_internal(self.
hasshasshass, self.
_sub_state_sub_state)
207 """Send disarm command.
209 This method is a coroutine.
211 code_required: bool = self.
_config_config[CONF_CODE_DISARM_REQUIRED]
212 if code_required
and not self.
_validate_code_validate_code(code,
"disarming"):
214 payload: str = self.
_config_config[CONF_PAYLOAD_DISARM]
215 await self.
_publish_publish(code, payload)
218 """Send arm home command.
220 This method is a coroutine.
222 code_required: bool = self.
_config_config[CONF_CODE_ARM_REQUIRED]
223 if code_required
and not self.
_validate_code_validate_code(code,
"arming home"):
225 action: str = self.
_config_config[CONF_PAYLOAD_ARM_HOME]
226 await self.
_publish_publish(code, action)
229 """Send arm away command.
231 This method is a coroutine.
233 code_required: bool = self.
_config_config[CONF_CODE_ARM_REQUIRED]
234 if code_required
and not self.
_validate_code_validate_code(code,
"arming away"):
236 action: str = self.
_config_config[CONF_PAYLOAD_ARM_AWAY]
237 await self.
_publish_publish(code, action)
240 """Send arm night command.
242 This method is a coroutine.
244 code_required: bool = self.
_config_config[CONF_CODE_ARM_REQUIRED]
245 if code_required
and not self.
_validate_code_validate_code(code,
"arming night"):
247 action: str = self.
_config_config[CONF_PAYLOAD_ARM_NIGHT]
248 await self.
_publish_publish(code, action)
251 """Send arm vacation command.
253 This method is a coroutine.
255 code_required: bool = self.
_config_config[CONF_CODE_ARM_REQUIRED]
256 if code_required
and not self.
_validate_code_validate_code(code,
"arming vacation"):
258 action: str = self.
_config_config[CONF_PAYLOAD_ARM_VACATION]
259 await self.
_publish_publish(code, action)
262 """Send arm custom bypass command.
264 This method is a coroutine.
266 code_required: bool = self.
_config_config[CONF_CODE_ARM_REQUIRED]
267 if code_required
and not self.
_validate_code_validate_code(code,
"arming custom bypass"):
269 action: str = self.
_config_config[CONF_PAYLOAD_ARM_CUSTOM_BYPASS]
270 await self.
_publish_publish(code, action)
273 """Send trigger command.
275 This method is a coroutine.
277 code_required: bool = self.
_config_config[CONF_CODE_TRIGGER_REQUIRED]
278 if code_required
and not self.
_validate_code_validate_code(code,
"triggering"):
280 action: str = self.
_config_config[CONF_PAYLOAD_TRIGGER]
281 await self.
_publish_publish(code, action)
283 async
def _publish(self, code: str |
None, action: str) ->
None:
284 """Publish via mqtt."""
285 variables = {
"action": action,
"code": code}
290 """Validate given code."""
291 conf_code: str |
None = self.
_config_config.
get(CONF_CODE)
295 or (conf_code == REMOTE_CODE
and code)
296 or (conf_code == REMOTE_CODE_TEXT
and code)
299 _LOGGER.warning(
"Wrong code entered for %s", state)
bool _validate_code(self, str|None code, str state)
None async_alarm_arm_custom_bypass(self, str|None code=None)
vol.Schema config_schema()
None _subscribe_topics(self)
None _setup_from_config(self, ConfigType config)
None async_alarm_arm_vacation(self, str|None code=None)
None async_alarm_arm_night(self, str|None code=None)
None async_alarm_arm_home(self, str|None code=None)
None async_alarm_arm_away(self, str|None code=None)
None async_alarm_disarm(self, str|None code=None)
None _prepare_subscribe_topics(self)
None _state_message_received(self, ReceiveMessage msg)
None async_alarm_trigger(self, str|None code=None)
None _publish(self, str|None code, str action)
None async_publish_with_config(self, str topic, PublishPayloadType payload)
bool add_subscription(self, str state_topic_config_key, Callable[[ReceiveMessage], None] msg_callback, set[str]|None tracked_attributes, bool disable_encoding=False)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
None async_setup_entity_entry_helper(HomeAssistant hass, ConfigEntry entry, type[MqttEntity]|None entity_class, str domain, AddEntitiesCallback async_add_entities, VolSchemaType discovery_schema, VolSchemaType platform_schema_modern, dict[str, type[MqttEntity]]|None schema_class_mapping=None)