Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 205 additions & 5 deletions homeassistant/components/reolink/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .entity import ReolinkChannelCoordinatorEntity, ReolinkChannelEntityDescription
from .entity import (
ReolinkChannelCoordinatorEntity,
ReolinkChannelEntityDescription,
ReolinkEntityDescription,
)
from .util import ReolinkConfigEntry, ReolinkData

PARALLEL_UPDATES = 0
Expand All @@ -41,6 +45,18 @@ class ReolinkBinarySensorEntityDescription(
value: Callable[[Host, int], bool]


@dataclass(frozen=True, kw_only=True)
class ReolinkSmartAIBinarySensorEntityDescription(
BinarySensorEntityDescription,
ReolinkEntityDescription,
):
"""A class that describes Smart AI binary sensor entities."""

smart_type: str
value: Callable[[Host, int, int], bool]
supported: Callable[[Host, int, int], bool] = lambda api, ch, loc: True


BINARY_PUSH_SENSORS = (
ReolinkBinarySensorEntityDescription(
key="motion",
Expand Down Expand Up @@ -121,6 +137,142 @@ class ReolinkBinarySensorEntityDescription(
),
)

BINARY_SMART_AI_SENSORS = (
ReolinkSmartAIBinarySensorEntityDescription(
key="crossline_person",
smart_type="crossline",
cmd_id=33,
translation_key="crossline_person",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "crossline", loc, "people")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_crossline")
and "people" in api.baichuan.smart_ai_type_list(ch, "crossline", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="crossline_vehicle",
smart_type="crossline",
cmd_id=33,
translation_key="crossline_vehicle",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "crossline", loc, "vehicle")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_crossline")
and "vehicle" in api.baichuan.smart_ai_type_list(ch, "crossline", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="crossline_dog_cat",
smart_type="crossline",
cmd_id=33,
translation_key="crossline_dog_cat",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "crossline", loc, "dog_cat")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_crossline")
and "dog_cat" in api.baichuan.smart_ai_type_list(ch, "crossline", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="intrusion_person",
smart_type="intrusion",
cmd_id=33,
translation_key="intrusion_person",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "intrusion", loc, "people")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_intrusion")
and "people" in api.baichuan.smart_ai_type_list(ch, "intrusion", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="intrusion_vehicle",
smart_type="intrusion",
cmd_id=33,
translation_key="intrusion_vehicle",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "intrusion", loc, "vehicle")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_intrusion")
and "vehicle" in api.baichuan.smart_ai_type_list(ch, "intrusion", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="intrusion_dog_cat",
smart_type="intrusion",
cmd_id=33,
translation_key="intrusion_dog_cat",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "intrusion", loc, "dog_cat")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_intrusion")
and "dog_cat" in api.baichuan.smart_ai_type_list(ch, "intrusion", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="linger_person",
smart_type="loitering",
cmd_id=33,
translation_key="linger_person",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "loitering", loc, "people")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_linger")
and "people" in api.baichuan.smart_ai_type_list(ch, "loitering", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="linger_vehicle",
smart_type="loitering",
cmd_id=33,
translation_key="linger_vehicle",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "loitering", loc, "vehicle")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_linger")
and "vehicle" in api.baichuan.smart_ai_type_list(ch, "loitering", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="linger_dog_cat",
smart_type="loitering",
cmd_id=33,
translation_key="linger_dog_cat",
value=lambda api, ch, loc: (
api.baichuan.smart_ai_state(ch, "loitering", loc, "dog_cat")
),
supported=lambda api, ch, loc: (
api.supported(ch, "ai_linger")
and "dog_cat" in api.baichuan.smart_ai_type_list(ch, "loitering", loc)
),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="forgotten_item",
smart_type="legacy",
cmd_id=33,
translation_key="forgotten_item",
value=lambda api, ch, loc: (api.baichuan.smart_ai_state(ch, "legacy", loc)),
supported=lambda api, ch, loc: api.supported(ch, "ai_forgotten_item"),
),
ReolinkSmartAIBinarySensorEntityDescription(
key="taken_item",
smart_type="loss",
cmd_id=33,
translation_key="taken_item",
value=lambda api, ch, loc: (api.baichuan.smart_ai_state(ch, "loss", loc)),
supported=lambda api, ch, loc: api.supported(ch, "ai_taken_item"),
),
)


async def async_setup_entry(
hass: HomeAssistant,
Expand All @@ -129,18 +281,29 @@ async def async_setup_entry(
) -> None:
"""Set up a Reolink IP Camera."""
reolink_data: ReolinkData = config_entry.runtime_data
api = reolink_data.host.api

entities: list[ReolinkBinarySensorEntity] = []
for channel in reolink_data.host.api.channels:
entities: list[ReolinkBinarySensorEntity | ReolinkSmartAIBinarySensorEntity] = []
for channel in api.channels:
entities.extend(
ReolinkPushBinarySensorEntity(reolink_data, channel, entity_description)
for entity_description in BINARY_PUSH_SENSORS
if entity_description.supported(reolink_data.host.api, channel)
if entity_description.supported(api, channel)
)
entities.extend(
ReolinkBinarySensorEntity(reolink_data, channel, entity_description)
for entity_description in BINARY_SENSORS
if entity_description.supported(reolink_data.host.api, channel)
if entity_description.supported(api, channel)
)
entities.extend(
ReolinkSmartAIBinarySensorEntity(
reolink_data, channel, location, entity_description
)
for entity_description in BINARY_SMART_AI_SENSORS
for location in api.baichuan.smart_location_list(
channel, entity_description.key
)
if entity_description.supported(api, channel, location)
)

async_add_entities(entities)
Expand Down Expand Up @@ -198,3 +361,40 @@ async def async_added_to_hass(self) -> None:
async def _async_handle_event(self, event: str) -> None:
"""Handle incoming event for motion detection."""
self.async_write_ha_state()


class ReolinkSmartAIBinarySensorEntity(
ReolinkChannelCoordinatorEntity, BinarySensorEntity
):
"""Binary-sensor class for Reolink IP camera Smart AI sensors."""

entity_description: ReolinkSmartAIBinarySensorEntityDescription

def __init__(
self,
reolink_data: ReolinkData,
channel: int,
location: int,
entity_description: ReolinkSmartAIBinarySensorEntityDescription,
) -> None:
"""Initialize Reolink binary sensor."""
self.entity_description = entity_description
super().__init__(reolink_data, channel)
unique_index = self._host.api.baichuan.smart_ai_index(
channel, entity_description.smart_type, location
)
self._attr_unique_id = f"{self._attr_unique_id}_{unique_index}"

self._location = location
self._attr_translation_placeholders = {
"zone_name": self._host.api.baichuan.smart_ai_name(
channel, entity_description.smart_type, location
)
}

@property
def is_on(self) -> bool:
"""State of the sensor."""
return self.entity_description.value(
self._host.api, self._channel, self._location
)
66 changes: 66 additions & 0 deletions homeassistant/components/reolink/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,72 @@
"state": {
"on": "mdi:sleep"
}
},
"crossline_person": {
"default": "mdi:fence",
"state": {
"on": "mdi:fence-electric"
}
},
"crossline_vehicle": {
"default": "mdi:fence",
"state": {
"on": "mdi:fence-electric"
}
},
"crossline_dog_cat": {
"default": "mdi:fence",
"state": {
"on": "mdi:fence-electric"
}
},
"intrusion_person": {
"default": "mdi:location-enter",
"state": {
"on": "mdi:alert-circle-outline"
}
},
"intrusion_vehicle": {
"default": "mdi:location-enter",
"state": {
"on": "mdi:alert-circle-outline"
}
},
"intrusion_dog_cat": {
"default": "mdi:location-enter",
"state": {
"on": "mdi:alert-circle-outline"
}
},
"linger_person": {
"default": "mdi:account-switch",
"state": {
"on": "mdi:account-alert"
}
},
"linger_vehicle": {
"default": "mdi:account-switch",
"state": {
"on": "mdi:account-alert"
}
},
"linger_dog_cat": {
"default": "mdi:account-switch",
"state": {
"on": "mdi:account-alert"
}
},
"forgotten_item": {
"default": "mdi:package-variant-closed-plus",
"state": {
"on": "mdi:package-variant-closed-check"
}
},
"taken_item": {
"default": "mdi:package-variant-closed-minus",
"state": {
"on": "mdi:package-variant-closed-check"
}
}
},
"button": {
Expand Down
Loading
Loading