core/homeassistant/components/august/binary_sensor.py

189 lines
6.5 KiB
Python
Raw Normal View History

"""Support for August binary sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
2024-06-19 14:21:04 +00:00
from functools import partial
Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299) * Add doorsense sensor for August 3rd Gen Smart Lock Pro Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor. This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end. * Changed to use snjoetw provided code Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor. Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think. I modified his coding with: Fixes that were done to the August component and not part of the coding snjoetw provided. Added the debug logging improvement I had done in the code. Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same. * Reverted change from add_device to add_entities Missed an item when merging snjoetw's code with current. Fixed. * Updated call from add_devices to add_entities as well Updated the call from add_devices to add_entities. * Fixed permissions on files Fixed permissions on components/august.py and binary_snesor/august.py * Changed if/else to if/continue Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic. * Added available property for Door Sensor Added the available property for the Door Sensor and setting it to False if a status unknown is received. * Updated setting self._available Changed line for setting self._available to what Martin provided. Much more efficient to read. :-)
2018-10-19 07:37:02 +00:00
import logging
2024-07-07 13:39:58 +00:00
from yalexs.activity import Activity, ActivityType
from yalexs.doorbell import DoorbellDetail
from yalexs.lock import LockDetail, LockDoorStatus
from yalexs.manager.const import ACTIVITY_UPDATE_INTERVAL
from yalexs.util import update_lock_detail_from_activity
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later
2024-05-02 12:39:02 +00:00
from . import AugustConfigEntry, AugustData
from .entity import AugustDescriptionEntity
2024-07-07 13:39:58 +00:00
from .util import (
retrieve_ding_activity,
retrieve_doorbell_motion_activity,
retrieve_online_state,
retrieve_time_based_activity,
)
Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299) * Add doorsense sensor for August 3rd Gen Smart Lock Pro Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor. This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end. * Changed to use snjoetw provided code Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor. Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think. I modified his coding with: Fixes that were done to the August component and not part of the coding snjoetw provided. Added the debug logging improvement I had done in the code. Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same. * Reverted change from add_device to add_entities Missed an item when merging snjoetw's code with current. Fixed. * Updated call from add_devices to add_entities as well Updated the call from add_devices to add_entities. * Fixed permissions on files Fixed permissions on components/august.py and binary_snesor/august.py * Changed if/else to if/continue Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic. * Added available property for Door Sensor Added the available property for the Door Sensor and setting it to False if a status unknown is received. * Updated setting self._available Changed line for setting self._available to what Martin provided. Much more efficient to read. :-)
2018-10-19 07:37:02 +00:00
_LOGGER = logging.getLogger(__name__)
TIME_TO_RECHECK_DETECTION = timedelta(
seconds=ACTIVITY_UPDATE_INTERVAL.total_seconds() * 3
)
@dataclass(frozen=True, kw_only=True)
class AugustDoorbellBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes August binary_sensor entity."""
2024-07-07 13:39:58 +00:00
value_fn: Callable[[AugustData, DoorbellDetail | LockDetail], Activity | None]
is_time_based: bool
SENSOR_TYPE_DOOR = BinarySensorEntityDescription(
key="open",
device_class=BinarySensorDeviceClass.DOOR,
)
SENSOR_TYPES_VIDEO_DOORBELL = (
AugustDoorbellBinarySensorEntityDescription(
key="motion",
device_class=BinarySensorDeviceClass.MOTION,
2024-07-07 13:39:58 +00:00
value_fn=retrieve_doorbell_motion_activity,
is_time_based=True,
),
AugustDoorbellBinarySensorEntityDescription(
key="image capture",
translation_key="image_capture",
2024-06-19 14:21:04 +00:00
value_fn=partial(
2024-07-07 13:39:58 +00:00
retrieve_time_based_activity, {ActivityType.DOORBELL_IMAGE_CAPTURE}
2024-06-19 14:21:04 +00:00
),
is_time_based=True,
),
AugustDoorbellBinarySensorEntityDescription(
key="online",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
2024-07-07 13:39:58 +00:00
value_fn=retrieve_online_state,
is_time_based=False,
),
)
SENSOR_TYPES_DOORBELL: tuple[AugustDoorbellBinarySensorEntityDescription, ...] = (
AugustDoorbellBinarySensorEntityDescription(
key="ding",
translation_key="ding",
device_class=BinarySensorDeviceClass.OCCUPANCY,
2024-07-07 13:39:58 +00:00
value_fn=retrieve_ding_activity,
is_time_based=True,
),
)
async def async_setup_entry(
hass: HomeAssistant,
2024-05-02 12:39:02 +00:00
config_entry: AugustConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the August binary sensors."""
2024-05-02 12:39:02 +00:00
data = config_entry.runtime_data
entities: list[BinarySensorEntity] = []
for lock in data.locks:
detail = data.get_device_detail(lock.device_id)
if detail.doorsense:
entities.append(AugustDoorBinarySensor(data, lock, SENSOR_TYPE_DOOR))
Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299) * Add doorsense sensor for August 3rd Gen Smart Lock Pro Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor. This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end. * Changed to use snjoetw provided code Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor. Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think. I modified his coding with: Fixes that were done to the August component and not part of the coding snjoetw provided. Added the debug logging improvement I had done in the code. Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same. * Reverted change from add_device to add_entities Missed an item when merging snjoetw's code with current. Fixed. * Updated call from add_devices to add_entities as well Updated the call from add_devices to add_entities. * Fixed permissions on files Fixed permissions on components/august.py and binary_snesor/august.py * Changed if/else to if/continue Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic. * Added available property for Door Sensor Added the available property for the Door Sensor and setting it to False if a status unknown is received. * Updated setting self._available Changed line for setting self._available to what Martin provided. Much more efficient to read. :-)
2018-10-19 07:37:02 +00:00
if detail.doorbell:
entities.extend(
AugustDoorbellBinarySensor(data, lock, description)
for description in SENSOR_TYPES_DOORBELL
)
entities.extend(
AugustDoorbellBinarySensor(data, doorbell, description)
for description in SENSOR_TYPES_DOORBELL + SENSOR_TYPES_VIDEO_DOORBELL
for doorbell in data.doorbells
)
2021-04-25 09:32:34 +00:00
async_add_entities(entities)
class AugustDoorBinarySensor(AugustDescriptionEntity, BinarySensorEntity):
Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299) * Add doorsense sensor for August 3rd Gen Smart Lock Pro Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor. This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end. * Changed to use snjoetw provided code Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor. Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think. I modified his coding with: Fixes that were done to the August component and not part of the coding snjoetw provided. Added the debug logging improvement I had done in the code. Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same. * Reverted change from add_device to add_entities Missed an item when merging snjoetw's code with current. Fixed. * Updated call from add_devices to add_entities as well Updated the call from add_devices to add_entities. * Fixed permissions on files Fixed permissions on components/august.py and binary_snesor/august.py * Changed if/else to if/continue Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic. * Added available property for Door Sensor Added the available property for the Door Sensor and setting it to False if a status unknown is received. * Updated setting self._available Changed line for setting self._available to what Martin provided. Much more efficient to read. :-)
2018-10-19 07:37:02 +00:00
"""Representation of an August Door binary sensor."""
_attr_device_class = BinarySensorDeviceClass.DOOR
description: BinarySensorEntityDescription
Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299) * Add doorsense sensor for August 3rd Gen Smart Lock Pro Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor. This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end. * Changed to use snjoetw provided code Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor. Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think. I modified his coding with: Fixes that were done to the August component and not part of the coding snjoetw provided. Added the debug logging improvement I had done in the code. Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same. * Reverted change from add_device to add_entities Missed an item when merging snjoetw's code with current. Fixed. * Updated call from add_devices to add_entities as well Updated the call from add_devices to add_entities. * Fixed permissions on files Fixed permissions on components/august.py and binary_snesor/august.py * Changed if/else to if/continue Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic. * Added available property for Door Sensor Added the available property for the Door Sensor and setting it to False if a status unknown is received. * Updated setting self._available Changed line for setting self._available to what Martin provided. Much more efficient to read. :-)
2018-10-19 07:37:02 +00:00
@callback
2024-01-18 22:13:08 +00:00
def _update_from_data(self) -> None:
"""Get the latest state of the sensor and update activity."""
2024-06-19 14:21:04 +00:00
if door_activity := self._get_latest({ActivityType.DOOR_OPERATION}):
update_lock_detail_from_activity(self._detail, door_activity)
2024-06-19 19:11:36 +00:00
if door_activity.was_pushed:
2021-04-25 09:32:34 +00:00
self._detail.set_online(True)
2024-06-19 14:21:04 +00:00
if bridge_activity := self._get_latest({ActivityType.BRIDGE_OPERATION}):
update_lock_detail_from_activity(self._detail, bridge_activity)
self._attr_available = self._detail.bridge_is_online
self._attr_is_on = self._detail.door_state == LockDoorStatus.OPEN
class AugustDoorbellBinarySensor(AugustDescriptionEntity, BinarySensorEntity):
"""Representation of an August binary sensor."""
entity_description: AugustDoorbellBinarySensorEntityDescription
_check_for_off_update_listener: Callable[[], None] | None = None
@callback
2024-01-18 22:13:08 +00:00
def _update_from_data(self) -> None:
"""Get the latest state of the sensor."""
self._cancel_any_pending_updates()
2024-07-07 13:39:58 +00:00
self._attr_is_on = bool(
self.entity_description.value_fn(self._data, self._detail)
)
if self.entity_description.is_time_based:
2024-07-07 13:39:58 +00:00
self._attr_available = retrieve_online_state(self._data, self._detail)
self._schedule_update_to_recheck_turn_off_sensor()
else:
self._attr_available = True
2024-06-18 20:43:16 +00:00
@callback
def _async_scheduled_update(self, now: datetime) -> None:
"""Timer callback for sensor update."""
self._check_for_off_update_listener = None
self._update_from_data()
if not self.is_on:
self.async_write_ha_state()
2024-01-18 22:13:08 +00:00
def _schedule_update_to_recheck_turn_off_sensor(self) -> None:
"""Schedule an update to recheck the sensor to see if it is ready to turn off."""
# If the sensor is already off there is nothing to do
if not self.is_on:
return
self._check_for_off_update_listener = async_call_later(
2024-06-18 20:43:16 +00:00
self.hass, TIME_TO_RECHECK_DETECTION, self._async_scheduled_update
)
2024-01-18 22:13:08 +00:00
def _cancel_any_pending_updates(self) -> None:
"""Cancel any updates to recheck a sensor to see if it is ready to turn off."""
if not self._check_for_off_update_listener:
return
_LOGGER.debug("%s: canceled pending update", self.entity_id)
self._check_for_off_update_listener()
self._check_for_off_update_listener = None
2023-02-17 18:15:56 +00:00
async def async_will_remove_from_hass(self) -> None:
"""When removing cancel any scheduled updates."""
self._cancel_any_pending_updates()
await super().async_will_remove_from_hass()