mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Bump brother
backend library (#78072)
* Update integration for a new library * Update tests * Add unique_id migration * Update integration and tests for lib 2.0.0 * Improve test coverage * Improve typing in tests
This commit is contained in:
parent
3c6c673a20
commit
721fddc016
@ -5,12 +5,12 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from brother import Brother, DictToObj, SnmpError, UnsupportedModel
|
from brother import Brother, BrotherSensors, SnmpError, UnsupportedModel
|
||||||
import pysnmp.hlapi.asyncio as SnmpEngine
|
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, CONF_TYPE, Platform
|
from homeassistant.const import CONF_HOST, CONF_TYPE, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN, SNMP
|
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:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Brother from a config entry."""
|
"""Set up Brother from a config entry."""
|
||||||
host = entry.data[CONF_HOST]
|
host = entry.data[CONF_HOST]
|
||||||
kind = entry.data[CONF_TYPE]
|
printer_type = entry.data[CONF_TYPE]
|
||||||
|
|
||||||
snmp_engine = get_snmp_engine(hass)
|
snmp_engine = get_snmp_engine(hass)
|
||||||
|
try:
|
||||||
coordinator = BrotherDataUpdateCoordinator(
|
brother = await Brother.create(
|
||||||
hass, host=host, kind=kind, snmp_engine=snmp_engine
|
host, printer_type=printer_type, snmp_engine=snmp_engine
|
||||||
)
|
)
|
||||||
|
except (ConnectionError, SnmpError) as error:
|
||||||
|
raise ConfigEntryNotReady from error
|
||||||
|
|
||||||
|
coordinator = BrotherDataUpdateCoordinator(hass, brother)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
@ -61,11 +65,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
class BrotherDataUpdateCoordinator(DataUpdateCoordinator):
|
class BrotherDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
"""Class to manage fetching Brother data from the printer."""
|
"""Class to manage fetching Brother data from the printer."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, hass: HomeAssistant, brother: Brother) -> None:
|
||||||
self, hass: HomeAssistant, host: str, kind: str, snmp_engine: SnmpEngine
|
|
||||||
) -> None:
|
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self.brother = Brother(host, kind=kind, snmp_engine=snmp_engine)
|
self.brother = brother
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
@ -74,7 +76,7 @@ class BrotherDataUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
update_interval=SCAN_INTERVAL,
|
update_interval=SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update_data(self) -> DictToObj:
|
async def _async_update_data(self) -> BrotherSensors:
|
||||||
"""Update data via library."""
|
"""Update data via library."""
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(20):
|
async with async_timeout.timeout(20):
|
||||||
|
@ -46,7 +46,9 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
snmp_engine = get_snmp_engine(self.hass)
|
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 brother.async_update()
|
||||||
|
|
||||||
await self.async_set_unique_id(brother.serial.lower())
|
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")
|
model = discovery_info.properties.get("product")
|
||||||
|
|
||||||
try:
|
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()
|
await self.brother.async_update()
|
||||||
except UnsupportedModel:
|
except UnsupportedModel:
|
||||||
return self.async_abort(reason="unsupported_model")
|
return self.async_abort(reason="unsupported_model")
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Diagnostics support for Brother."""
|
"""Diagnostics support for Brother."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ async def async_get_config_entry_diagnostics(
|
|||||||
|
|
||||||
diagnostics_data = {
|
diagnostics_data = {
|
||||||
"info": dict(config_entry.data),
|
"info": dict(config_entry.data),
|
||||||
"data": coordinator.data,
|
"data": asdict(coordinator.data),
|
||||||
}
|
}
|
||||||
|
|
||||||
return diagnostics_data
|
return diagnostics_data
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Brother Printer",
|
"name": "Brother Printer",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/brother",
|
"documentation": "https://www.home-assistant.io/integrations/brother",
|
||||||
"codeowners": ["@bieniu"],
|
"codeowners": ["@bieniu"],
|
||||||
"requirements": ["brother==1.2.3"],
|
"requirements": ["brother==2.0.0"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_printer._tcp.local.",
|
"type": "_printer._tcp.local.",
|
||||||
|
@ -3,9 +3,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
|
DOMAIN as PLATFORM,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
@ -14,6 +16,7 @@ from homeassistant.components.sensor import (
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, PERCENTAGE
|
from homeassistant.const import CONF_HOST, PERCENTAGE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
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_DRUM_REMAINING_PAGES = "black_drum_remaining_pages"
|
||||||
ATTR_BLACK_INK_REMAINING = "black_ink_remaining"
|
ATTR_BLACK_INK_REMAINING = "black_ink_remaining"
|
||||||
ATTR_BLACK_TONER_REMAINING = "black_toner_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_COLOR_COUNTER = "color_counter"
|
||||||
ATTR_COUNTER = "counter"
|
ATTR_COUNTER = "counter"
|
||||||
ATTR_CYAN_DRUM_COUNTER = "cyan_drum_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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
@ -89,6 +94,22 @@ async def async_setup_entry(
|
|||||||
"""Add Brother entities from a config_entry."""
|
"""Add Brother entities from a config_entry."""
|
||||||
coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id]
|
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 = []
|
sensors = []
|
||||||
|
|
||||||
device_info = DeviceInfo(
|
device_info = DeviceInfo(
|
||||||
@ -97,11 +118,11 @@ async def async_setup_entry(
|
|||||||
manufacturer=ATTR_MANUFACTURER,
|
manufacturer=ATTR_MANUFACTURER,
|
||||||
model=coordinator.data.model,
|
model=coordinator.data.model,
|
||||||
name=coordinator.data.model,
|
name=coordinator.data.model,
|
||||||
sw_version=getattr(coordinator.data, "firmware", None),
|
sw_version=coordinator.data.firmware,
|
||||||
)
|
)
|
||||||
|
|
||||||
for description in SENSOR_TYPES:
|
for description in SENSOR_TYPES:
|
||||||
if description.key in coordinator.data:
|
if getattr(coordinator.data, description.key) is not None:
|
||||||
sensors.append(
|
sensors.append(
|
||||||
description.entity_class(coordinator, description, device_info)
|
description.entity_class(coordinator, description, device_info)
|
||||||
)
|
)
|
||||||
|
@ -449,7 +449,7 @@ boto3==1.20.24
|
|||||||
broadlink==0.18.2
|
broadlink==0.18.2
|
||||||
|
|
||||||
# homeassistant.components.brother
|
# homeassistant.components.brother
|
||||||
brother==1.2.3
|
brother==2.0.0
|
||||||
|
|
||||||
# homeassistant.components.brottsplatskartan
|
# homeassistant.components.brottsplatskartan
|
||||||
brottsplatskartan==0.0.1
|
brottsplatskartan==0.0.1
|
||||||
|
@ -356,7 +356,7 @@ boschshcpy==0.2.30
|
|||||||
broadlink==0.18.2
|
broadlink==0.18.2
|
||||||
|
|
||||||
# homeassistant.components.brother
|
# homeassistant.components.brother
|
||||||
brother==1.2.3
|
brother==2.0.0
|
||||||
|
|
||||||
# homeassistant.components.brunt
|
# homeassistant.components.brunt
|
||||||
brunt==1.2.0
|
brunt==1.2.0
|
||||||
|
@ -4,11 +4,14 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
from homeassistant.components.brother.const import DOMAIN
|
from homeassistant.components.brother.const import DOMAIN
|
||||||
from homeassistant.const import CONF_HOST, CONF_TYPE
|
from homeassistant.const import CONF_HOST, CONF_TYPE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
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."""
|
"""Set up the Brother integration in Home Assistant."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
@ -20,7 +23,7 @@ async def init_integration(hass, skip_setup=False) -> MockConfigEntry:
|
|||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
if not skip_setup:
|
if not skip_setup:
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
||||||
):
|
):
|
||||||
|
@ -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_life": 97,
|
||||||
"belt_unit_remaining_pages": 48436,
|
"belt_unit_remaining_pages": 48436,
|
||||||
"black_drum_counter": 1611,
|
"black_drum_counter": 1611,
|
||||||
@ -12,6 +17,9 @@
|
|||||||
"cyan_drum_counter": 1611,
|
"cyan_drum_counter": 1611,
|
||||||
"cyan_drum_remaining_life": 92,
|
"cyan_drum_remaining_life": 92,
|
||||||
"cyan_drum_remaining_pages": 16389,
|
"cyan_drum_remaining_pages": 16389,
|
||||||
|
"cyan_ink": null,
|
||||||
|
"cyan_ink_remaining": null,
|
||||||
|
"cyan_ink_status": null,
|
||||||
"cyan_toner": 10,
|
"cyan_toner": 10,
|
||||||
"cyan_toner_remaining": 10,
|
"cyan_toner_remaining": 10,
|
||||||
"cyan_toner_status": 1,
|
"cyan_toner_status": 1,
|
||||||
@ -22,10 +30,17 @@
|
|||||||
"duplex_unit_pages_counter": 538,
|
"duplex_unit_pages_counter": 538,
|
||||||
"firmware": "1.17",
|
"firmware": "1.17",
|
||||||
"fuser_remaining_life": 97,
|
"fuser_remaining_life": 97,
|
||||||
|
"fuser_unit_remaining_pages": null,
|
||||||
|
"image_counter": null,
|
||||||
|
"laser_remaining_life": null,
|
||||||
"laser_unit_remaining_pages": 48389,
|
"laser_unit_remaining_pages": 48389,
|
||||||
|
"magenta_counter": null,
|
||||||
"magenta_drum_counter": 1611,
|
"magenta_drum_counter": 1611,
|
||||||
"magenta_drum_remaining_life": 92,
|
"magenta_drum_remaining_life": 92,
|
||||||
"magenta_drum_remaining_pages": 16389,
|
"magenta_drum_remaining_pages": 16389,
|
||||||
|
"magenta_ink": null,
|
||||||
|
"magenta_ink_remaining": null,
|
||||||
|
"magenta_ink_status": null,
|
||||||
"magenta_toner": 10,
|
"magenta_toner": 10,
|
||||||
"magenta_toner_remaining": 8,
|
"magenta_toner_remaining": 8,
|
||||||
"magenta_toner_status": 2,
|
"magenta_toner_status": 2,
|
||||||
@ -33,12 +48,18 @@
|
|||||||
"page_counter": 986,
|
"page_counter": 986,
|
||||||
"pf_kit_1_remaining_life": 98,
|
"pf_kit_1_remaining_life": 98,
|
||||||
"pf_kit_1_remaining_pages": 48741,
|
"pf_kit_1_remaining_pages": 48741,
|
||||||
|
"pf_kit_mp_remaining_life": null,
|
||||||
|
"pf_kit_mp_remaining_pages": null,
|
||||||
"serial": "0123456789",
|
"serial": "0123456789",
|
||||||
"status": "waiting",
|
"status": "waiting",
|
||||||
"uptime": "2019-09-24T12:14:56+00:00",
|
"uptime": "2019-09-24T12:14:56+00:00",
|
||||||
|
"yellow_counter": null,
|
||||||
"yellow_drum_counter": 1611,
|
"yellow_drum_counter": 1611,
|
||||||
"yellow_drum_remaining_life": 92,
|
"yellow_drum_remaining_life": 92,
|
||||||
"yellow_drum_remaining_pages": 16389,
|
"yellow_drum_remaining_pages": 16389,
|
||||||
|
"yellow_ink": null,
|
||||||
|
"yellow_ink_remaining": null,
|
||||||
|
"yellow_ink_status": null,
|
||||||
"yellow_toner": 10,
|
"yellow_toner": 10,
|
||||||
"yellow_toner_remaining": 2,
|
"yellow_toner_remaining": 2,
|
||||||
"yellow_toner_status": 2
|
"yellow_toner_status": 2
|
||||||
|
@ -9,13 +9,14 @@ from homeassistant.components import zeroconf
|
|||||||
from homeassistant.components.brother.const import DOMAIN
|
from homeassistant.components.brother.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
||||||
from homeassistant.const import CONF_HOST, CONF_TYPE
|
from homeassistant.const import CONF_HOST, CONF_TYPE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import MockConfigEntry, load_fixture
|
||||||
|
|
||||||
CONFIG = {CONF_HOST: "127.0.0.1", CONF_TYPE: "laser"}
|
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."""
|
"""Test that the form is served with no input."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
@ -25,9 +26,9 @@ async def test_show_form(hass):
|
|||||||
assert result["step_id"] == SOURCE_USER
|
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."""
|
"""Test that the user step works with printer hostname."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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"
|
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."""
|
"""Test that the user step works with printer IPv4 address."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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"
|
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."""
|
"""Test that the user step works with printer IPv6 address."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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"
|
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."""
|
"""Test invalid hostname in user_input."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -88,9 +89,11 @@ async def test_invalid_hostname(hass):
|
|||||||
assert result["errors"] == {CONF_HOST: "wrong_host"}
|
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."""
|
"""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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
||||||
)
|
)
|
||||||
@ -98,9 +101,11 @@ async def test_connection_error(hass):
|
|||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
async def test_snmp_error(hass):
|
async def test_snmp_error(hass: HomeAssistant) -> None:
|
||||||
"""Test SNMP error."""
|
"""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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
||||||
)
|
)
|
||||||
@ -108,9 +113,11 @@ async def test_snmp_error(hass):
|
|||||||
assert result["errors"] == {"base": "snmp_error"}
|
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."""
|
"""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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
|
||||||
@ -120,9 +127,9 @@ async def test_unsupported_model_error(hass):
|
|||||||
assert result["reason"] == "unsupported_model"
|
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."""
|
"""Test we abort config flow if Brother printer already configured."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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"
|
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."""
|
"""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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -159,9 +168,11 @@ async def test_zeroconf_snmp_error(hass):
|
|||||||
assert result["reason"] == "cannot_connect"
|
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."""
|
"""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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_ZEROCONF},
|
context={"source": SOURCE_ZEROCONF},
|
||||||
@ -181,9 +192,9 @@ async def test_zeroconf_unsupported_model(hass):
|
|||||||
assert len(mock_get_data.mock_calls) == 0
|
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."""
|
"""Test we abort zeroconf flow if Brother printer already configured."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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"
|
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."""
|
"""Test we do not probe the device is the host is already configured."""
|
||||||
entry = MockConfigEntry(domain=DOMAIN, unique_id="0123456789", data=CONFIG)
|
entry = MockConfigEntry(domain=DOMAIN, unique_id="0123456789", data=CONFIG)
|
||||||
entry.add_to_hass(hass)
|
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(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_ZEROCONF},
|
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
|
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."""
|
"""Test zeroconf confirmation and create config entry."""
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
||||||
):
|
):
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
"""Test Brother diagnostics."""
|
"""Test Brother diagnostics."""
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.util.dt import UTC
|
from homeassistant.util.dt import UTC
|
||||||
|
|
||||||
from tests.common import load_fixture
|
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
|
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."""
|
"""Test config entry diagnostics."""
|
||||||
entry = await init_integration(hass, skip_setup=True)
|
entry = await init_integration(hass, skip_setup=True)
|
||||||
|
|
||||||
diagnostics_data = json.loads(load_fixture("diagnostics_data.json", "brother"))
|
diagnostics_data = json.loads(load_fixture("diagnostics_data.json", "brother"))
|
||||||
test_time = datetime(2019, 11, 11, 9, 10, 32, tzinfo=UTC)
|
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",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
||||||
):
|
):
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
"""Test init of Brother integration."""
|
"""Test init of Brother integration."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from brother import SnmpError
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.brother.const import DOMAIN
|
from homeassistant.components.brother.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import CONF_HOST, CONF_TYPE, STATE_UNAVAILABLE
|
from homeassistant.const import CONF_HOST, CONF_TYPE, STATE_UNAVAILABLE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.brother import init_integration
|
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."""
|
"""Test a successful setup entry."""
|
||||||
await init_integration(hass)
|
await init_integration(hass)
|
||||||
|
|
||||||
@ -19,7 +23,7 @@ async def test_async_setup_entry(hass):
|
|||||||
assert state.state == "waiting"
|
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."""
|
"""Test for setup failure if connection to broker is missing."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
@ -28,13 +32,31 @@ async def test_config_not_ready(hass):
|
|||||||
data={CONF_HOST: "localhost", CONF_TYPE: "laser"},
|
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)
|
entry.add_to_hass(hass)
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
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."""
|
"""Test successful unload of entry."""
|
||||||
entry = await init_integration(hass)
|
entry = await init_integration(hass)
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ from homeassistant.const import (
|
|||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
|
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 homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.dt import UTC, utcnow
|
from homeassistant.util.dt import UTC, utcnow
|
||||||
@ -30,7 +31,7 @@ ATTR_REMAINING_PAGES = "remaining_pages"
|
|||||||
ATTR_COUNTER = "counter"
|
ATTR_COUNTER = "counter"
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass: HomeAssistant) -> None:
|
||||||
"""Test states of the sensors."""
|
"""Test states of the sensors."""
|
||||||
entry = await init_integration(hass, skip_setup=True)
|
entry = await init_integration(hass, skip_setup=True)
|
||||||
|
|
||||||
@ -45,7 +46,9 @@ async def test_sensors(hass):
|
|||||||
disabled_by=None,
|
disabled_by=None,
|
||||||
)
|
)
|
||||||
test_time = datetime(2019, 11, 11, 9, 10, 32, tzinfo=UTC)
|
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",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
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")
|
entry = registry.async_get("sensor.hl_l2340dw_b_w_counter")
|
||||||
assert entry
|
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")
|
state = hass.states.get("sensor.hl_l2340dw_color_counter")
|
||||||
assert state
|
assert state
|
||||||
@ -276,7 +279,7 @@ async def test_disabled_by_default_sensors(hass):
|
|||||||
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
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."""
|
"""Ensure that we mark the entities unavailable correctly when device is offline."""
|
||||||
await init_integration(hass)
|
await init_integration(hass)
|
||||||
|
|
||||||
@ -286,7 +289,9 @@ async def test_availability(hass):
|
|||||||
assert state.state == "waiting"
|
assert state.state == "waiting"
|
||||||
|
|
||||||
future = utcnow() + timedelta(minutes=5)
|
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)
|
async_fire_time_changed(hass, future)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
@ -295,7 +300,7 @@ async def test_availability(hass):
|
|||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
future = utcnow() + timedelta(minutes=10)
|
future = utcnow() + timedelta(minutes=10)
|
||||||
with patch(
|
with patch("brother.Brother.initialize"), patch(
|
||||||
"brother.Brother._get_data",
|
"brother.Brother._get_data",
|
||||||
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
return_value=json.loads(load_fixture("printer_data.json", "brother")),
|
||||||
):
|
):
|
||||||
@ -308,7 +313,7 @@ async def test_availability(hass):
|
|||||||
assert state.state == "waiting"
|
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."""
|
"""Test manual update entity via service homeassistant/update_entity."""
|
||||||
await init_integration(hass)
|
await init_integration(hass)
|
||||||
|
|
||||||
@ -326,3 +331,22 @@ async def test_manual_update_entity(hass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert len(mock_update.mock_calls) == 1
|
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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user