Skip to content

Commit

Permalink
Remove Reolink Home Hub main level switches (#126697)
Browse files Browse the repository at this point in the history
Co-authored-by: Robert Resch <robert@resch.dev>
  • Loading branch information
starkillerOG and edenhaus authored Sep 25, 2024
1 parent 90dcb02 commit 1395bae
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 8 deletions.
4 changes: 4 additions & 0 deletions homeassistant/components/reolink/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
"hdr_switch_deprecated": {
"title": "Reolink HDR switch deprecated",
"description": "The Reolink HDR switch entity is deprecated and will be removed in HA 2025.2.0. It has been replaced by a HDR select entity offering options `on`, `off` and `auto`. To remove this issue, please adjust automations accordingly and disable the HDR switch entity."
},
"hub_switch_deprecated": {
"title": "Reolink Home Hub switches deprecated",
"description": "The redundant 'Record', 'Email on event', 'FTP upload', 'Push notifications', and 'Buzzer on event' switches on the Reolink Home Hub are depricated since the new firmware no longer supports these. Please use the equally named switches under each of the camera devices connected to the Home Hub instead. To remove this issue, please adjust automations accordingly and disable the switch entities mentioned."
}
},
"services": {
Expand Down
88 changes: 81 additions & 7 deletions homeassistant/components/reolink/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class ReolinkChimeSwitchEntityDescription(
cmd_key="GetEmail",
translation_key="email",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "email"),
supported=lambda api: api.supported(None, "email") and not api.is_hub,
value=lambda api: api.email_enabled(),
method=lambda api, value: api.set_email(None, value),
),
Expand All @@ -223,7 +223,7 @@ class ReolinkChimeSwitchEntityDescription(
cmd_key="GetFtp",
translation_key="ftp_upload",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "ftp"),
supported=lambda api: api.supported(None, "ftp") and not api.is_hub,
value=lambda api: api.ftp_enabled(),
method=lambda api, value: api.set_ftp(None, value),
),
Expand All @@ -232,7 +232,7 @@ class ReolinkChimeSwitchEntityDescription(
cmd_key="GetPush",
translation_key="push_notifications",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "push"),
supported=lambda api: api.supported(None, "push") and not api.is_hub,
value=lambda api: api.push_enabled(),
method=lambda api, value: api.set_push(None, value),
),
Expand All @@ -241,7 +241,7 @@ class ReolinkChimeSwitchEntityDescription(
cmd_key="GetRec",
translation_key="record",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "recording"),
supported=lambda api: api.supported(None, "recording") and not api.is_hub,
value=lambda api: api.recording_enabled(),
method=lambda api, value: api.set_recording(None, value),
),
Expand All @@ -250,7 +250,7 @@ class ReolinkChimeSwitchEntityDescription(
cmd_key="GetBuzzerAlarmV20",
translation_key="hub_ringtone_on_event",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "buzzer"),
supported=lambda api: api.supported(None, "buzzer") and not api.is_hub,
value=lambda api: api.buzzer_enabled(),
method=lambda api, value: api.set_buzzer(None, value),
),
Expand Down Expand Up @@ -279,6 +279,56 @@ class ReolinkChimeSwitchEntityDescription(
method=lambda api, ch, value: api.set_HDR(ch, value),
)

# Can be removed in HA 2025.4.0
DEPRECATED_NVR_SWITCHES = [
ReolinkNVRSwitchEntityDescription(
key="email",
cmd_key="GetEmail",
translation_key="email",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.is_hub,
value=lambda api: api.email_enabled(),
method=lambda api, value: api.set_email(None, value),
),
ReolinkNVRSwitchEntityDescription(
key="ftp_upload",
cmd_key="GetFtp",
translation_key="ftp_upload",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.is_hub,
value=lambda api: api.ftp_enabled(),
method=lambda api, value: api.set_ftp(None, value),
),
ReolinkNVRSwitchEntityDescription(
key="push_notifications",
cmd_key="GetPush",
translation_key="push_notifications",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.is_hub,
value=lambda api: api.push_enabled(),
method=lambda api, value: api.set_push(None, value),
),
ReolinkNVRSwitchEntityDescription(
key="record",
cmd_key="GetRec",
translation_key="record",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.is_hub,
value=lambda api: api.recording_enabled(),
method=lambda api, value: api.set_recording(None, value),
),
ReolinkNVRSwitchEntityDescription(
key="buzzer",
cmd_key="GetBuzzerAlarmV20",
translation_key="hub_ringtone_on_event",
icon="mdi:room-service",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.is_hub,
value=lambda api: api.buzzer_enabled(),
method=lambda api, value: api.set_buzzer(None, value),
),
]


async def async_setup_entry(
hass: HomeAssistant,
Expand Down Expand Up @@ -307,10 +357,17 @@ async def async_setup_entry(
for chime in reolink_data.host.api.chime_list
)

# Can be removed in HA 2025.2.0
# Can be removed in HA 2025.4.0
depricated_dict = {}
for desc in DEPRECATED_NVR_SWITCHES:
if not desc.supported(reolink_data.host.api):
continue
depricated_dict[f"{reolink_data.host.unique_id}_{desc.key}"] = desc

entity_reg = er.async_get(hass)
reg_entities = er.async_entries_for_config_entry(entity_reg, config_entry.entry_id)
for entity in reg_entities:
# Can be removed in HA 2025.2.0
if entity.domain == "switch" and entity.unique_id.endswith("_hdr"):
if entity.disabled:
entity_reg.async_remove(entity.entity_id)
Expand All @@ -329,7 +386,24 @@ async def async_setup_entry(
for channel in reolink_data.host.api.channels
if DEPRECATED_HDR.supported(reolink_data.host.api, channel)
)
break

# Can be removed in HA 2025.4.0
if entity.domain == "switch" and entity.unique_id in depricated_dict:
if entity.disabled:
entity_reg.async_remove(entity.entity_id)
continue

ir.async_create_issue(
hass,
DOMAIN,
"hub_switch_deprecated",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key="hub_switch_deprecated",
)
entities.append(
ReolinkNVRSwitchEntity(reolink_data, depricated_dict[entity.unique_id])
)

async_add_entities(entities)

Expand Down
1 change: 1 addition & 0 deletions tests/components/reolink/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def reolink_connect_class() -> Generator[MagicMock]:
host_mock.check_new_firmware.return_value = False
host_mock.unsubscribe.return_value = True
host_mock.logout.return_value = True
host_mock.is_hub = False
host_mock.mac_address = TEST_MAC
host_mock.uid = TEST_UID
host_mock.onvif_enabled = True
Expand Down
146 changes: 145 additions & 1 deletion tests/components/reolink/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from tests.common import MockConfigEntry, async_fire_time_changed


async def test_cleanup_hdr_switch_(
async def test_cleanup_hdr_switch(
hass: HomeAssistant,
config_entry: MockConfigEntry,
reolink_connect: MagicMock,
Expand Down Expand Up @@ -60,6 +60,77 @@ async def test_cleanup_hdr_switch_(
assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id) is None


@pytest.mark.parametrize(
(
"original_id",
"capability",
),
[
(
f"{TEST_UID}_record",
"recording",
),
(
f"{TEST_UID}_ftp_upload",
"ftp",
),
(
f"{TEST_UID}_push_notifications",
"push",
),
(
f"{TEST_UID}_email",
"email",
),
(
f"{TEST_UID}_buzzer",
"buzzer",
),
],
)
async def test_cleanup_hub_switches(
hass: HomeAssistant,
config_entry: MockConfigEntry,
reolink_connect: MagicMock,
entity_registry: er.EntityRegistry,
original_id: str,
capability: str,
) -> None:
"""Test entity ids that need to be migrated."""

def mock_supported(ch, cap):
if cap == capability:
return False
return True

domain = Platform.SWITCH

reolink_connect.channels = [0]
reolink_connect.is_hub = True
reolink_connect.supported = mock_supported

entity_registry.async_get_or_create(
domain=domain,
platform=DOMAIN,
unique_id=original_id,
config_entry=config_entry,
suggested_object_id=original_id,
disabled_by=er.RegistryEntryDisabler.USER,
)

assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id)

# setup CH 0 and host entities/device
with patch("homeassistant.components.reolink.PLATFORMS", [domain]):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id) is None

reolink_connect.is_hub = False
reolink_connect.supported.return_value = True


async def test_hdr_switch_deprecated_repair_issue(
hass: HomeAssistant,
config_entry: MockConfigEntry,
Expand Down Expand Up @@ -95,6 +166,79 @@ async def test_hdr_switch_deprecated_repair_issue(
assert (DOMAIN, "hdr_switch_deprecated") in issue_registry.issues


@pytest.mark.parametrize(
(
"original_id",
"capability",
),
[
(
f"{TEST_UID}_record",
"recording",
),
(
f"{TEST_UID}_ftp_upload",
"ftp",
),
(
f"{TEST_UID}_push_notifications",
"push",
),
(
f"{TEST_UID}_email",
"email",
),
(
f"{TEST_UID}_buzzer",
"buzzer",
),
],
)
async def test_hub_switches_repair_issue(
hass: HomeAssistant,
config_entry: MockConfigEntry,
reolink_connect: MagicMock,
entity_registry: er.EntityRegistry,
issue_registry: ir.IssueRegistry,
original_id: str,
capability: str,
) -> None:
"""Test entity ids that need to be migrated."""

def mock_supported(ch, cap):
if cap == capability:
return False
return True

domain = Platform.SWITCH

reolink_connect.channels = [0]
reolink_connect.is_hub = True
reolink_connect.supported = mock_supported

entity_registry.async_get_or_create(
domain=domain,
platform=DOMAIN,
unique_id=original_id,
config_entry=config_entry,
suggested_object_id=original_id,
disabled_by=None,
)

assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id)

# setup CH 0 and host entities/device
with patch("homeassistant.components.reolink.PLATFORMS", [domain]):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id)
assert (DOMAIN, "hub_switch_deprecated") in issue_registry.issues

reolink_connect.is_hub = False
reolink_connect.supported.return_value = True


async def test_switch(
hass: HomeAssistant,
config_entry: MockConfigEntry,
Expand Down

0 comments on commit 1395bae

Please sign in to comment.