Deprecate UniFi Protect HDR switch and package sensor (#113636)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Christopher Bailey 2024-03-16 22:15:32 -04:00 committed by GitHub
parent 0a26829ffc
commit 43652a4ace
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 305 additions and 321 deletions

View File

@ -458,6 +458,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
name="Package Detected", name="Package Detected",
icon="mdi:package-variant-closed", icon="mdi:package-variant-closed",
ufp_value="is_package_currently_detected", ufp_value="is_package_currently_detected",
entity_registry_enabled_default=False,
ufp_required_field="can_detect_package", ufp_required_field="can_detect_package",
ufp_enabled="is_package_detection_on", ufp_enabled="is_package_detection_on",
ufp_event_obj="last_package_detect_event", ufp_event_obj="last_package_detect_event",

View File

@ -2,19 +2,103 @@
from __future__ import annotations from __future__ import annotations
from itertools import chain
import logging import logging
from pyunifiprotect import ProtectApiClient from pyunifiprotect import ProtectApiClient
from pyunifiprotect.data import NVR, Bootstrap, ProtectAdoptableDeviceModel from pyunifiprotect.data import Bootstrap
from typing_extensions import TypedDict
from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.issue_registry import IssueSeverity
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class EntityRef(TypedDict):
"""Entity ref parameter variable."""
id: str
platform: Platform
class EntityUsage(TypedDict):
"""Entity usages response variable."""
automations: dict[str, list[str]]
scripts: dict[str, list[str]]
@callback
def check_if_used(
hass: HomeAssistant, entry: ConfigEntry, entities: dict[str, EntityRef]
) -> dict[str, EntityUsage]:
"""Check for usages of entities and return them."""
entity_registry = er.async_get(hass)
refs: dict[str, EntityUsage] = {
ref: {"automations": {}, "scripts": {}} for ref in entities
}
for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id):
for ref_id, ref in entities.items():
if (
entity.domain == ref["platform"]
and entity.disabled_by is None
and ref["id"] in entity.unique_id
):
entity_automations = automations_with_entity(hass, entity.entity_id)
entity_scripts = scripts_with_entity(hass, entity.entity_id)
if entity_automations:
refs[ref_id]["automations"][entity.entity_id] = entity_automations
if entity_scripts:
refs[ref_id]["scripts"][entity.entity_id] = entity_scripts
return refs
@callback
def create_repair_if_used(
hass: HomeAssistant,
entry: ConfigEntry,
breaks_in: str,
entities: dict[str, EntityRef],
) -> None:
"""Create repairs for used entities that are deprecated."""
usages = check_if_used(hass, entry, entities)
for ref_id, refs in usages.items():
issue_id = f"deprecate_{ref_id}"
automations = refs["automations"]
scripts = refs["scripts"]
if automations or scripts:
items = sorted(
set(chain.from_iterable(chain(automations.values(), scripts.values())))
)
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
is_fixable=False,
breaks_in_ha_version=breaks_in,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"items": "* `" + "`\n* `".join(items) + "`\n"
},
)
else:
_LOGGER.debug("No found usages of %s", ref_id)
ir.async_delete_issue(hass, DOMAIN, issue_id)
async def async_migrate_data( async def async_migrate_data(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: ConfigEntry,
@ -23,132 +107,32 @@ async def async_migrate_data(
) -> None: ) -> None:
"""Run all valid UniFi Protect data migrations.""" """Run all valid UniFi Protect data migrations."""
_LOGGER.debug("Start Migrate: async_migrate_buttons") _LOGGER.debug("Start Migrate: async_deprecate_hdr_package")
await async_migrate_buttons(hass, entry, protect, bootstrap) async_deprecate_hdr_package(hass, entry)
_LOGGER.debug("Completed Migrate: async_migrate_buttons") _LOGGER.debug("Completed Migrate: async_deprecate_hdr_package")
_LOGGER.debug("Start Migrate: async_migrate_device_ids")
await async_migrate_device_ids(hass, entry, protect, bootstrap)
_LOGGER.debug("Completed Migrate: async_migrate_device_ids")
async def async_migrate_buttons( @callback
hass: HomeAssistant, def async_deprecate_hdr_package(hass: HomeAssistant, entry: ConfigEntry) -> None:
entry: ConfigEntry, """Check for usages of hdr_mode switch and package sensor and raise repair if it is used.
protect: ProtectApiClient,
bootstrap: Bootstrap,
) -> None:
"""Migrate existing Reboot button unique IDs from {device_id} to {deivce_id}_reboot.
This allows for additional types of buttons that are outside of just a reboot button. UniFi Protect v3.0.22 changed how HDR works so it is no longer a simple on/off toggle. There is
Always On, Always Off and Auto. So it has been migrated to a select. The old switch is now deprecated.
Added in 2022.6.0. Additionally, the Package sensor is no longer functional due to how events work so a repair to notify users.
Added in 2024.4.0
""" """
registry = er.async_get(hass) create_repair_if_used(
to_migrate = [] hass,
for entity in er.async_entries_for_config_entry(registry, entry.entry_id): entry,
if entity.domain == Platform.BUTTON and "_" not in entity.unique_id: "2024.10.0",
_LOGGER.debug("Button %s needs migration", entity.entity_id) {
to_migrate.append(entity) "hdr_switch": {"id": "hdr_mode", "platform": Platform.SWITCH},
"package_sensor": {
if len(to_migrate) == 0: "id": "smart_obj_package",
_LOGGER.debug("No button entities need migration") "platform": Platform.BINARY_SENSOR,
return },
},
count = 0 )
for button in to_migrate:
device = bootstrap.get_device_from_id(button.unique_id)
if device is None:
continue
new_unique_id = f"{device.id}_reboot"
_LOGGER.debug(
"Migrating entity %s (old unique_id: %s, new unique_id: %s)",
button.entity_id,
button.unique_id,
new_unique_id,
)
try:
registry.async_update_entity(button.entity_id, new_unique_id=new_unique_id)
except ValueError:
_LOGGER.warning(
"Could not migrate entity %s (old unique_id: %s, new unique_id: %s)",
button.entity_id,
button.unique_id,
new_unique_id,
)
else:
count += 1
if count < len(to_migrate):
_LOGGER.warning("Failed to migate %s reboot buttons", len(to_migrate) - count)
async def async_migrate_device_ids(
hass: HomeAssistant,
entry: ConfigEntry,
protect: ProtectApiClient,
bootstrap: Bootstrap,
) -> None:
"""Migrate unique IDs from {device_id}_{name} format to {mac}_{name} format.
This makes devices persist better with in HA. Anything a device is unadopted/readopted or
the Protect instance has to rebuild the disk array, the device IDs of Protect devices
can change. This causes a ton of orphaned entities and loss of historical data. MAC
addresses are the one persistent identifier a device has that does not change.
Added in 2022.7.0.
"""
registry = er.async_get(hass)
to_migrate = []
for entity in er.async_entries_for_config_entry(registry, entry.entry_id):
parts = entity.unique_id.split("_")
# device ID = 24 characters, MAC = 12
if len(parts[0]) == 24:
_LOGGER.debug("Entity %s needs migration", entity.entity_id)
to_migrate.append(entity)
if len(to_migrate) == 0:
_LOGGER.debug("No entities need migration to MAC address ID")
return
count = 0
for entity in to_migrate:
parts = entity.unique_id.split("_")
if parts[0] == bootstrap.nvr.id:
device: NVR | ProtectAdoptableDeviceModel | None = bootstrap.nvr
else:
device = bootstrap.get_device_from_id(parts[0])
if device is None:
continue
new_unique_id = device.mac
if len(parts) > 1:
new_unique_id = f"{device.mac}_{'_'.join(parts[1:])}"
_LOGGER.debug(
"Migrating entity %s (old unique_id: %s, new unique_id: %s)",
entity.entity_id,
entity.unique_id,
new_unique_id,
)
try:
registry.async_update_entity(entity.entity_id, new_unique_id=new_unique_id)
except ValueError as err:
_LOGGER.warning(
(
"Could not migrate entity %s (old unique_id: %s, new unique_id:"
" %s): %s"
),
entity.entity_id,
entity.unique_id,
new_unique_id,
err,
)
else:
count += 1
if count < len(to_migrate):
_LOGGER.warning("Failed to migrate %s entities", len(to_migrate) - count)

View File

@ -90,6 +90,14 @@
} }
} }
} }
},
"deprecate_hdr_switch": {
"title": "HDR Mode Switch Deprecated",
"description": "UniFi Protect v3 added a new state for HDR (auto). As a result, the HDR Mode Switch has been replaced with an HDR Mode Select, and it is deprecated.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly."
},
"deprecate_package_sensor": {
"title": "Package Event Sensor Deprecated",
"description": "The package event sensor never tripped because of the way events are reported in UniFi Protect. As a result, the sensor is deprecated and will be removed.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly."
} }
}, },
"entity": { "entity": {

View File

@ -5,7 +5,7 @@
"canAutoUpdate": true, "canAutoUpdate": true,
"isStatsGatheringEnabled": true, "isStatsGatheringEnabled": true,
"timezone": "America/New_York", "timezone": "America/New_York",
"version": "1.21.0-beta.2", "version": "2.2.6",
"ucoreVersion": "2.3.26", "ucoreVersion": "2.3.26",
"firmwareVersion": "2.3.10", "firmwareVersion": "2.3.10",
"uiVersion": null, "uiVersion": null,
@ -40,7 +40,7 @@
"enableStatsReporting": false, "enableStatsReporting": false,
"isSshEnabled": false, "isSshEnabled": false,
"errorCode": null, "errorCode": null,
"releaseChannel": "beta", "releaseChannel": "release",
"ssoChannel": null, "ssoChannel": null,
"hosts": ["192.168.216.198"], "hosts": ["192.168.216.198"],
"enableBridgeAutoAdoption": true, "enableBridgeAutoAdoption": true,

View File

@ -2,238 +2,225 @@
from __future__ import annotations from __future__ import annotations
from unittest.mock import AsyncMock from unittest.mock import patch
from pyunifiprotect.data import Light from pyunifiprotect.data import Camera
from pyunifiprotect.exceptions import NvrError
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
from homeassistant.components.repairs.issue_handler import (
async_process_repairs_platforms,
)
from homeassistant.components.script import DOMAIN as SCRIPT_DOMAIN
from homeassistant.components.unifiprotect.const import DOMAIN from homeassistant.components.unifiprotect.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.const import SERVICE_RELOAD, Platform
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from .utils import ( from .utils import MockUFPFixture, init_entry
MockUFPFixture,
generate_random_ids, from tests.typing import WebSocketGenerator
init_entry,
regenerate_device_ids,
)
async def test_migrate_reboot_button( async def test_deprecated_entity(
hass: HomeAssistant, ufp: MockUFPFixture, light: Light hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
) -> None: ):
"""Test migrating unique ID of reboot button.""" """Test Deprecate entity repair does not exist by default (new installs)."""
light1 = light.copy() await init_entry(hass, ufp, [doorbell])
light1.name = "Test Light 1"
regenerate_device_ids(light1)
light2 = light.copy() await async_process_repairs_platforms(hass)
light2.name = "Test Light 2" ws_client = await hass_ws_client(hass)
regenerate_device_ids(light2)
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
issue = None
for i in msg["result"]["issues"]:
if i["issue_id"] == "deprecate_hdr_switch":
issue = i
assert issue is None
async def test_deprecated_entity_no_automations(
hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera
):
"""Test Deprecate entity repair exists for existing installs."""
registry = er.async_get(hass) registry = er.async_get(hass)
registry.async_get_or_create( registry.async_get_or_create(
Platform.BUTTON, DOMAIN, light1.id, config_entry=ufp.entry Platform.SWITCH,
)
registry.async_get_or_create(
Platform.BUTTON,
DOMAIN, DOMAIN,
f"{light2.mac}_reboot", f"{doorbell.mac}_hdr_mode",
config_entry=ufp.entry, config_entry=ufp.entry,
) )
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap) await init_entry(hass, ufp, [doorbell])
await init_entry(hass, ufp, [light1, light2], regenerate_ids=False)
assert ufp.entry.state == ConfigEntryState.LOADED await async_process_repairs_platforms(hass)
assert ufp.api.update.called ws_client = await hass_ws_client(hass)
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
buttons = [ await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
entity msg = await ws_client.receive_json()
for entity in er.async_entries_for_config_entry(registry, ufp.entry.entry_id)
if entity.domain == Platform.BUTTON.value
]
assert len(buttons) == 4
assert registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device") is None assert msg["success"]
assert registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device_2") is None issue = None
light = registry.async_get(f"{Platform.BUTTON}.unifiprotect_{light1.id.lower()}") for i in msg["result"]["issues"]:
assert light is not None if i["issue_id"] == "deprecate_hdr_switch":
assert light.unique_id == f"{light1.mac}_reboot" issue = i
assert issue is None
assert registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device") is None
assert registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device_2") is None async def _load_automation(hass: HomeAssistant, entity_id: str):
light = registry.async_get( assert await async_setup_component(
f"{Platform.BUTTON}.unifiprotect_{light2.mac.lower()}_reboot" hass,
AUTOMATION_DOMAIN,
{
AUTOMATION_DOMAIN: [
{
"alias": "test1",
"trigger": [
{"platform": "state", "entity_id": entity_id},
{
"platform": "event",
"event_type": "state_changed",
"event_data": {"entity_id": entity_id},
},
],
"condition": {
"condition": "state",
"entity_id": entity_id,
"state": "on",
},
"action": [
{
"service": "test.script",
"data": {"entity_id": entity_id},
},
],
},
]
},
) )
assert light is not None
assert light.unique_id == f"{light2.mac}_reboot"
async def test_migrate_nvr_mac( async def test_deprecate_entity_automation(
hass: HomeAssistant, ufp: MockUFPFixture, light: Light hass: HomeAssistant,
ufp: MockUFPFixture,
hass_ws_client: WebSocketGenerator,
doorbell: Camera,
) -> None: ) -> None:
"""Test migrating unique ID of NVR to use MAC address.""" """Test Deprecate entity repair exists for existing installs."""
light1 = light.copy()
light1.name = "Test Light 1"
regenerate_device_ids(light1)
light2 = light.copy()
light2.name = "Test Light 2"
regenerate_device_ids(light2)
nvr = ufp.api.bootstrap.nvr
regenerate_device_ids(nvr)
registry = er.async_get(hass)
registry.async_get_or_create(
Platform.SENSOR,
DOMAIN,
f"{nvr.id}_storage_utilization",
config_entry=ufp.entry,
)
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
await init_entry(hass, ufp, [light1, light2], regenerate_ids=False)
assert ufp.entry.state == ConfigEntryState.LOADED
assert ufp.api.update.called
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
assert registry.async_get(f"{Platform.SENSOR}.{DOMAIN}_storage_utilization") is None
assert (
registry.async_get(f"{Platform.SENSOR}.{DOMAIN}_storage_utilization_2") is None
)
sensor = registry.async_get(
f"{Platform.SENSOR}.{DOMAIN}_{nvr.id}_storage_utilization"
)
assert sensor is not None
assert sensor.unique_id == f"{nvr.mac}_storage_utilization"
async def test_migrate_reboot_button_no_device(
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
) -> None:
"""Test migrating unique ID of reboot button if UniFi Protect device ID changed."""
light2_id, _ = generate_random_ids()
registry = er.async_get(hass) registry = er.async_get(hass)
registry.async_get_or_create( entry = registry.async_get_or_create(
Platform.BUTTON, DOMAIN, light2_id, config_entry=ufp.entry Platform.SWITCH,
DOMAIN,
f"{doorbell.mac}_hdr_mode",
config_entry=ufp.entry,
)
await _load_automation(hass, entry.entity_id)
await init_entry(hass, ufp, [doorbell])
await async_process_repairs_platforms(hass)
ws_client = await hass_ws_client(hass)
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
issue = None
for i in msg["result"]["issues"]:
if i["issue_id"] == "deprecate_hdr_switch":
issue = i
assert issue is not None
with patch(
"homeassistant.config.load_yaml_config_file",
autospec=True,
return_value={AUTOMATION_DOMAIN: []},
):
await hass.services.async_call(AUTOMATION_DOMAIN, SERVICE_RELOAD, blocking=True)
await hass.config_entries.async_reload(ufp.entry.entry_id)
await hass.async_block_till_done()
await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
issue = None
for i in msg["result"]["issues"]:
if i["issue_id"] == "deprecate_hdr_switch":
issue = i
assert issue is None
async def _load_script(hass: HomeAssistant, entity_id: str):
assert await async_setup_component(
hass,
SCRIPT_DOMAIN,
{
SCRIPT_DOMAIN: {
"test": {
"sequence": {
"service": "test.script",
"data": {"entity_id": entity_id},
}
}
},
},
) )
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
await init_entry(hass, ufp, [light], regenerate_ids=False)
assert ufp.entry.state == ConfigEntryState.LOADED async def test_deprecate_entity_script(
assert ufp.api.update.called hass: HomeAssistant,
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac ufp: MockUFPFixture,
hass_ws_client: WebSocketGenerator,
buttons = [ doorbell: Camera,
entity
for entity in er.async_entries_for_config_entry(registry, ufp.entry.entry_id)
if entity.domain == Platform.BUTTON.value
]
assert len(buttons) == 3
entity = registry.async_get(f"{Platform.BUTTON}.unifiprotect_{light2_id.lower()}")
assert entity is not None
assert entity.unique_id == light2_id
async def test_migrate_reboot_button_fail(
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
) -> None: ) -> None:
"""Test migrating unique ID of reboot button.""" """Test Deprecate entity repair exists for existing installs."""
registry = er.async_get(hass) registry = er.async_get(hass)
registry.async_get_or_create( entry = registry.async_get_or_create(
Platform.BUTTON, Platform.SWITCH,
DOMAIN, DOMAIN,
light.id, f"{doorbell.mac}_hdr_mode",
config_entry=ufp.entry, config_entry=ufp.entry,
suggested_object_id=light.display_name,
)
registry.async_get_or_create(
Platform.BUTTON,
DOMAIN,
f"{light.id}_reboot",
config_entry=ufp.entry,
suggested_object_id=light.display_name,
) )
await _load_script(hass, entry.entity_id)
await init_entry(hass, ufp, [doorbell])
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap) await async_process_repairs_platforms(hass)
await init_entry(hass, ufp, [light], regenerate_ids=False) ws_client = await hass_ws_client(hass)
assert ufp.entry.state == ConfigEntryState.LOADED await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
assert ufp.api.update.called msg = await ws_client.receive_json()
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
entity = registry.async_get(f"{Platform.BUTTON}.test_light") assert msg["success"]
assert entity is not None issue = None
assert entity.unique_id == f"{light.mac}" for i in msg["result"]["issues"]:
if i["issue_id"] == "deprecate_hdr_switch":
issue = i
assert issue is not None
with patch(
"homeassistant.config.load_yaml_config_file",
autospec=True,
return_value={SCRIPT_DOMAIN: {}},
):
await hass.services.async_call(SCRIPT_DOMAIN, SERVICE_RELOAD, blocking=True)
async def test_migrate_device_mac_button_fail( await hass.config_entries.async_reload(ufp.entry.entry_id)
hass: HomeAssistant, ufp: MockUFPFixture, light: Light await hass.async_block_till_done()
) -> None:
"""Test migrating unique ID to MAC format."""
registry = er.async_get(hass) await ws_client.send_json({"id": 2, "type": "repairs/list_issues"})
registry.async_get_or_create( msg = await ws_client.receive_json()
Platform.BUTTON,
DOMAIN,
f"{light.id}_reboot",
config_entry=ufp.entry,
suggested_object_id=light.display_name,
)
registry.async_get_or_create(
Platform.BUTTON,
DOMAIN,
f"{light.mac}_reboot",
config_entry=ufp.entry,
suggested_object_id=light.display_name,
)
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap) assert msg["success"]
await init_entry(hass, ufp, [light], regenerate_ids=False) issue = None
for i in msg["result"]["issues"]:
assert ufp.entry.state == ConfigEntryState.LOADED if i["issue_id"] == "deprecate_hdr_switch":
assert ufp.api.update.called issue = i
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac assert issue is None
entity = registry.async_get(f"{Platform.BUTTON}.test_light")
assert entity is not None
assert entity.unique_id == f"{light.id}_reboot"
async def test_migrate_device_mac_bootstrap_fail(
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
) -> None:
"""Test migrating with a network error."""
registry = er.async_get(hass)
registry.async_get_or_create(
Platform.BUTTON,
DOMAIN,
f"{light.id}_reboot",
config_entry=ufp.entry,
suggested_object_id=light.name,
)
registry.async_get_or_create(
Platform.BUTTON,
DOMAIN,
f"{light.mac}_reboot",
config_entry=ufp.entry,
suggested_object_id=light.name,
)
ufp.api.get_bootstrap = AsyncMock(side_effect=NvrError)
await init_entry(hass, ufp, [light], regenerate_ids=False)
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY

View File

@ -32,6 +32,8 @@ async def test_ea_warning_ignore(
) -> None: ) -> None:
"""Test EA warning is created if using prerelease version of Protect.""" """Test EA warning is created if using prerelease version of Protect."""
ufp.api.bootstrap.nvr.release_channel = "beta"
ufp.api.bootstrap.nvr.version = Version("1.21.0-beta.2")
version = ufp.api.bootstrap.nvr.version version = ufp.api.bootstrap.nvr.version
assert version.is_prerelease assert version.is_prerelease
await init_entry(hass, ufp, []) await init_entry(hass, ufp, [])
@ -92,6 +94,8 @@ async def test_ea_warning_fix(
) -> None: ) -> None:
"""Test EA warning is created if using prerelease version of Protect.""" """Test EA warning is created if using prerelease version of Protect."""
ufp.api.bootstrap.nvr.release_channel = "beta"
ufp.api.bootstrap.nvr.version = Version("1.21.0-beta.2")
version = ufp.api.bootstrap.nvr.version version = ufp.api.bootstrap.nvr.version
assert version.is_prerelease assert version.is_prerelease
await init_entry(hass, ufp, []) await init_entry(hass, ufp, [])
@ -125,8 +129,8 @@ async def test_ea_warning_fix(
assert data["step_id"] == "start" assert data["step_id"] == "start"
new_nvr = copy(ufp.api.bootstrap.nvr) new_nvr = copy(ufp.api.bootstrap.nvr)
new_nvr.version = Version("2.2.6")
new_nvr.release_channel = "release" new_nvr.release_channel = "release"
new_nvr.version = Version("2.2.6")
mock_msg = Mock() mock_msg = Mock()
mock_msg.changed_data = {"version": "2.2.6", "releaseChannel": "release"} mock_msg.changed_data = {"version": "2.2.6", "releaseChannel": "release"}
mock_msg.new_obj = new_nvr mock_msg.new_obj = new_nvr