Skip to content

Commit c7d7cfa

Browse files
authored
Add Reolink IO input binary sensor (home-assistant#154133)
1 parent e4ea798 commit c7d7cfa

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

homeassistant/components/reolink/binary_sensor.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ class ReolinkSmartAIBinarySensorEntityDescription(
5757
supported: Callable[[Host, int, int], bool] = lambda api, ch, loc: True
5858

5959

60+
@dataclass(frozen=True, kw_only=True)
61+
class ReolinkIndexBinarySensorEntityDescription(
62+
BinarySensorEntityDescription,
63+
ReolinkEntityDescription,
64+
):
65+
"""A class that describes binary sensor entities with an extra index."""
66+
67+
value: Callable[[Host, int, int], bool | None]
68+
supported: Callable[[Host, int, int], bool] = lambda api, ch, idx: True
69+
70+
6071
BINARY_PUSH_SENSORS = (
6172
ReolinkBinarySensorEntityDescription(
6273
key="motion",
@@ -282,6 +293,13 @@ class ReolinkSmartAIBinarySensorEntityDescription(
282293
),
283294
)
284295

296+
BINARY_IO_INPUT_SENSOR = ReolinkIndexBinarySensorEntityDescription(
297+
key="io_input",
298+
cmd_id=677,
299+
translation_key="io_input",
300+
value=lambda api, ch, idx: api.baichuan.io_input_state(ch, idx),
301+
)
302+
285303

286304
async def async_setup_entry(
287305
hass: HomeAssistant,
@@ -292,7 +310,7 @@ async def async_setup_entry(
292310
reolink_data: ReolinkData = config_entry.runtime_data
293311
api = reolink_data.host.api
294312

295-
entities: list[ReolinkBinarySensorEntity | ReolinkSmartAIBinarySensorEntity] = []
313+
entities: list[BinarySensorEntity] = []
296314
for channel in api.channels:
297315
entities.extend(
298316
ReolinkPushBinarySensorEntity(reolink_data, channel, entity_description)
@@ -314,6 +332,12 @@ async def async_setup_entry(
314332
)
315333
if entity_description.supported(api, channel, location)
316334
)
335+
entities.extend(
336+
ReolinkIndexBinarySensorEntity(
337+
reolink_data, channel, index, BINARY_IO_INPUT_SENSOR
338+
)
339+
for index in api.baichuan.io_inputs(channel)
340+
)
317341

318342
async_add_entities(entities)
319343

@@ -407,3 +431,31 @@ def is_on(self) -> bool:
407431
return self.entity_description.value(
408432
self._host.api, self._channel, self._location
409433
)
434+
435+
436+
class ReolinkIndexBinarySensorEntity(
437+
ReolinkChannelCoordinatorEntity, BinarySensorEntity
438+
):
439+
"""Binary-sensor class for Reolink IP camera with an extra index."""
440+
441+
entity_description: ReolinkIndexBinarySensorEntityDescription
442+
443+
def __init__(
444+
self,
445+
reolink_data: ReolinkData,
446+
channel: int,
447+
index: int,
448+
entity_description: ReolinkIndexBinarySensorEntityDescription,
449+
) -> None:
450+
"""Initialize Reolink binary sensor."""
451+
self.entity_description = entity_description
452+
super().__init__(reolink_data, channel)
453+
self._attr_unique_id = f"{self._attr_unique_id}_{index}"
454+
455+
self._index = index
456+
self._attr_translation_placeholders = {"index": str(index)}
457+
458+
@property
459+
def is_on(self) -> bool | None:
460+
"""State of the sensor."""
461+
return self.entity_description.value(self._host.api, self._channel, self._index)

homeassistant/components/reolink/icons.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@
126126
"state": {
127127
"on": "mdi:package-variant-closed-check"
128128
}
129+
},
130+
"io_input": {
131+
"default": "mdi:electric-switch",
132+
"state": {
133+
"on": "mdi:electric-switch-closed"
134+
}
129135
}
130136
},
131137
"button": {

homeassistant/components/reolink/strings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,13 @@
427427
"off": "[%key:component::binary_sensor::entity_component::gas::state::off%]",
428428
"on": "[%key:component::binary_sensor::entity_component::gas::state::on%]"
429429
}
430+
},
431+
"io_input": {
432+
"name": "IO input {index}",
433+
"state": {
434+
"off": "[%key:common::state::disconnected%]",
435+
"on": "[%key:common::state::connected%]"
436+
}
430437
}
431438
},
432439
"button": {

tests/components/reolink/test_binary_sensor.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ async def test_motion_sensor(
5353

5454
async def test_smart_ai_sensor(
5555
hass: HomeAssistant,
56-
hass_client_no_auth: ClientSessionGenerator,
5756
freezer: FrozenDateTimeFactory,
5857
config_entry: MockConfigEntry,
5958
reolink_host: MagicMock,
@@ -77,6 +76,31 @@ async def test_smart_ai_sensor(
7776
assert hass.states.get(entity_id).state == STATE_OFF
7877

7978

79+
async def test_index_sensor(
80+
hass: HomeAssistant,
81+
freezer: FrozenDateTimeFactory,
82+
config_entry: MockConfigEntry,
83+
reolink_host: MagicMock,
84+
) -> None:
85+
"""Test index binary sensor entity."""
86+
reolink_host.baichuan.io_inputs.return_value = [0]
87+
reolink_host.baichuan.io_input_state.return_value = True
88+
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.BINARY_SENSOR]):
89+
assert await hass.config_entries.async_setup(config_entry.entry_id)
90+
await hass.async_block_till_done()
91+
assert config_entry.state is ConfigEntryState.LOADED
92+
93+
entity_id = f"{Platform.BINARY_SENSOR}.{TEST_CAM_NAME}_io_input_0"
94+
assert hass.states.get(entity_id).state == STATE_ON
95+
96+
reolink_host.baichuan.io_input_state.return_value = False
97+
freezer.tick(DEVICE_UPDATE_INTERVAL)
98+
async_fire_time_changed(hass)
99+
await hass.async_block_till_done()
100+
101+
assert hass.states.get(entity_id).state == STATE_OFF
102+
103+
80104
async def test_tcp_callback(
81105
hass: HomeAssistant,
82106
config_entry: MockConfigEntry,

0 commit comments

Comments
 (0)