Home Assistant Unofficial Reference 2024.12.1
binary_sensor.py
Go to the documentation of this file.
1 """Support for Modbus Coil and Discrete Input sensors."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime
6 import logging
7 from typing import Any
8 
9 from homeassistant.components.binary_sensor import BinarySensorEntity
10 from homeassistant.const import (
11  CONF_BINARY_SENSORS,
12  CONF_DEVICE_CLASS,
13  CONF_NAME,
14  CONF_UNIQUE_ID,
15  STATE_ON,
16 )
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 from homeassistant.helpers.restore_state import RestoreEntity
20 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
22  CoordinatorEntity,
23  DataUpdateCoordinator,
24 )
25 
26 from . import get_hub
27 from .const import (
28  CALL_TYPE_COIL,
29  CALL_TYPE_DISCRETE,
30  CONF_SLAVE_COUNT,
31  CONF_VIRTUAL_COUNT,
32 )
33 from .entity import BasePlatform
34 from .modbus import ModbusHub
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 PARALLEL_UPDATES = 1
39 
40 
42  hass: HomeAssistant,
43  config: ConfigType,
44  async_add_entities: AddEntitiesCallback,
45  discovery_info: DiscoveryInfoType | None = None,
46 ) -> None:
47  """Set up the Modbus binary sensors."""
48 
49  if discovery_info is None:
50  return
51 
52  sensors: list[ModbusBinarySensor | SlaveSensor] = []
53  hub = get_hub(hass, discovery_info[CONF_NAME])
54  for entry in discovery_info[CONF_BINARY_SENSORS]:
55  slave_count = entry.get(CONF_SLAVE_COUNT, None) or entry.get(
56  CONF_VIRTUAL_COUNT, 0
57  )
58  sensor = ModbusBinarySensor(hass, hub, entry, slave_count)
59  if slave_count > 0:
60  sensors.extend(await sensor.async_setup_slaves(hass, slave_count, entry))
61  sensors.append(sensor)
62  async_add_entities(sensors)
63 
64 
66  """Modbus binary sensor."""
67 
68  def __init__(
69  self,
70  hass: HomeAssistant,
71  hub: ModbusHub,
72  entry: dict[str, Any],
73  slave_count: int,
74  ) -> None:
75  """Initialize the Modbus binary sensor."""
76  self._count_count = slave_count + 1
77  self._coordinator_coordinator: DataUpdateCoordinator[list[int] | None] | None = None
78  self._result_result: list[int] = []
79  super().__init__(hass, hub, entry)
80 
81  async def async_setup_slaves(
82  self, hass: HomeAssistant, slave_count: int, entry: dict[str, Any]
83  ) -> list[SlaveSensor]:
84  """Add slaves as needed (1 read for multiple sensors)."""
85 
86  # Add a dataCoordinator for each sensor that have slaves
87  # this ensures that idx = bit position of value in result
88  # polling is done with the base class
89  name = self._attr_name_attr_name if self._attr_name_attr_name else "modbus_sensor"
90  self._coordinator_coordinator = DataUpdateCoordinator(
91  hass,
92  _LOGGER,
93  config_entry=None,
94  name=name,
95  )
96 
97  return [
98  SlaveSensor(self._coordinator_coordinator, idx, entry) for idx in range(slave_count)
99  ]
100 
101  async def async_added_to_hass(self) -> None:
102  """Handle entity which will be added."""
103  await self.async_base_added_to_hassasync_base_added_to_hass()
104  if state := await self.async_get_last_stateasync_get_last_state():
105  self._attr_is_on_attr_is_on = state.state == STATE_ON
106 
107  async def async_update(self, now: datetime | None = None) -> None:
108  """Update the state of the sensor."""
109 
110  # do not allow multiple active calls to the same platform
111  if self._call_active_call_active_call_active:
112  return
113  self._call_active_call_active_call_active = True
114  result = await self._hub_hub.async_pb_call(
115  self._slave_slave, self._address_address, self._count_count, self._input_type_input_type
116  )
117  self._call_active_call_active_call_active = False
118  if result is None:
119  self._attr_available_attr_available_attr_available = False
120  self._result_result = []
121  else:
122  self._attr_available_attr_available_attr_available = True
123  if self._input_type_input_type in (CALL_TYPE_COIL, CALL_TYPE_DISCRETE):
124  self._result_result = result.bits
125  else:
126  self._result_result = result.registers
127  self._attr_is_on_attr_is_on = bool(self._result_result[0] & 1)
128 
129  self.async_write_ha_stateasync_write_ha_state()
130  if self._coordinator_coordinator:
131  self._coordinator_coordinator.async_set_updated_data(self._result_result)
132 
133 
135  CoordinatorEntity[DataUpdateCoordinator[list[int] | None]],
136  RestoreEntity,
137  BinarySensorEntity,
138 ):
139  """Modbus slave binary sensor."""
140 
141  def __init__(
142  self,
143  coordinator: DataUpdateCoordinator[list[int] | None],
144  idx: int,
145  entry: dict[str, Any],
146  ) -> None:
147  """Initialize the Modbus binary sensor."""
148  idx += 1
149  self._attr_name_attr_name = f"{entry[CONF_NAME]} {idx}"
150  self._attr_device_class_attr_device_class = entry.get(CONF_DEVICE_CLASS)
151  self._attr_unique_id_attr_unique_id = entry.get(CONF_UNIQUE_ID)
152  if self._attr_unique_id_attr_unique_id:
153  self._attr_unique_id_attr_unique_id = f"{self._attr_unique_id}_{idx}"
154  self._attr_available_attr_available = False
155  self._result_inx_result_inx = idx
156  super().__init__(coordinator)
157 
158  async def async_added_to_hass(self) -> None:
159  """Handle entity which will be added."""
160  if state := await self.async_get_last_stateasync_get_last_state():
161  self._attr_is_on_attr_is_on = state.state == STATE_ON
162  self.async_write_ha_stateasync_write_ha_state()
163  await super().async_added_to_hass()
164 
165  @callback
166  def _handle_coordinator_update(self) -> None:
167  """Handle updated data from the coordinator."""
168  result = self.coordinator.data
169  self._attr_is_on_attr_is_on = bool(result[self._result_inx_result_inx] & 1) if result else None
None __init__(self, HomeAssistant hass, ModbusHub hub, dict[str, Any] entry, int slave_count)
list[SlaveSensor] async_setup_slaves(self, HomeAssistant hass, int slave_count, dict[str, Any] entry)
None __init__(self, DataUpdateCoordinator[list[int]|None] coordinator, int idx, dict[str, Any] entry)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
ModbusHub get_hub(HomeAssistant hass, str name)
Definition: __init__.py:445