Home Assistant Unofficial Reference 2024.12.1
binary_sensor.py
Go to the documentation of this file.
1 """Support for Axis binary sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime, timedelta
8 
9 from axis.interfaces.applications.fence_guard import FenceGuardHandler
10 from axis.interfaces.applications.loitering_guard import LoiteringGuardHandler
11 from axis.interfaces.applications.motion_guard import MotionGuardHandler
12 from axis.interfaces.applications.vmd4 import Vmd4Handler
13 from axis.models.event import Event, EventTopic
14 
16  BinarySensorDeviceClass,
17  BinarySensorEntity,
18  BinarySensorEntityDescription,
19 )
20 from homeassistant.core import HomeAssistant, callback
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 from homeassistant.helpers.event import async_call_later
23 
24 from . import AxisConfigEntry
25 from .entity import AxisEventDescription, AxisEventEntity
26 from .hub import AxisHub
27 
28 
29 @dataclass(frozen=True, kw_only=True)
31  """Axis binary sensor entity description."""
32 
33 
34 @callback
35 def event_id_is_int(event_id: str) -> bool:
36  """Make sure event ID is int."""
37  try:
38  _ = int(event_id)
39  except ValueError:
40  return False
41  return True
42 
43 
44 @callback
45 def guard_suite_supported_fn(hub: AxisHub, event: Event) -> bool:
46  """Validate event ID is int."""
47  _, _, profile_id = event.id.partition("Profile")
48  return event_id_is_int(profile_id)
49 
50 
51 @callback
52 def object_analytics_supported_fn(hub: AxisHub, event: Event) -> bool:
53  """Validate event ID is int."""
54  _, _, profile_id = event.id.partition("Scenario")
55  return event_id_is_int(profile_id)
56 
57 
58 @callback
60  handler: FenceGuardHandler
61  | LoiteringGuardHandler
62  | MotionGuardHandler
63  | Vmd4Handler,
64  event: Event,
65  event_type: str,
66 ) -> str:
67  """Get guard suite item name."""
68  if handler.initialized and (profiles := handler["0"].profiles):
69  for profile_id, profile in profiles.items():
70  camera_id = profile.camera
71  if event.id == f"Camera{camera_id}Profile{profile_id}":
72  return f"{event_type} {profile.name}"
73  return ""
74 
75 
76 @callback
77 def fence_guard_name_fn(hub: AxisHub, event: Event) -> str:
78  """Fence guard name."""
79  return guard_suite_name_fn(hub.api.vapix.fence_guard, event, "Fence Guard")
80 
81 
82 @callback
83 def loitering_guard_name_fn(hub: AxisHub, event: Event) -> str:
84  """Loitering guard name."""
85  return guard_suite_name_fn(hub.api.vapix.loitering_guard, event, "Loitering Guard")
86 
87 
88 @callback
89 def motion_guard_name_fn(hub: AxisHub, event: Event) -> str:
90  """Motion guard name."""
91  return guard_suite_name_fn(hub.api.vapix.motion_guard, event, "Motion Guard")
92 
93 
94 @callback
95 def motion_detection_4_name_fn(hub: AxisHub, event: Event) -> str:
96  """Motion detection 4 name."""
97  return guard_suite_name_fn(hub.api.vapix.vmd4, event, "VMD4")
98 
99 
100 @callback
101 def object_analytics_name_fn(hub: AxisHub, event: Event) -> str:
102  """Get object analytics name."""
103  if hub.api.vapix.object_analytics.initialized and (
104  scenarios := hub.api.vapix.object_analytics["0"].scenarios
105  ):
106  for scenario_id, scenario in scenarios.items():
107  device_id = scenario.devices[0]["id"]
108  if event.id == f"Device{device_id}Scenario{scenario_id}":
109  return f"Object Analytics {scenario.name}"
110  return ""
111 
112 
113 ENTITY_DESCRIPTIONS = (
115  key="Input port state",
116  device_class=BinarySensorDeviceClass.CONNECTIVITY,
117  event_topic=(EventTopic.PORT_INPUT, EventTopic.PORT_SUPERVISED_INPUT),
118  name_fn=lambda hub, event: hub.api.vapix.ports[event.id].name,
119  supported_fn=lambda hub, event: event_id_is_int(event.id),
120  ),
122  key="Day/Night vision state",
123  device_class=BinarySensorDeviceClass.LIGHT,
124  event_topic=EventTopic.DAY_NIGHT_VISION,
125  ),
127  key="Sound trigger state",
128  device_class=BinarySensorDeviceClass.SOUND,
129  event_topic=EventTopic.SOUND_TRIGGER_LEVEL,
130  ),
132  key="Motion sensors state",
133  device_class=BinarySensorDeviceClass.MOTION,
134  event_topic=(
135  EventTopic.PIR,
136  EventTopic.MOTION_DETECTION,
137  EventTopic.MOTION_DETECTION_3,
138  ),
139  ),
141  key="Motion detection 4 state",
142  device_class=BinarySensorDeviceClass.MOTION,
143  event_topic=EventTopic.MOTION_DETECTION_4,
144  name_fn=motion_detection_4_name_fn,
145  supported_fn=guard_suite_supported_fn,
146  ),
148  key="Fence guard state",
149  device_class=BinarySensorDeviceClass.MOTION,
150  event_topic=EventTopic.FENCE_GUARD,
151  name_fn=fence_guard_name_fn,
152  supported_fn=guard_suite_supported_fn,
153  ),
155  key="Loitering guard state",
156  device_class=BinarySensorDeviceClass.MOTION,
157  event_topic=EventTopic.LOITERING_GUARD,
158  name_fn=loitering_guard_name_fn,
159  supported_fn=guard_suite_supported_fn,
160  ),
162  key="Motion guard state",
163  device_class=BinarySensorDeviceClass.MOTION,
164  event_topic=EventTopic.MOTION_GUARD,
165  name_fn=motion_guard_name_fn,
166  supported_fn=guard_suite_supported_fn,
167  ),
169  key="Object analytics state",
170  device_class=BinarySensorDeviceClass.MOTION,
171  event_topic=EventTopic.OBJECT_ANALYTICS,
172  name_fn=object_analytics_name_fn,
173  supported_fn=object_analytics_supported_fn,
174  ),
175 )
176 
177 
179  hass: HomeAssistant,
180  config_entry: AxisConfigEntry,
181  async_add_entities: AddEntitiesCallback,
182 ) -> None:
183  """Set up a Axis binary sensor."""
184  config_entry.runtime_data.entity_loader.register_platform(
185  async_add_entities, AxisBinarySensor, ENTITY_DESCRIPTIONS
186  )
187 
188 
190  """Representation of a binary Axis event."""
191 
192  entity_description: AxisBinarySensorDescription
193 
194  def __init__(
195  self, hub: AxisHub, description: AxisBinarySensorDescription, event: Event
196  ) -> None:
197  """Initialize the Axis binary sensor."""
198  super().__init__(hub, description, event)
199 
200  self._attr_is_on_attr_is_on = event.is_tripped
201  self.cancel_scheduled_updatecancel_scheduled_update: Callable[[], None] | None = None
202 
203  @callback
204  def async_event_callback(self, event: Event) -> None:
205  """Update the sensor's state, if needed."""
206  self._attr_is_on_attr_is_on = event.is_tripped
207 
208  @callback
209  def scheduled_update(now: datetime) -> None:
210  """Timer callback for sensor update."""
211  self.cancel_scheduled_updatecancel_scheduled_update = None
212  self.async_write_ha_stateasync_write_ha_state()
213 
214  if self.cancel_scheduled_updatecancel_scheduled_update is not None:
215  self.cancel_scheduled_updatecancel_scheduled_update()
216  self.cancel_scheduled_updatecancel_scheduled_update = None
217 
218  if self.is_on or self.hubhub.config.trigger_time == 0:
219  self.async_write_ha_stateasync_write_ha_state()
220  return
221 
222  self.cancel_scheduled_updatecancel_scheduled_update = async_call_later(
223  self.hasshass,
224  timedelta(seconds=self.hubhub.config.trigger_time),
225  scheduled_update,
226  )
None __init__(self, AxisHub hub, AxisBinarySensorDescription description, Event event)
str fence_guard_name_fn(AxisHub hub, Event event)
bool object_analytics_supported_fn(AxisHub hub, Event event)
None async_setup_entry(HomeAssistant hass, AxisConfigEntry config_entry, AddEntitiesCallback async_add_entities)
str object_analytics_name_fn(AxisHub hub, Event event)
bool guard_suite_supported_fn(AxisHub hub, Event event)
str guard_suite_name_fn(FenceGuardHandler|LoiteringGuardHandler|MotionGuardHandler|Vmd4Handler handler, Event event, str event_type)
str motion_detection_4_name_fn(AxisHub hub, Event event)
str motion_guard_name_fn(AxisHub hub, Event event)
str loitering_guard_name_fn(AxisHub hub, Event event)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)
Definition: event.py:1597