Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Support for MQTT switches."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from typing import Any
7 
8 import voluptuous as vol
9 
10 from homeassistant.components import switch
11 from homeassistant.components.switch import DEVICE_CLASSES_SCHEMA, SwitchEntity
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import (
14  CONF_DEVICE_CLASS,
15  CONF_NAME,
16  CONF_OPTIMISTIC,
17  CONF_PAYLOAD_OFF,
18  CONF_PAYLOAD_ON,
19  CONF_VALUE_TEMPLATE,
20  STATE_ON,
21 )
22 from homeassistant.core import HomeAssistant, callback
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.restore_state import RestoreEntity
26 from homeassistant.helpers.service_info.mqtt import ReceivePayloadType
27 from homeassistant.helpers.typing import ConfigType
28 
29 from . import subscription
30 from .config import MQTT_RW_SCHEMA
31 from .const import (
32  CONF_COMMAND_TEMPLATE,
33  CONF_COMMAND_TOPIC,
34  CONF_STATE_TOPIC,
35  PAYLOAD_NONE,
36 )
37 from .entity import MqttEntity, async_setup_entity_entry_helper
38 from .models import (
39  MqttCommandTemplate,
40  MqttValueTemplate,
41  PublishPayloadType,
42  ReceiveMessage,
43 )
44 from .schemas import MQTT_ENTITY_COMMON_SCHEMA
45 
46 PARALLEL_UPDATES = 0
47 
48 DEFAULT_NAME = "MQTT Switch"
49 DEFAULT_PAYLOAD_ON = "ON"
50 DEFAULT_PAYLOAD_OFF = "OFF"
51 CONF_STATE_ON = "state_on"
52 CONF_STATE_OFF = "state_off"
53 
54 PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend(
55  {
56  vol.Optional(CONF_COMMAND_TEMPLATE): cv.template,
57  vol.Optional(CONF_NAME): vol.Any(cv.string, None),
58  vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
59  vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
60  vol.Optional(CONF_STATE_OFF): cv.string,
61  vol.Optional(CONF_STATE_ON): cv.string,
62  vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
63  vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None),
64  }
65 ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
66 
67 DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)
68 
69 
71  hass: HomeAssistant,
72  config_entry: ConfigEntry,
73  async_add_entities: AddEntitiesCallback,
74 ) -> None:
75  """Set up MQTT switch through YAML and through MQTT discovery."""
77  hass,
78  config_entry,
79  MqttSwitch,
80  switch.DOMAIN,
81  async_add_entities,
82  DISCOVERY_SCHEMA,
83  PLATFORM_SCHEMA_MODERN,
84  )
85 
86 
88  """Representation of a switch that can be toggled using MQTT."""
89 
90  _default_name = DEFAULT_NAME
91  _entity_id_format = switch.ENTITY_ID_FORMAT
92 
93  _optimistic: bool
94  _is_on_map: dict[str | bytes, bool | None]
95  _command_template: Callable[[PublishPayloadType], PublishPayloadType]
96  _value_template: Callable[[ReceivePayloadType], ReceivePayloadType]
97 
98  @staticmethod
99  def config_schema() -> vol.Schema:
100  """Return the config schema."""
101  return DISCOVERY_SCHEMA
102 
103  def _setup_from_config(self, config: ConfigType) -> None:
104  """(Re)Setup the entity."""
105  self._attr_device_class_attr_device_class = config.get(CONF_DEVICE_CLASS)
106  state_on: str | None = config.get(CONF_STATE_ON)
107  state_off: str | None = config.get(CONF_STATE_OFF)
108  self._is_on_map_is_on_map = {
109  state_on if state_on else config[CONF_PAYLOAD_ON]: True,
110  state_off if state_off else config[CONF_PAYLOAD_OFF]: False,
111  PAYLOAD_NONE: None,
112  }
113  self._optimistic_optimistic = (
114  config[CONF_OPTIMISTIC] or config.get(CONF_STATE_TOPIC) is None
115  )
116  self._attr_assumed_state_attr_assumed_state = bool(self._optimistic_optimistic)
117  self._command_template_command_template = MqttCommandTemplate(
118  config.get(CONF_COMMAND_TEMPLATE), entity=self
119  ).async_render
120  self._value_template_value_template = MqttValueTemplate(
121  config.get(CONF_VALUE_TEMPLATE), entity=self
122  ).async_render_with_possible_json_value
123 
124  @callback
125  def _state_message_received(self, msg: ReceiveMessage) -> None:
126  """Handle new MQTT state messages."""
127  if (payload := self._value_template_value_template(msg.payload)) in self._is_on_map_is_on_map:
128  self._attr_is_on_attr_is_on = self._is_on_map_is_on_map[payload]
129 
130  @callback
131  def _prepare_subscribe_topics(self) -> None:
132  """(Re)Subscribe to topics."""
133  if not self.add_subscriptionadd_subscription(
134  CONF_STATE_TOPIC, self._state_message_received_state_message_received, {"_attr_is_on"}
135  ):
136  # Force into optimistic mode.
137  self._optimistic_optimistic = True
138  return
139 
140  async def _subscribe_topics(self) -> None:
141  """(Re)Subscribe to topics."""
142  subscription.async_subscribe_topics_internal(self.hasshasshass, self._sub_state_sub_state)
143 
144  if self._optimistic_optimistic and (last_state := await self.async_get_last_stateasync_get_last_state()):
145  self._attr_is_on_attr_is_on = last_state.state == STATE_ON
146 
147  async def async_turn_on(self, **kwargs: Any) -> None:
148  """Turn the device on.
149 
150  This method is a coroutine.
151  """
152  payload = self._command_template_command_template(self._config_config[CONF_PAYLOAD_ON])
153  await self.async_publish_with_configasync_publish_with_config(self._config_config[CONF_COMMAND_TOPIC], payload)
154  if self._optimistic_optimistic:
155  # Optimistically assume that switch has changed state.
156  self._attr_is_on_attr_is_on = True
157  self.async_write_ha_stateasync_write_ha_state()
158 
159  async def async_turn_off(self, **kwargs: Any) -> None:
160  """Turn the device off.
161 
162  This method is a coroutine.
163  """
164  payload = self._command_template_command_template(self._config_config[CONF_PAYLOAD_OFF])
165  await self.async_publish_with_configasync_publish_with_config(self._config_config[CONF_COMMAND_TOPIC], payload)
166  if self._optimistic_optimistic:
167  # Optimistically assume that switch has changed state.
168  self._attr_is_on_attr_is_on = False
169  self.async_write_ha_stateasync_write_ha_state()
None async_publish_with_config(self, str topic, PublishPayloadType payload)
Definition: entity.py:1377
bool add_subscription(self, str state_topic_config_key, Callable[[ReceiveMessage], None] msg_callback, set[str]|None tracked_attributes, bool disable_encoding=False)
Definition: entity.py:1484
None async_turn_on(self, **Any kwargs)
Definition: switch.py:147
None _state_message_received(self, ReceiveMessage msg)
Definition: switch.py:125
None async_turn_off(self, **Any kwargs)
Definition: switch.py:159
None _setup_from_config(self, ConfigType config)
Definition: switch.py:103
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)
Definition: entity.py:245
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: switch.py:74