diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index 7c3d3003ea6..c1dbfd5bf0a 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -5,12 +5,12 @@ from datetime import timedelta import logging import async_timeout -from brother import Brother, DictToObj, SnmpError, UnsupportedModel -import pysnmp.hlapi.asyncio as SnmpEngine +from brother import Brother, BrotherSensors, SnmpError, UnsupportedModel from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_TYPE, Platform from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DATA_CONFIG_ENTRY, DOMAIN, SNMP @@ -26,13 +26,17 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Brother from a config entry.""" host = entry.data[CONF_HOST] - kind = entry.data[CONF_TYPE] + printer_type = entry.data[CONF_TYPE] snmp_engine = get_snmp_engine(hass) + try: + brother = await Brother.create( + host, printer_type=printer_type, snmp_engine=snmp_engine + ) + except (ConnectionError, SnmpError) as error: + raise ConfigEntryNotReady from error - coordinator = BrotherDataUpdateCoordinator( - hass, host=host, kind=kind, snmp_engine=snmp_engine - ) + coordinator = BrotherDataUpdateCoordinator(hass, brother) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {}) @@ -61,11 +65,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class BrotherDataUpdateCoordinator(DataUpdateCoordinator): """Class to manage fetching Brother data from the printer.""" - def __init__( - self, hass: HomeAssistant, host: str, kind: str, snmp_engine: SnmpEngine - ) -> None: + def __init__(self, hass: HomeAssistant, brother: Brother) -> None: """Initialize.""" - self.brother = Brother(host, kind=kind, snmp_engine=snmp_engine) + self.brother = brother super().__init__( hass, @@ -74,7 +76,7 @@ class BrotherDataUpdateCoordinator(DataUpdateCoordinator): update_interval=SCAN_INTERVAL, ) - async def _async_update_data(self) -> DictToObj: + async def _async_update_data(self) -> BrotherSensors: """Update data via library.""" try: async with async_timeout.timeout(20): diff --git a/homeassistant/components/brother/config_flow.py b/homeassistant/components/brother/config_flow.py index 27f3c73cd63..48c73d0c4d1 100644 --- a/homeassistant/components/brother/config_flow.py +++ b/homeassistant/components/brother/config_flow.py @@ -46,7 +46,9 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): snmp_engine = get_snmp_engine(self.hass) - brother = Brother(user_input[CONF_HOST], snmp_engine=snmp_engine) + brother = await Brother.create( + user_input[CONF_HOST], snmp_engine=snmp_engine + ) await brother.async_update() await self.async_set_unique_id(brother.serial.lower()) @@ -80,7 +82,9 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): model = discovery_info.properties.get("product") try: - self.brother = Brother(self.host, snmp_engine=snmp_engine, model=model) + self.brother = await Brother.create( + self.host, snmp_engine=snmp_engine, model=model + ) await self.brother.async_update() except UnsupportedModel: return self.async_abort(reason="unsupported_model") diff --git a/homeassistant/components/brother/diagnostics.py b/homeassistant/components/brother/diagnostics.py index 9ff515004cb..239d4916d6b 100644 --- a/homeassistant/components/brother/diagnostics.py +++ b/homeassistant/components/brother/diagnostics.py @@ -1,6 +1,8 @@ """Diagnostics support for Brother.""" from __future__ import annotations +from dataclasses import asdict + from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -18,7 +20,7 @@ async def async_get_config_entry_diagnostics( diagnostics_data = { "info": dict(config_entry.data), - "data": coordinator.data, + "data": asdict(coordinator.data), } return diagnostics_data diff --git a/homeassistant/components/brother/manifest.json b/homeassistant/components/brother/manifest.json index e14079f6dd9..61b1d8bcdc9 100644 --- a/homeassistant/components/brother/manifest.json +++ b/homeassistant/components/brother/manifest.json @@ -3,7 +3,7 @@ "name": "Brother Printer", "documentation": "https://www.home-assistant.io/integrations/brother", "codeowners": ["@bieniu"], - "requirements": ["brother==1.2.3"], + "requirements": ["brother==2.0.0"], "zeroconf": [ { "type": "_printer._tcp.local.", diff --git a/homeassistant/components/brother/sensor.py b/homeassistant/components/brother/sensor.py index b6af96087af..86f1d2d40ec 100644 --- a/homeassistant/components/brother/sensor.py +++ b/homeassistant/components/brother/sensor.py @@ -3,9 +3,11 @@ from __future__ import annotations from dataclasses import dataclass from datetime import datetime +import logging from typing import Any, cast from homeassistant.components.sensor import ( + DOMAIN as PLATFORM, SensorDeviceClass, SensorEntity, SensorEntityDescription, @@ -14,6 +16,7 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, PERCENTAGE from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -28,7 +31,7 @@ ATTR_BLACK_DRUM_REMAINING_LIFE = "black_drum_remaining_life" ATTR_BLACK_DRUM_REMAINING_PAGES = "black_drum_remaining_pages" ATTR_BLACK_INK_REMAINING = "black_ink_remaining" ATTR_BLACK_TONER_REMAINING = "black_toner_remaining" -ATTR_BW_COUNTER = "b/w_counter" +ATTR_BW_COUNTER = "bw_counter" ATTR_COLOR_COUNTER = "color_counter" ATTR_COUNTER = "counter" ATTR_CYAN_DRUM_COUNTER = "cyan_drum_counter" @@ -82,6 +85,8 @@ ATTRS_MAP: dict[str, tuple[str, str]] = { ), } +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -89,6 +94,22 @@ async def async_setup_entry( """Add Brother entities from a config_entry.""" coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] + # Due to the change of the attribute name of one sensor, it is necessary to migrate + # the unique_id to the new one. + entity_registry = er.async_get(hass) + old_unique_id = f"{coordinator.data.serial.lower()}_b/w_counter" + if entity_id := entity_registry.async_get_entity_id( + PLATFORM, DOMAIN, old_unique_id + ): + new_unique_id = f"{coordinator.data.serial.lower()}_bw_counter" + _LOGGER.debug( + "Migrating entity %s from old unique ID '%s' to new unique ID '%s'", + entity_id, + old_unique_id, + new_unique_id, + ) + entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id) + sensors = [] device_info = DeviceInfo( @@ -97,11 +118,11 @@ async def async_setup_entry( manufacturer=ATTR_MANUFACTURER, model=coordinator.data.model, name=coordinator.data.model, - sw_version=getattr(coordinator.data, "firmware", None), + sw_version=coordinator.data.firmware, ) for description in SENSOR_TYPES: - if description.key in coordinator.data: + if getattr(coordinator.data, description.key) is not None: sensors.append( description.entity_class(coordinator, description, device_info) ) diff --git a/requirements_all.txt b/requirements_all.txt index 8d0bd4ff995..32ac27e2fbb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -449,7 +449,7 @@ boto3==1.20.24 broadlink==0.18.2 # homeassistant.components.brother -brother==1.2.3 +brother==2.0.0 # homeassistant.components.brottsplatskartan brottsplatskartan==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bdba84bb811..8ad195aafc6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -356,7 +356,7 @@ boschshcpy==0.2.30 broadlink==0.18.2 # homeassistant.components.brother -brother==1.2.3 +brother==2.0.0 # homeassistant.components.brunt brunt==1.2.0 diff --git a/tests/components/brother/__init__.py b/tests/components/brother/__init__.py index 57cbe8b71de..8e24c2d8058 100644 --- a/tests/components/brother/__init__.py +++ b/tests/components/brother/__init__.py @@ -4,11 +4,14 @@ from unittest.mock import patch from homeassistant.components.brother.const import DOMAIN from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, load_fixture -async def init_integration(hass, skip_setup=False) -> MockConfigEntry: +async def init_integration( + hass: HomeAssistant, skip_setup: bool = False +) -> MockConfigEntry: """Set up the Brother integration in Home Assistant.""" entry = MockConfigEntry( domain=DOMAIN, @@ -20,7 +23,7 @@ async def init_integration(hass, skip_setup=False) -> MockConfigEntry: entry.add_to_hass(hass) if not skip_setup: - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): diff --git a/tests/components/brother/fixtures/diagnostics_data.json b/tests/components/brother/fixtures/diagnostics_data.json index 0199acdd722..fd22f861e8d 100644 --- a/tests/components/brother/fixtures/diagnostics_data.json +++ b/tests/components/brother/fixtures/diagnostics_data.json @@ -1,5 +1,10 @@ { - "b/w_counter": 709, + "black_counter": null, + "black_ink": null, + "black_ink_remaining": null, + "black_ink_status": null, + "cyan_counter": null, + "bw_counter": 709, "belt_unit_remaining_life": 97, "belt_unit_remaining_pages": 48436, "black_drum_counter": 1611, @@ -12,6 +17,9 @@ "cyan_drum_counter": 1611, "cyan_drum_remaining_life": 92, "cyan_drum_remaining_pages": 16389, + "cyan_ink": null, + "cyan_ink_remaining": null, + "cyan_ink_status": null, "cyan_toner": 10, "cyan_toner_remaining": 10, "cyan_toner_status": 1, @@ -22,10 +30,17 @@ "duplex_unit_pages_counter": 538, "firmware": "1.17", "fuser_remaining_life": 97, + "fuser_unit_remaining_pages": null, + "image_counter": null, + "laser_remaining_life": null, "laser_unit_remaining_pages": 48389, + "magenta_counter": null, "magenta_drum_counter": 1611, "magenta_drum_remaining_life": 92, "magenta_drum_remaining_pages": 16389, + "magenta_ink": null, + "magenta_ink_remaining": null, + "magenta_ink_status": null, "magenta_toner": 10, "magenta_toner_remaining": 8, "magenta_toner_status": 2, @@ -33,12 +48,18 @@ "page_counter": 986, "pf_kit_1_remaining_life": 98, "pf_kit_1_remaining_pages": 48741, + "pf_kit_mp_remaining_life": null, + "pf_kit_mp_remaining_pages": null, "serial": "0123456789", "status": "waiting", "uptime": "2019-09-24T12:14:56+00:00", + "yellow_counter": null, "yellow_drum_counter": 1611, "yellow_drum_remaining_life": 92, "yellow_drum_remaining_pages": 16389, + "yellow_ink": null, + "yellow_ink_remaining": null, + "yellow_ink_status": null, "yellow_toner": 10, "yellow_toner_remaining": 2, "yellow_toner_status": 2 diff --git a/tests/components/brother/test_config_flow.py b/tests/components/brother/test_config_flow.py index 60f29dbd901..5a145edf030 100644 --- a/tests/components/brother/test_config_flow.py +++ b/tests/components/brother/test_config_flow.py @@ -9,13 +9,14 @@ from homeassistant.components import zeroconf from homeassistant.components.brother.const import DOMAIN from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, load_fixture CONFIG = {CONF_HOST: "127.0.0.1", CONF_TYPE: "laser"} -async def test_show_form(hass): +async def test_show_form(hass: HomeAssistant) -> None: """Test that the form is served with no input.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -25,9 +26,9 @@ async def test_show_form(hass): assert result["step_id"] == SOURCE_USER -async def test_create_entry_with_hostname(hass): +async def test_create_entry_with_hostname(hass: HomeAssistant) -> None: """Test that the user step works with printer hostname.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -43,9 +44,9 @@ async def test_create_entry_with_hostname(hass): assert result["data"][CONF_TYPE] == "laser" -async def test_create_entry_with_ipv4_address(hass): +async def test_create_entry_with_ipv4_address(hass: HomeAssistant) -> None: """Test that the user step works with printer IPv4 address.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -59,9 +60,9 @@ async def test_create_entry_with_ipv4_address(hass): assert result["data"][CONF_TYPE] == "laser" -async def test_create_entry_with_ipv6_address(hass): +async def test_create_entry_with_ipv6_address(hass: HomeAssistant) -> None: """Test that the user step works with printer IPv6 address.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -77,7 +78,7 @@ async def test_create_entry_with_ipv6_address(hass): assert result["data"][CONF_TYPE] == "laser" -async def test_invalid_hostname(hass): +async def test_invalid_hostname(hass: HomeAssistant) -> None: """Test invalid hostname in user_input.""" result = await hass.config_entries.flow.async_init( DOMAIN, @@ -88,9 +89,11 @@ async def test_invalid_hostname(hass): assert result["errors"] == {CONF_HOST: "wrong_host"} -async def test_connection_error(hass): +async def test_connection_error(hass: HomeAssistant) -> None: """Test connection to host error.""" - with patch("brother.Brother._get_data", side_effect=ConnectionError()): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=ConnectionError() + ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG ) @@ -98,9 +101,11 @@ async def test_connection_error(hass): assert result["errors"] == {"base": "cannot_connect"} -async def test_snmp_error(hass): +async def test_snmp_error(hass: HomeAssistant) -> None: """Test SNMP error.""" - with patch("brother.Brother._get_data", side_effect=SnmpError("error")): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=SnmpError("error") + ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG ) @@ -108,9 +113,11 @@ async def test_snmp_error(hass): assert result["errors"] == {"base": "snmp_error"} -async def test_unsupported_model_error(hass): +async def test_unsupported_model_error(hass: HomeAssistant) -> None: """Test unsupported printer model error.""" - with patch("brother.Brother._get_data", side_effect=UnsupportedModel("error")): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=UnsupportedModel("error") + ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG @@ -120,9 +127,9 @@ async def test_unsupported_model_error(hass): assert result["reason"] == "unsupported_model" -async def test_device_exists_abort(hass): +async def test_device_exists_abort(hass: HomeAssistant) -> None: """Test we abort config flow if Brother printer already configured.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -137,9 +144,11 @@ async def test_device_exists_abort(hass): assert result["reason"] == "already_configured" -async def test_zeroconf_snmp_error(hass): +async def test_zeroconf_snmp_error(hass: HomeAssistant) -> None: """Test we abort zeroconf flow on SNMP error.""" - with patch("brother.Brother._get_data", side_effect=SnmpError("error")): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=SnmpError("error") + ): result = await hass.config_entries.flow.async_init( DOMAIN, @@ -159,9 +168,11 @@ async def test_zeroconf_snmp_error(hass): assert result["reason"] == "cannot_connect" -async def test_zeroconf_unsupported_model(hass): +async def test_zeroconf_unsupported_model(hass: HomeAssistant) -> None: """Test unsupported printer model error.""" - with patch("brother.Brother._get_data") as mock_get_data: + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data" + ) as mock_get_data: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, @@ -181,9 +192,9 @@ async def test_zeroconf_unsupported_model(hass): assert len(mock_get_data.mock_calls) == 0 -async def test_zeroconf_device_exists_abort(hass): +async def test_zeroconf_device_exists_abort(hass: HomeAssistant) -> None: """Test we abort zeroconf flow if Brother printer already configured.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -215,11 +226,13 @@ async def test_zeroconf_device_exists_abort(hass): assert entry.data["host"] == "127.0.0.1" -async def test_zeroconf_no_probe_existing_device(hass): +async def test_zeroconf_no_probe_existing_device(hass: HomeAssistant) -> None: """Test we do not probe the device is the host is already configured.""" entry = MockConfigEntry(domain=DOMAIN, unique_id="0123456789", data=CONFIG) entry.add_to_hass(hass) - with patch("brother.Brother._get_data") as mock_get_data: + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data" + ) as mock_get_data: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, @@ -240,9 +253,9 @@ async def test_zeroconf_no_probe_existing_device(hass): assert len(mock_get_data.mock_calls) == 0 -async def test_zeroconf_confirm_create_entry(hass): +async def test_zeroconf_confirm_create_entry(hass: HomeAssistant) -> None: """Test zeroconf confirmation and create config entry.""" - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): diff --git a/tests/components/brother/test_diagnostics.py b/tests/components/brother/test_diagnostics.py index 7e25ffaa4f0..73b3b6dda7a 100644 --- a/tests/components/brother/test_diagnostics.py +++ b/tests/components/brother/test_diagnostics.py @@ -1,8 +1,12 @@ """Test Brother diagnostics.""" +from collections.abc import Awaitable, Callable from datetime import datetime import json from unittest.mock import Mock, patch +from aiohttp import ClientSession + +from homeassistant.core import HomeAssistant from homeassistant.util.dt import UTC from tests.common import load_fixture @@ -10,13 +14,17 @@ from tests.components.brother import init_integration from tests.components.diagnostics import get_diagnostics_for_config_entry -async def test_entry_diagnostics(hass, hass_client): +async def test_entry_diagnostics( + hass: HomeAssistant, hass_client: Callable[..., Awaitable[ClientSession]] +) -> None: """Test config entry diagnostics.""" entry = await init_integration(hass, skip_setup=True) diagnostics_data = json.loads(load_fixture("diagnostics_data.json", "brother")) test_time = datetime(2019, 11, 11, 9, 10, 32, tzinfo=UTC) - with patch("brother.datetime", utcnow=Mock(return_value=test_time)), patch( + with patch("brother.Brother.initialize"), patch( + "brother.datetime", utcnow=Mock(return_value=test_time) + ), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): diff --git a/tests/components/brother/test_init.py b/tests/components/brother/test_init.py index 76b999c3e54..b034259974a 100644 --- a/tests/components/brother/test_init.py +++ b/tests/components/brother/test_init.py @@ -1,15 +1,19 @@ """Test init of Brother integration.""" from unittest.mock import patch +from brother import SnmpError +import pytest + from homeassistant.components.brother.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_HOST, CONF_TYPE, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry from tests.components.brother import init_integration -async def test_async_setup_entry(hass): +async def test_async_setup_entry(hass: HomeAssistant) -> None: """Test a successful setup entry.""" await init_integration(hass) @@ -19,7 +23,7 @@ async def test_async_setup_entry(hass): assert state.state == "waiting" -async def test_config_not_ready(hass): +async def test_config_not_ready(hass: HomeAssistant) -> None: """Test for setup failure if connection to broker is missing.""" entry = MockConfigEntry( domain=DOMAIN, @@ -28,13 +32,31 @@ async def test_config_not_ready(hass): data={CONF_HOST: "localhost", CONF_TYPE: "laser"}, ) - with patch("brother.Brother._get_data", side_effect=ConnectionError()): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=ConnectionError() + ): entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_RETRY -async def test_unload_entry(hass): +@pytest.mark.parametrize("exc", [(SnmpError("SNMP Error")), (ConnectionError)]) +async def test_error_on_init(hass: HomeAssistant, exc: Exception) -> None: + """Test for error on init.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="HL-L2340DW 0123456789", + unique_id="0123456789", + data={CONF_HOST: "localhost", CONF_TYPE: "laser"}, + ) + + with patch("brother.Brother.initialize", side_effect=exc): + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + assert entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_unload_entry(hass: HomeAssistant) -> None: """Test successful unload of entry.""" entry = await init_integration(hass) diff --git a/tests/components/brother/test_sensor.py b/tests/components/brother/test_sensor.py index 7ef87a2e396..f50104c0b3a 100644 --- a/tests/components/brother/test_sensor.py +++ b/tests/components/brother/test_sensor.py @@ -19,6 +19,7 @@ from homeassistant.const import ( PERCENTAGE, STATE_UNAVAILABLE, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component from homeassistant.util.dt import UTC, utcnow @@ -30,7 +31,7 @@ ATTR_REMAINING_PAGES = "remaining_pages" ATTR_COUNTER = "counter" -async def test_sensors(hass): +async def test_sensors(hass: HomeAssistant) -> None: """Test states of the sensors.""" entry = await init_integration(hass, skip_setup=True) @@ -45,7 +46,9 @@ async def test_sensors(hass): disabled_by=None, ) test_time = datetime(2019, 11, 11, 9, 10, 32, tzinfo=UTC) - with patch("brother.datetime", utcnow=Mock(return_value=test_time)), patch( + with patch("brother.Brother.initialize"), patch( + "brother.datetime", utcnow=Mock(return_value=test_time) + ), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -235,7 +238,7 @@ async def test_sensors(hass): entry = registry.async_get("sensor.hl_l2340dw_b_w_counter") assert entry - assert entry.unique_id == "0123456789_b/w_counter" + assert entry.unique_id == "0123456789_bw_counter" state = hass.states.get("sensor.hl_l2340dw_color_counter") assert state @@ -276,7 +279,7 @@ async def test_disabled_by_default_sensors(hass): assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION -async def test_availability(hass): +async def test_availability(hass: HomeAssistant) -> None: """Ensure that we mark the entities unavailable correctly when device is offline.""" await init_integration(hass) @@ -286,7 +289,9 @@ async def test_availability(hass): assert state.state == "waiting" future = utcnow() + timedelta(minutes=5) - with patch("brother.Brother._get_data", side_effect=ConnectionError()): + with patch("brother.Brother.initialize"), patch( + "brother.Brother._get_data", side_effect=ConnectionError() + ): async_fire_time_changed(hass, future) await hass.async_block_till_done() @@ -295,7 +300,7 @@ async def test_availability(hass): assert state.state == STATE_UNAVAILABLE future = utcnow() + timedelta(minutes=10) - with patch( + with patch("brother.Brother.initialize"), patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("printer_data.json", "brother")), ): @@ -308,7 +313,7 @@ async def test_availability(hass): assert state.state == "waiting" -async def test_manual_update_entity(hass): +async def test_manual_update_entity(hass: HomeAssistant) -> None: """Test manual update entity via service homeassistant/update_entity.""" await init_integration(hass) @@ -326,3 +331,22 @@ async def test_manual_update_entity(hass): ) assert len(mock_update.mock_calls) == 1 + + +async def test_unique_id_migration(hass: HomeAssistant) -> None: + """Test states of the unique_id migration.""" + registry = er.async_get(hass) + + registry.async_get_or_create( + SENSOR_DOMAIN, + DOMAIN, + "0123456789_b/w_counter", + suggested_object_id="hl_l2340dw_b_w_counter", + disabled_by=None, + ) + + await init_integration(hass) + + entry = registry.async_get("sensor.hl_l2340dw_b_w_counter") + assert entry + assert entry.unique_id == "0123456789_bw_counter"