diff --git a/.strict-typing b/.strict-typing index ab150056a85e..b708b3615045 100644 --- a/.strict-typing +++ b/.strict-typing @@ -6,6 +6,7 @@ homeassistant.components homeassistant.components.automation.* homeassistant.components.binary_sensor.* homeassistant.components.bond.* +homeassistant.components.brother.* homeassistant.components.calendar.* homeassistant.components.cover.* homeassistant.components.device_automation.* diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index b4994688cf4b..45053c74f03c 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -1,8 +1,11 @@ """The Brother component.""" +from __future__ import annotations + from datetime import timedelta import logging -from brother import Brother, SnmpError, UnsupportedModel +from brother import Brother, DictToObj, SnmpError, UnsupportedModel +import pysnmp.hlapi.asyncio as SnmpEngine from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_TYPE @@ -19,7 +22,7 @@ SCAN_INTERVAL = timedelta(seconds=30) _LOGGER = logging.getLogger(__name__) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): +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] @@ -41,7 +44,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) @@ -57,7 +60,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): class BrotherDataUpdateCoordinator(DataUpdateCoordinator): """Class to manage fetching Brother data from the printer.""" - def __init__(self, hass, host, kind, snmp_engine): + def __init__( + self, hass: HomeAssistant, host: str, kind: str, snmp_engine: SnmpEngine + ) -> None: """Initialize.""" self.brother = Brother(host, kind=kind, snmp_engine=snmp_engine) @@ -68,7 +73,7 @@ class BrotherDataUpdateCoordinator(DataUpdateCoordinator): update_interval=SCAN_INTERVAL, ) - async def _async_update_data(self): + async def _async_update_data(self) -> DictToObj: """Update data via library.""" try: data = await self.brother.async_update() diff --git a/homeassistant/components/brother/config_flow.py b/homeassistant/components/brother/config_flow.py index 1d635984b722..0b6df361b232 100644 --- a/homeassistant/components/brother/config_flow.py +++ b/homeassistant/components/brother/config_flow.py @@ -1,12 +1,17 @@ """Adds config flow for Brother Printer.""" +from __future__ import annotations + import ipaddress import re +from typing import Any from brother import Brother, SnmpError, UnsupportedModel import voluptuous as vol from homeassistant import config_entries, exceptions from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.typing import DiscoveryInfoType from .const import DOMAIN, PRINTER_TYPES from .utils import get_snmp_engine @@ -19,14 +24,15 @@ DATA_SCHEMA = vol.Schema( ) -def host_valid(host): +def host_valid(host: str) -> bool: """Return True if hostname or IP address is valid.""" try: - if ipaddress.ip_address(host).version == (4 or 6): + if ipaddress.ip_address(host).version in [4, 6]: return True except ValueError: disallowed = re.compile(r"[^a-zA-Z\d\-]") return all(x and not disallowed.search(x) for x in host.split(".")) + return False class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @@ -35,12 +41,14 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL - def __init__(self): + def __init__(self) -> None: """Initialize.""" - self.brother = None - self.host = None + self.brother: Brother = None + self.host: str | None = None - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the initial step.""" errors = {} @@ -72,11 +80,10 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=DATA_SCHEMA, errors=errors ) - async def async_step_zeroconf(self, discovery_info): + async def async_step_zeroconf( + self, discovery_info: DiscoveryInfoType + ) -> FlowResult: """Handle zeroconf discovery.""" - if discovery_info is None: - return self.async_abort(reason="cannot_connect") - if not discovery_info.get("name") or not discovery_info["name"].startswith( "Brother" ): @@ -107,7 +114,9 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) return await self.async_step_zeroconf_confirm() - async def async_step_zeroconf_confirm(self, user_input=None): + async def async_step_zeroconf_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initiated by zeroconf.""" if user_input is not None: title = f"{self.brother.model} {self.brother.serial}" diff --git a/homeassistant/components/brother/const.py b/homeassistant/components/brother/const.py index 2df14031f94c..a812e81f0eeb 100644 --- a/homeassistant/components/brother/const.py +++ b/homeassistant/components/brother/const.py @@ -1,5 +1,9 @@ """Constants for Brother integration.""" -from homeassistant.const import ATTR_ICON, PERCENTAGE +from __future__ import annotations + +from typing import TypedDict + +from homeassistant.const import PERCENTAGE ATTR_BELT_UNIT_REMAINING_LIFE = "belt_unit_remaining_life" ATTR_BLACK_DRUM_COUNTER = "black_drum_counter" @@ -18,9 +22,7 @@ ATTR_DRUM_COUNTER = "drum_counter" ATTR_DRUM_REMAINING_LIFE = "drum_remaining_life" ATTR_DRUM_REMAINING_PAGES = "drum_remaining_pages" ATTR_DUPLEX_COUNTER = "duplex_unit_pages_counter" -ATTR_ENABLED = "enabled" ATTR_FUSER_REMAINING_LIFE = "fuser_remaining_life" -ATTR_LABEL = "label" ATTR_LASER_REMAINING_LIFE = "laser_remaining_life" ATTR_MAGENTA_DRUM_COUNTER = "magenta_drum_counter" ATTR_MAGENTA_DRUM_REMAINING_LIFE = "magenta_drum_remaining_life" @@ -32,7 +34,6 @@ ATTR_PAGE_COUNTER = "page_counter" ATTR_PF_KIT_1_REMAINING_LIFE = "pf_kit_1_remaining_life" ATTR_PF_KIT_MP_REMAINING_LIFE = "pf_kit_mp_remaining_life" ATTR_STATUS = "status" -ATTR_UNIT = "unit" ATTR_UPTIME = "uptime" ATTR_YELLOW_DRUM_COUNTER = "yellow_drum_counter" ATTR_YELLOW_DRUM_REMAINING_LIFE = "yellow_drum_remaining_life" @@ -50,7 +51,7 @@ PRINTER_TYPES = ["laser", "ink"] SNMP = "snmp" -ATTRS_MAP = { +ATTRS_MAP: dict[str, tuple[str, str]] = { ATTR_DRUM_REMAINING_LIFE: (ATTR_DRUM_REMAINING_PAGES, ATTR_DRUM_COUNTER), ATTR_BLACK_DRUM_REMAINING_LIFE: ( ATTR_BLACK_DRUM_REMAINING_PAGES, @@ -70,149 +71,158 @@ ATTRS_MAP = { ), } -SENSOR_TYPES = { +SENSOR_TYPES: dict[str, SensorDescription] = { ATTR_STATUS: { - ATTR_ICON: "mdi:printer", - ATTR_LABEL: ATTR_STATUS.title(), - ATTR_UNIT: None, - ATTR_ENABLED: True, + "icon": "mdi:printer", + "label": ATTR_STATUS.title(), + "unit": None, + "enabled": True, }, ATTR_PAGE_COUNTER: { - ATTR_ICON: "mdi:file-document-outline", - ATTR_LABEL: ATTR_PAGE_COUNTER.replace("_", " ").title(), - ATTR_UNIT: UNIT_PAGES, - ATTR_ENABLED: True, + "icon": "mdi:file-document-outline", + "label": ATTR_PAGE_COUNTER.replace("_", " ").title(), + "unit": UNIT_PAGES, + "enabled": True, }, ATTR_BW_COUNTER: { - ATTR_ICON: "mdi:file-document-outline", - ATTR_LABEL: ATTR_BW_COUNTER.replace("_", " ").title(), - ATTR_UNIT: UNIT_PAGES, - ATTR_ENABLED: True, + "icon": "mdi:file-document-outline", + "label": ATTR_BW_COUNTER.replace("_", " ").title(), + "unit": UNIT_PAGES, + "enabled": True, }, ATTR_COLOR_COUNTER: { - ATTR_ICON: "mdi:file-document-outline", - ATTR_LABEL: ATTR_COLOR_COUNTER.replace("_", " ").title(), - ATTR_UNIT: UNIT_PAGES, - ATTR_ENABLED: True, + "icon": "mdi:file-document-outline", + "label": ATTR_COLOR_COUNTER.replace("_", " ").title(), + "unit": UNIT_PAGES, + "enabled": True, }, ATTR_DUPLEX_COUNTER: { - ATTR_ICON: "mdi:file-document-outline", - ATTR_LABEL: ATTR_DUPLEX_COUNTER.replace("_", " ").title(), - ATTR_UNIT: UNIT_PAGES, - ATTR_ENABLED: True, + "icon": "mdi:file-document-outline", + "label": ATTR_DUPLEX_COUNTER.replace("_", " ").title(), + "unit": UNIT_PAGES, + "enabled": True, }, ATTR_DRUM_REMAINING_LIFE: { - ATTR_ICON: "mdi:chart-donut", - ATTR_LABEL: ATTR_DRUM_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:chart-donut", + "label": ATTR_DRUM_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_BLACK_DRUM_REMAINING_LIFE: { - ATTR_ICON: "mdi:chart-donut", - ATTR_LABEL: ATTR_BLACK_DRUM_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:chart-donut", + "label": ATTR_BLACK_DRUM_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_CYAN_DRUM_REMAINING_LIFE: { - ATTR_ICON: "mdi:chart-donut", - ATTR_LABEL: ATTR_CYAN_DRUM_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:chart-donut", + "label": ATTR_CYAN_DRUM_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_MAGENTA_DRUM_REMAINING_LIFE: { - ATTR_ICON: "mdi:chart-donut", - ATTR_LABEL: ATTR_MAGENTA_DRUM_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:chart-donut", + "label": ATTR_MAGENTA_DRUM_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_YELLOW_DRUM_REMAINING_LIFE: { - ATTR_ICON: "mdi:chart-donut", - ATTR_LABEL: ATTR_YELLOW_DRUM_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:chart-donut", + "label": ATTR_YELLOW_DRUM_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_BELT_UNIT_REMAINING_LIFE: { - ATTR_ICON: "mdi:current-ac", - ATTR_LABEL: ATTR_BELT_UNIT_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:current-ac", + "label": ATTR_BELT_UNIT_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_FUSER_REMAINING_LIFE: { - ATTR_ICON: "mdi:water-outline", - ATTR_LABEL: ATTR_FUSER_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:water-outline", + "label": ATTR_FUSER_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_LASER_REMAINING_LIFE: { - ATTR_ICON: "mdi:spotlight-beam", - ATTR_LABEL: ATTR_LASER_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:spotlight-beam", + "label": ATTR_LASER_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_PF_KIT_1_REMAINING_LIFE: { - ATTR_ICON: "mdi:printer-3d", - ATTR_LABEL: ATTR_PF_KIT_1_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d", + "label": ATTR_PF_KIT_1_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_PF_KIT_MP_REMAINING_LIFE: { - ATTR_ICON: "mdi:printer-3d", - ATTR_LABEL: ATTR_PF_KIT_MP_REMAINING_LIFE.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d", + "label": ATTR_PF_KIT_MP_REMAINING_LIFE.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_BLACK_TONER_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_BLACK_TONER_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_BLACK_TONER_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_CYAN_TONER_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_CYAN_TONER_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_CYAN_TONER_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_MAGENTA_TONER_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_MAGENTA_TONER_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_MAGENTA_TONER_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_YELLOW_TONER_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_YELLOW_TONER_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_YELLOW_TONER_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_BLACK_INK_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_BLACK_INK_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_BLACK_INK_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_CYAN_INK_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_CYAN_INK_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_CYAN_INK_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_MAGENTA_INK_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_MAGENTA_INK_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_MAGENTA_INK_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_YELLOW_INK_REMAINING: { - ATTR_ICON: "mdi:printer-3d-nozzle", - ATTR_LABEL: ATTR_YELLOW_INK_REMAINING.replace("_", " ").title(), - ATTR_UNIT: PERCENTAGE, - ATTR_ENABLED: True, + "icon": "mdi:printer-3d-nozzle", + "label": ATTR_YELLOW_INK_REMAINING.replace("_", " ").title(), + "unit": PERCENTAGE, + "enabled": True, }, ATTR_UPTIME: { - ATTR_ICON: None, - ATTR_LABEL: ATTR_UPTIME.title(), - ATTR_UNIT: None, - ATTR_ENABLED: False, + "icon": None, + "label": ATTR_UPTIME.title(), + "unit": None, + "enabled": False, }, } + + +class SensorDescription(TypedDict): + """Sensor description class.""" + + icon: str | None + label: str + unit: str | None + enabled: bool diff --git a/homeassistant/components/brother/sensor.py b/homeassistant/components/brother/sensor.py index ca76932cd959..594da5fe84cc 100644 --- a/homeassistant/components/brother/sensor.py +++ b/homeassistant/components/brother/sensor.py @@ -1,14 +1,18 @@ """Support for the Brother service.""" +from __future__ import annotations + +from typing import Any + from homeassistant.components.sensor import SensorEntity +from homeassistant.config_entries import ConfigEntry from homeassistant.const import DEVICE_CLASS_TIMESTAMP +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity +from . import BrotherDataUpdateCoordinator from .const import ( - ATTR_ENABLED, - ATTR_ICON, - ATTR_LABEL, ATTR_MANUFACTURER, - ATTR_UNIT, ATTR_UPTIME, ATTRS_MAP, DATA_CONFIG_ENTRY, @@ -20,9 +24,11 @@ ATTR_COUNTER = "counter" ATTR_REMAINING_PAGES = "remaining_pages" -async def async_setup_entry(hass, config_entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: """Add Brother entities from a config_entry.""" - coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id] + coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] sensors = [] @@ -43,36 +49,42 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class BrotherPrinterSensor(CoordinatorEntity, SensorEntity): """Define an Brother Printer sensor.""" - def __init__(self, coordinator, kind, device_info): + def __init__( + self, + coordinator: BrotherDataUpdateCoordinator, + kind: str, + device_info: dict[str, Any], + ) -> None: """Initialize.""" super().__init__(coordinator) - self._name = f"{coordinator.data.model} {SENSOR_TYPES[kind][ATTR_LABEL]}" + self._description = SENSOR_TYPES[kind] + self._name = f"{coordinator.data.model} {self._description['label']}" self._unique_id = f"{coordinator.data.serial.lower()}_{kind}" self._device_info = device_info self.kind = kind - self._attrs = {} + self._attrs: dict[str, Any] = {} @property - def name(self): + def name(self) -> str: """Return the name.""" return self._name @property - def state(self): + def state(self) -> Any: """Return the state.""" if self.kind == ATTR_UPTIME: return getattr(self.coordinator.data, self.kind).isoformat() return getattr(self.coordinator.data, self.kind) @property - def device_class(self): + def device_class(self) -> str | None: """Return the class of this sensor.""" if self.kind == ATTR_UPTIME: return DEVICE_CLASS_TIMESTAMP return None @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes.""" remaining_pages, drum_counter = ATTRS_MAP.get(self.kind, (None, None)) if remaining_pages and drum_counter: @@ -83,26 +95,26 @@ class BrotherPrinterSensor(CoordinatorEntity, SensorEntity): return self._attrs @property - def icon(self): + def icon(self) -> str | None: """Return the icon.""" - return SENSOR_TYPES[self.kind][ATTR_ICON] + return self._description["icon"] @property - def unique_id(self): + def unique_id(self) -> str: """Return a unique_id for this entity.""" return self._unique_id @property - def unit_of_measurement(self): + def unit_of_measurement(self) -> str | None: """Return the unit the value is expressed in.""" - return SENSOR_TYPES[self.kind][ATTR_UNIT] + return self._description["unit"] @property - def device_info(self): + def device_info(self) -> dict[str, Any]: """Return the device info.""" return self._device_info @property - def entity_registry_enabled_default(self): + def entity_registry_enabled_default(self) -> bool: """Return if the entity should be enabled when first added to the entity registry.""" - return SENSOR_TYPES[self.kind][ATTR_ENABLED] + return self._description["enabled"] diff --git a/homeassistant/components/brother/utils.py b/homeassistant/components/brother/utils.py index 3a53f4c04a2d..e421be52154f 100644 --- a/homeassistant/components/brother/utils.py +++ b/homeassistant/components/brother/utils.py @@ -5,7 +5,7 @@ import pysnmp.hlapi.asyncio as hlapi from pysnmp.hlapi.asyncio.cmdgen import lcd from homeassistant.const import EVENT_HOMEASSISTANT_STOP -from homeassistant.core import callback +from homeassistant.core import Event, HomeAssistant, callback from homeassistant.helpers import singleton from .const import DOMAIN, SNMP @@ -14,13 +14,13 @@ _LOGGER = logging.getLogger(__name__) @singleton.singleton("snmp_engine") -def get_snmp_engine(hass): +def get_snmp_engine(hass: HomeAssistant) -> hlapi.SnmpEngine: """Get SNMP engine.""" _LOGGER.debug("Creating SNMP engine") snmp_engine = hlapi.SnmpEngine() @callback - def shutdown_listener(ev): + def shutdown_listener(ev: Event) -> None: if hass.data.get(DOMAIN): _LOGGER.debug("Unconfiguring SNMP engine") lcd.unconfigure(hass.data[DOMAIN][SNMP], None) diff --git a/mypy.ini b/mypy.ini index e633f1849063..c457b65b36ca 100644 --- a/mypy.ini +++ b/mypy.ini @@ -35,7 +35,7 @@ warn_return_any = false warn_unreachable = false warn_unused_ignores = false -[mypy-homeassistant.components,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.bond.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.huawei_lte.*,homeassistant.components.hyperion.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.knx.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.number.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.recorder.purge,homeassistant.components.recorder.repack,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.slack.*,homeassistant.components.sonos.media_player,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zeroconf.*,homeassistant.components.zone.*,homeassistant.components.zwave_js.*] +[mypy-homeassistant.components,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.bond.*,homeassistant.components.brother.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.huawei_lte.*,homeassistant.components.hyperion.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.knx.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.number.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.recorder.purge,homeassistant.components.recorder.repack,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.slack.*,homeassistant.components.sonos.media_player,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zeroconf.*,homeassistant.components.zone.*,homeassistant.components.zwave_js.*] check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true diff --git a/tests/components/brother/test_config_flow.py b/tests/components/brother/test_config_flow.py index d681ac9c988b..532200cf2a6e 100644 --- a/tests/components/brother/test_config_flow.py +++ b/tests/components/brother/test_config_flow.py @@ -40,8 +40,8 @@ async def test_create_entry_with_hostname(hass): assert result["data"][CONF_TYPE] == CONFIG[CONF_TYPE] -async def test_create_entry_with_ip_address(hass): - """Test that the user step works with printer IP address.""" +async def test_create_entry_with_ipv4_address(hass): + """Test that the user step works with printer IPv4 address.""" with patch( "brother.Brother._get_data", return_value=json.loads(load_fixture("brother_printer_data.json")), @@ -58,6 +58,24 @@ async def test_create_entry_with_ip_address(hass): assert result["data"][CONF_TYPE] == "laser" +async def test_create_entry_with_ipv6_address(hass): + """Test that the user step works with printer IPv6 address.""" + with patch( + "brother.Brother._get_data", + return_value=json.loads(load_fixture("brother_printer_data.json")), + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + data={CONF_HOST: "2001:db8::1428:57ab", CONF_TYPE: "laser"}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "HL-L2340DW 0123456789" + assert result["data"][CONF_HOST] == "2001:db8::1428:57ab" + assert result["data"][CONF_TYPE] == "laser" + + async def test_invalid_hostname(hass): """Test invalid hostname in user_input.""" result = await hass.config_entries.flow.async_init( @@ -118,16 +136,6 @@ async def test_device_exists_abort(hass): assert result["reason"] == "already_configured" -async def test_zeroconf_no_data(hass): - """Test we abort if zeroconf provides no data.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_ZEROCONF} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "cannot_connect" - - async def test_zeroconf_not_brother_printer_error(hass): """Test we abort zeroconf flow if printer isn't Brother.""" with patch(