Home Assistant Unofficial Reference 2024.12.1
select.py
Go to the documentation of this file.
1 """Support for AirGradient select entities."""
2 
3 from collections.abc import Awaitable, Callable
4 from dataclasses import dataclass
5 
6 from airgradient import AirGradientClient, Config
7 from airgradient.models import ConfigurationControl, LedBarMode, TemperatureUnit
8 
10  DOMAIN as SELECT_DOMAIN,
11  SelectEntity,
12  SelectEntityDescription,
13 )
14 from homeassistant.const import EntityCategory
15 from homeassistant.core import HomeAssistant, callback
16 from homeassistant.helpers import entity_registry as er
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 
19 from . import AirGradientConfigEntry
20 from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
21 from .coordinator import AirGradientCoordinator
22 from .entity import AirGradientEntity
23 
24 
25 @dataclass(frozen=True, kw_only=True)
27  """Describes AirGradient select entity."""
28 
29  value_fn: Callable[[Config], str | None]
30  set_value_fn: Callable[[AirGradientClient, str], Awaitable[None]]
31 
32 
33 CONFIG_CONTROL_ENTITY = AirGradientSelectEntityDescription(
34  key="configuration_control",
35  translation_key="configuration_control",
36  options=[ConfigurationControl.CLOUD.value, ConfigurationControl.LOCAL.value],
37  entity_category=EntityCategory.CONFIG,
38  value_fn=lambda config: (
39  config.configuration_control
40  if config.configuration_control is not ConfigurationControl.NOT_INITIALIZED
41  else None
42  ),
43  set_value_fn=lambda client, value: client.set_configuration_control(
44  ConfigurationControl(value)
45  ),
46 )
47 
48 DISPLAY_SELECT_TYPES: tuple[AirGradientSelectEntityDescription, ...] = (
50  key="display_temperature_unit",
51  translation_key="display_temperature_unit",
52  options=[x.value for x in TemperatureUnit],
53  entity_category=EntityCategory.CONFIG,
54  value_fn=lambda config: config.temperature_unit,
55  set_value_fn=lambda client, value: client.set_temperature_unit(
56  TemperatureUnit(value)
57  ),
58  ),
60  key="display_pm_standard",
61  translation_key="display_pm_standard",
62  options=list(PM_STANDARD_REVERSE),
63  entity_category=EntityCategory.CONFIG,
64  value_fn=lambda config: PM_STANDARD.get(config.pm_standard),
65  set_value_fn=lambda client, value: client.set_pm_standard(
66  PM_STANDARD_REVERSE[value]
67  ),
68  ),
69 )
70 
71 LED_BAR_ENTITIES: tuple[AirGradientSelectEntityDescription, ...] = (
73  key="led_bar_mode",
74  translation_key="led_bar_mode",
75  options=[x.value for x in LedBarMode],
76  entity_category=EntityCategory.CONFIG,
77  value_fn=lambda config: config.led_bar_mode,
78  set_value_fn=lambda client, value: client.set_led_bar_mode(LedBarMode(value)),
79  ),
80 )
81 
82 LEARNING_TIME_OFFSET_OPTIONS = [
83  "12",
84  "60",
85  "120",
86  "360",
87  "720",
88 ]
89 
90 ABC_DAYS = [
91  "1",
92  "8",
93  "30",
94  "90",
95  "180",
96  "0",
97 ]
98 
99 
100 def _get_value(value: int, values: list[str]) -> str | None:
101  str_value = str(value)
102  return str_value if str_value in values else None
103 
104 
105 CONTROL_ENTITIES: tuple[AirGradientSelectEntityDescription, ...] = (
107  key="nox_index_learning_time_offset",
108  translation_key="nox_index_learning_time_offset",
109  options=LEARNING_TIME_OFFSET_OPTIONS,
110  entity_category=EntityCategory.CONFIG,
111  value_fn=lambda config: _get_value(
112  config.nox_learning_offset, LEARNING_TIME_OFFSET_OPTIONS
113  ),
114  set_value_fn=lambda client, value: client.set_nox_learning_offset(int(value)),
115  ),
117  key="voc_index_learning_time_offset",
118  translation_key="voc_index_learning_time_offset",
119  options=LEARNING_TIME_OFFSET_OPTIONS,
120  entity_category=EntityCategory.CONFIG,
121  value_fn=lambda config: _get_value(
122  config.tvoc_learning_offset, LEARNING_TIME_OFFSET_OPTIONS
123  ),
124  set_value_fn=lambda client, value: client.set_tvoc_learning_offset(int(value)),
125  ),
127  key="co2_automatic_baseline_calibration",
128  translation_key="co2_automatic_baseline_calibration",
129  options=ABC_DAYS,
130  entity_category=EntityCategory.CONFIG,
131  value_fn=lambda config: _get_value(
132  config.co2_automatic_baseline_calibration_days, ABC_DAYS
133  ),
134  set_value_fn=lambda client,
135  value: client.set_co2_automatic_baseline_calibration(int(value)),
136  ),
137 )
138 
139 
141  hass: HomeAssistant,
142  entry: AirGradientConfigEntry,
143  async_add_entities: AddEntitiesCallback,
144 ) -> None:
145  """Set up AirGradient select entities based on a config entry."""
146 
147  coordinator = entry.runtime_data
148  model = coordinator.data.measures.model
149 
150  async_add_entities([AirGradientSelect(coordinator, CONFIG_CONTROL_ENTITY)])
151 
152  added_entities = False
153 
154  @callback
155  def _async_check_entities() -> None:
156  nonlocal added_entities
157 
158  if (
159  coordinator.data.config.configuration_control is ConfigurationControl.LOCAL
160  and not added_entities
161  ):
162  entities: list[AirGradientSelect] = [
163  AirGradientSelect(coordinator, description)
164  for description in CONTROL_ENTITIES
165  ]
166  if "I" in model:
167  entities.extend(
168  AirGradientSelect(coordinator, description)
169  for description in DISPLAY_SELECT_TYPES
170  )
171  if "L" in model:
172  entities.extend(
173  AirGradientSelect(coordinator, description)
174  for description in LED_BAR_ENTITIES
175  )
176 
177  async_add_entities(entities)
178  added_entities = True
179  elif (
180  coordinator.data.config.configuration_control
181  is not ConfigurationControl.LOCAL
182  and added_entities
183  ):
184  entity_registry = er.async_get(hass)
185  for entity_description in (
186  DISPLAY_SELECT_TYPES + LED_BAR_ENTITIES + CONTROL_ENTITIES
187  ):
188  unique_id = f"{coordinator.serial_number}-{entity_description.key}"
189  if entity_id := entity_registry.async_get_entity_id(
190  SELECT_DOMAIN, DOMAIN, unique_id
191  ):
192  entity_registry.async_remove(entity_id)
193  added_entities = False
194 
195  coordinator.async_add_listener(_async_check_entities)
196  _async_check_entities()
197 
198 
200  """Defines an AirGradient select entity."""
201 
202  entity_description: AirGradientSelectEntityDescription
203 
204  def __init__(
205  self,
206  coordinator: AirGradientCoordinator,
207  description: AirGradientSelectEntityDescription,
208  ) -> None:
209  """Initialize AirGradient select."""
210  super().__init__(coordinator)
211  self.entity_descriptionentity_description = description
212  self._attr_unique_id_attr_unique_id = f"{coordinator.serial_number}-{description.key}"
213 
214  @property
215  def current_option(self) -> str | None:
216  """Return the state of the select."""
217  return self.entity_descriptionentity_description.value_fn(self.coordinator.data.config)
218 
219  async def async_select_option(self, option: str) -> None:
220  """Change the selected option."""
221  await self.entity_descriptionentity_description.set_value_fn(self.coordinator.client, option)
222  await self.coordinator.async_request_refresh()
None __init__(self, AirGradientCoordinator coordinator, AirGradientSelectEntityDescription description)
Definition: select.py:208
None async_setup_entry(HomeAssistant hass, AirGradientConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: select.py:144
str|None _get_value(int value, list[str] values)
Definition: select.py:100