Home Assistant Unofficial Reference 2024.12.1
select.py
Go to the documentation of this file.
1 """Configure select in a device through MQTT topic."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 import logging
7 
8 import voluptuous as vol
9 
10 from homeassistant.components import select
11 from homeassistant.components.select import SelectEntity
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.const import CONF_NAME, CONF_OPTIMISTIC, CONF_VALUE_TEMPLATE
14 from homeassistant.core import HomeAssistant, callback
15 from homeassistant.helpers import config_validation as cv
16 from homeassistant.helpers.entity_platform import AddEntitiesCallback
17 from homeassistant.helpers.restore_state import RestoreEntity
18 from homeassistant.helpers.service_info.mqtt import ReceivePayloadType
19 from homeassistant.helpers.typing import ConfigType, VolSchemaType
20 
21 from . import subscription
22 from .config import MQTT_RW_SCHEMA
23 from .const import (
24  CONF_COMMAND_TEMPLATE,
25  CONF_COMMAND_TOPIC,
26  CONF_OPTIONS,
27  CONF_STATE_TOPIC,
28 )
29 from .entity import MqttEntity, async_setup_entity_entry_helper
30 from .models import (
31  MqttCommandTemplate,
32  MqttValueTemplate,
33  PublishPayloadType,
34  ReceiveMessage,
35 )
36 from .schemas import MQTT_ENTITY_COMMON_SCHEMA
37 
38 _LOGGER = logging.getLogger(__name__)
39 
40 PARALLEL_UPDATES = 0
41 
42 DEFAULT_NAME = "MQTT Select"
43 
44 MQTT_SELECT_ATTRIBUTES_BLOCKED = frozenset(
45  {
46  select.ATTR_OPTIONS,
47  }
48 )
49 
50 
51 PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend(
52  {
53  vol.Optional(CONF_COMMAND_TEMPLATE): cv.template,
54  vol.Optional(CONF_NAME): vol.Any(cv.string, None),
55  vol.Required(CONF_OPTIONS): cv.ensure_list,
56  vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
57  },
58 ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema)
59 
60 DISCOVERY_SCHEMA = vol.All(PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA))
61 
62 
64  hass: HomeAssistant,
65  config_entry: ConfigEntry,
66  async_add_entities: AddEntitiesCallback,
67 ) -> None:
68  """Set up MQTT select through YAML and through MQTT discovery."""
70  hass,
71  config_entry,
72  MqttSelect,
73  select.DOMAIN,
74  async_add_entities,
75  DISCOVERY_SCHEMA,
76  PLATFORM_SCHEMA_MODERN,
77  )
78 
79 
81  """representation of an MQTT select."""
82 
83  _attr_current_option: str | None = None
84  _default_name = DEFAULT_NAME
85  _entity_id_format = select.ENTITY_ID_FORMAT
86  _attributes_extra_blocked = MQTT_SELECT_ATTRIBUTES_BLOCKED
87  _command_template: Callable[[PublishPayloadType], PublishPayloadType]
88  _value_template: Callable[[ReceivePayloadType], ReceivePayloadType]
89  _optimistic: bool = False
90 
91  @staticmethod
92  def config_schema() -> VolSchemaType:
93  """Return the config schema."""
94  return DISCOVERY_SCHEMA
95 
96  def _setup_from_config(self, config: ConfigType) -> None:
97  """(Re)Setup the entity."""
98  self._attr_assumed_state_attr_assumed_state = config[CONF_OPTIMISTIC]
99  self._attr_options_attr_options = config[CONF_OPTIONS]
100 
101  self._command_template_command_template = MqttCommandTemplate(
102  config.get(CONF_COMMAND_TEMPLATE),
103  entity=self,
104  ).async_render
105  self._value_template_value_template = MqttValueTemplate(
106  config.get(CONF_VALUE_TEMPLATE), entity=self
107  ).async_render_with_possible_json_value
108 
109  @callback
110  def _message_received(self, msg: ReceiveMessage) -> None:
111  """Handle new MQTT messages."""
112  payload = str(self._value_template_value_template(msg.payload))
113  if not payload.strip(): # No output from template, ignore
114  _LOGGER.debug(
115  "Ignoring empty payload '%s' after rendering for topic %s",
116  payload,
117  msg.topic,
118  )
119  return
120  if payload.lower() == "none":
121  self._attr_current_option_attr_current_option = None
122  return
123 
124  if payload not in self.optionsoptions:
125  _LOGGER.error(
126  "Invalid option for %s: '%s' (valid options: %s)",
127  self.entity_identity_id,
128  payload,
129  self.optionsoptions,
130  )
131  return
132  self._attr_current_option_attr_current_option = payload
133 
134  @callback
135  def _prepare_subscribe_topics(self) -> None:
136  """(Re)Subscribe to topics."""
137  if not self.add_subscriptionadd_subscription(
138  CONF_STATE_TOPIC, self._message_received_message_received, {"_attr_current_option"}
139  ):
140  # Force into optimistic mode.
141  self._attr_assumed_state_attr_assumed_state = True
142  return
143 
144  async def _subscribe_topics(self) -> None:
145  """(Re)Subscribe to topics."""
146  subscription.async_subscribe_topics_internal(self.hasshasshass, self._sub_state_sub_state)
147 
148  if self._attr_assumed_state_attr_assumed_state and (
149  last_state := await self.async_get_last_stateasync_get_last_state()
150  ):
151  self._attr_current_option_attr_current_option = last_state.state
152 
153  async def async_select_option(self, option: str) -> None:
154  """Update the current value."""
155  payload = self._command_template_command_template(option)
156  if self._attr_assumed_state_attr_assumed_state:
157  self._attr_current_option_attr_current_option = option
158  self.async_write_ha_stateasync_write_ha_state()
159  await self.async_publish_with_configasync_publish_with_config(self._config_config[CONF_COMMAND_TOPIC], payload)
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 _message_received(self, ReceiveMessage msg)
Definition: select.py:110
None _setup_from_config(self, ConfigType config)
Definition: select.py:96
None async_select_option(self, str option)
Definition: select.py:153
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: select.py:67