Home Assistant Unofficial Reference 2024.12.1
number.py
Go to the documentation of this file.
1 """Number for Shelly."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import TYPE_CHECKING, Any, Final, cast
8 
9 from aioshelly.block_device import Block
10 from aioshelly.const import RPC_GENERATIONS
11 from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
12 
14  DOMAIN as NUMBER_PLATFORM,
15  NumberEntity,
16  NumberEntityDescription,
17  NumberExtraStoredData,
18  NumberMode,
19  RestoreNumber,
20 )
21 from homeassistant.const import PERCENTAGE, EntityCategory
22 from homeassistant.core import HomeAssistant
23 from homeassistant.exceptions import HomeAssistantError
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.entity_registry import RegistryEntry
26 
27 from .const import CONF_SLEEP_PERIOD, LOGGER, VIRTUAL_NUMBER_MODE_MAP
28 from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
29 from .entity import (
30  BlockEntityDescription,
31  RpcEntityDescription,
32  ShellyRpcAttributeEntity,
33  ShellySleepingBlockAttributeEntity,
34  async_setup_entry_attribute_entities,
35  async_setup_entry_rpc,
36 )
37 from .utils import (
38  async_remove_orphaned_entities,
39  get_device_entry_gen,
40  get_virtual_component_ids,
41 )
42 
43 
44 @dataclass(frozen=True, kw_only=True)
46  """Class to describe a BLOCK sensor."""
47 
48  rest_path: str = ""
49  rest_arg: str = ""
50 
51 
52 @dataclass(frozen=True, kw_only=True)
54  """Class to describe a RPC number entity."""
55 
56  max_fn: Callable[[dict], float] | None = None
57  min_fn: Callable[[dict], float] | None = None
58  step_fn: Callable[[dict], float] | None = None
59  mode_fn: Callable[[dict], NumberMode] | None = None
60 
61 
62 NUMBERS: dict[tuple[str, str], BlockNumberDescription] = {
63  ("device", "valvePos"): BlockNumberDescription(
64  key="device|valvepos",
65  translation_key="valve_position",
66  name="Valve position",
67  native_unit_of_measurement=PERCENTAGE,
68  available=lambda block: cast(int, block.valveError) != 1,
69  entity_category=EntityCategory.CONFIG,
70  native_min_value=0,
71  native_max_value=100,
72  native_step=1,
73  mode=NumberMode.SLIDER,
74  rest_path="thermostat/0",
75  rest_arg="pos",
76  ),
77 }
78 
79 
80 RPC_NUMBERS: Final = {
81  "number": RpcNumberDescription(
82  key="number",
83  sub_key="value",
84  has_entity_name=True,
85  max_fn=lambda config: config["max"],
86  min_fn=lambda config: config["min"],
87  mode_fn=lambda config: VIRTUAL_NUMBER_MODE_MAP.get(
88  config["meta"]["ui"]["view"], NumberMode.BOX
89  ),
90  step_fn=lambda config: config["meta"]["ui"]["step"],
91  # If the unit is not set, the device sends an empty string
92  unit=lambda config: config["meta"]["ui"]["unit"]
93  if config["meta"]["ui"]["unit"]
94  else None,
95  ),
96 }
97 
98 
100  hass: HomeAssistant,
101  config_entry: ShellyConfigEntry,
102  async_add_entities: AddEntitiesCallback,
103 ) -> None:
104  """Set up numbers for device."""
105  if get_device_entry_gen(config_entry) in RPC_GENERATIONS:
106  coordinator = config_entry.runtime_data.rpc
107  assert coordinator
108 
110  hass, config_entry, async_add_entities, RPC_NUMBERS, RpcNumber
111  )
112 
113  # the user can remove virtual components from the device configuration, so
114  # we need to remove orphaned entities
115  virtual_number_ids = get_virtual_component_ids(
116  coordinator.device.config, NUMBER_PLATFORM
117  )
119  hass,
120  config_entry.entry_id,
121  coordinator.mac,
122  NUMBER_PLATFORM,
123  virtual_number_ids,
124  "number",
125  )
126  return
127 
128  if config_entry.data[CONF_SLEEP_PERIOD]:
130  hass,
131  config_entry,
132  async_add_entities,
133  NUMBERS,
134  BlockSleepingNumber,
135  )
136 
137 
139  """Represent a block sleeping number."""
140 
141  entity_description: BlockNumberDescription
142 
143  def __init__(
144  self,
145  coordinator: ShellyBlockCoordinator,
146  block: Block | None,
147  attribute: str,
148  description: BlockNumberDescription,
149  entry: RegistryEntry | None = None,
150  ) -> None:
151  """Initialize the sleeping sensor."""
152  self.restored_datarestored_data: NumberExtraStoredData | None = None
153  super().__init__(coordinator, block, attribute, description, entry)
154 
155  async def async_added_to_hass(self) -> None:
156  """Handle entity which will be added."""
157  await super().async_added_to_hass()
158  self.restored_datarestored_data = await self.async_get_last_number_dataasync_get_last_number_data()
159 
160  @property
161  def native_value(self) -> float | None:
162  """Return value of number."""
163  if self.blockblockblock is not None:
164  return cast(float, self.attribute_valueattribute_value)
165 
166  if self.restored_datarestored_data is None:
167  return None
168 
169  return cast(float, self.restored_datarestored_data.native_value)
170 
171  async def async_set_native_value(self, value: float) -> None:
172  """Set value."""
173  # Example for Shelly Valve: http://192.168.188.187/thermostat/0?pos=13.0
174  await self._set_state_full_path_set_state_full_path(
175  self.entity_descriptionentity_descriptionentity_description.rest_path,
176  {self.entity_descriptionentity_descriptionentity_description.rest_arg: value},
177  )
178  self.async_write_ha_stateasync_write_ha_state()
179 
180  async def _set_state_full_path(self, path: str, params: Any) -> Any:
181  """Set block state (HTTP request)."""
182  LOGGER.debug("Setting state for entity %s, state: %s", self.namename, params)
183  try:
184  return await self.coordinatorcoordinator.device.http_request("get", path, params)
185  except DeviceConnectionError as err:
186  self.coordinatorcoordinator.last_update_success = False
187  raise HomeAssistantError(
188  f"Setting state for entity {self.name} failed, state: {params}, error:"
189  f" {err!r}"
190  ) from err
191  except InvalidAuthError:
193 
194 
196  """Represent a RPC number entity."""
197 
198  entity_description: RpcNumberDescription
199 
200  def __init__(
201  self,
202  coordinator: ShellyRpcCoordinator,
203  key: str,
204  attribute: str,
205  description: RpcNumberDescription,
206  ) -> None:
207  """Initialize sensor."""
208  super().__init__(coordinator, key, attribute, description)
209 
210  if description.max_fn is not None:
211  self._attr_native_max_value_attr_native_max_value = description.max_fn(
212  coordinator.device.config[key]
213  )
214  if description.min_fn is not None:
215  self._attr_native_min_value_attr_native_min_value = description.min_fn(
216  coordinator.device.config[key]
217  )
218  if description.step_fn is not None:
219  self._attr_native_step_attr_native_step = description.step_fn(coordinator.device.config[key])
220  if description.mode_fn is not None:
221  self._attr_mode_attr_mode = description.mode_fn(coordinator.device.config[key])
222 
223  @property
224  def native_value(self) -> float | None:
225  """Return value of number."""
226  if TYPE_CHECKING:
227  assert isinstance(self.attribute_valueattribute_value, float | None)
228 
229  return self.attribute_valueattribute_value
230 
231  async def async_set_native_value(self, value: float) -> None:
232  """Change the value."""
233  await self.call_rpccall_rpc("Number.Set", {"id": self._id_id, "value": value})
NumberExtraStoredData|None async_get_last_number_data(self)
Definition: __init__.py:549
Any call_rpc(self, str method, Any params)
Definition: entity.py:384
Any _set_state_full_path(self, str path, Any params)
Definition: number.py:180
None __init__(self, ShellyBlockCoordinator coordinator, Block|None block, str attribute, BlockNumberDescription description, RegistryEntry|None entry=None)
Definition: number.py:150
None async_set_native_value(self, float value)
Definition: number.py:231
None __init__(self, ShellyRpcCoordinator coordinator, str key, str attribute, RpcNumberDescription description)
Definition: number.py:206
str|UndefinedType|None name(self)
Definition: entity.py:738
None async_setup_entry_rpc(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[str, RpcEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:142
None async_setup_entry_attribute_entities(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[tuple[str, str], BlockEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:39
None async_setup_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: number.py:103
list[str] get_virtual_component_ids(dict[str, Any] config, str platform)
Definition: utils.py:528
None async_remove_orphaned_entities(HomeAssistant hass, str config_entry_id, str mac, str platform, Iterable[str] keys, str|None key_suffix=None)
Definition: utils.py:555
int get_device_entry_gen(ConfigEntry entry)
Definition: utils.py:353