diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index 1c1768b58fd2bb..e732438bc03e73 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -2,28 +2,40 @@ from __future__ import annotations +import logging + from brother import Brother, SnmpError from homeassistant.components.snmp import async_get_snmp_engine -from homeassistant.const import CONF_HOST, CONF_TYPE, Platform +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import DOMAIN +from .const import ( + CONF_COMMUNITY, + DEFAULT_COMMUNITY, + DEFAULT_PORT, + DOMAIN, + SECTION_ADVANCED_SETTINGS, +) from .coordinator import BrotherConfigEntry, BrotherDataUpdateCoordinator +_LOGGER = logging.getLogger(__name__) + PLATFORMS = [Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> bool: """Set up Brother from a config entry.""" host = entry.data[CONF_HOST] + port = entry.data[SECTION_ADVANCED_SETTINGS][CONF_PORT] + community = entry.data[SECTION_ADVANCED_SETTINGS][CONF_COMMUNITY] printer_type = entry.data[CONF_TYPE] snmp_engine = await async_get_snmp_engine(hass) try: brother = await Brother.create( - host, printer_type=printer_type, snmp_engine=snmp_engine + host, port, community, printer_type=printer_type, snmp_engine=snmp_engine ) except (ConnectionError, SnmpError, TimeoutError) as error: raise ConfigEntryNotReady( @@ -48,3 +60,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> b async def async_unload_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + + +async def async_migrate_entry(hass: HomeAssistant, entry: BrotherConfigEntry) -> bool: + """Migrate an old entry.""" + if entry.version == 1 and entry.minor_version < 2: + new_data = entry.data.copy() + new_data[SECTION_ADVANCED_SETTINGS] = { + CONF_PORT: DEFAULT_PORT, + CONF_COMMUNITY: DEFAULT_COMMUNITY, + } + hass.config_entries.async_update_entry(entry, data=new_data, minor_version=2) + + _LOGGER.info( + "Migration to configuration version %s.%s successful", + entry.version, + entry.minor_version, + ) + + return True diff --git a/homeassistant/components/brother/config_flow.py b/homeassistant/components/brother/config_flow.py index f6b3f456056ba1..e4167dbf7523e3 100644 --- a/homeassistant/components/brother/config_flow.py +++ b/homeassistant/components/brother/config_flow.py @@ -9,21 +9,65 @@ from homeassistant.components.snmp import async_get_snmp_engine from homeassistant.config_entries import ConfigFlow, ConfigFlowResult -from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import section from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo from homeassistant.util.network import is_host_valid -from .const import DOMAIN, PRINTER_TYPES +from .const import ( + CONF_COMMUNITY, + DEFAULT_COMMUNITY, + DEFAULT_PORT, + DOMAIN, + PRINTER_TYPES, + SECTION_ADVANCED_SETTINGS, +) DATA_SCHEMA = vol.Schema( { vol.Required(CONF_HOST): str, vol.Optional(CONF_TYPE, default="laser"): vol.In(PRINTER_TYPES), + vol.Required(SECTION_ADVANCED_SETTINGS): section( + vol.Schema( + { + vol.Required(CONF_PORT, default=DEFAULT_PORT): int, + vol.Required(CONF_COMMUNITY, default=DEFAULT_COMMUNITY): str, + }, + ), + {"collapsed": True}, + ), + } +) +ZEROCONF_SCHEMA = vol.Schema( + { + vol.Optional(CONF_TYPE, default="laser"): vol.In(PRINTER_TYPES), + vol.Required(SECTION_ADVANCED_SETTINGS): section( + vol.Schema( + { + vol.Required(CONF_PORT, default=DEFAULT_PORT): int, + vol.Required(CONF_COMMUNITY, default=DEFAULT_COMMUNITY): str, + }, + ), + {"collapsed": True}, + ), + } +) +RECONFIGURE_SCHEMA = vol.Schema( + { + vol.Required(CONF_HOST): str, + vol.Required(SECTION_ADVANCED_SETTINGS): section( + vol.Schema( + { + vol.Required(CONF_PORT, default=DEFAULT_PORT): int, + vol.Required(CONF_COMMUNITY, default=DEFAULT_COMMUNITY): str, + }, + ), + {"collapsed": True}, + ), } ) -RECONFIGURE_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str}) async def validate_input( @@ -35,7 +79,12 @@ async def validate_input( snmp_engine = await async_get_snmp_engine(hass) - brother = await Brother.create(user_input[CONF_HOST], snmp_engine=snmp_engine) + brother = await Brother.create( + user_input[CONF_HOST], + user_input[SECTION_ADVANCED_SETTINGS][CONF_PORT], + user_input[SECTION_ADVANCED_SETTINGS][CONF_COMMUNITY], + snmp_engine=snmp_engine, + ) await brother.async_update() if expected_mac is not None and brother.serial.lower() != expected_mac: @@ -48,6 +97,7 @@ class BrotherConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Brother Printer.""" VERSION = 1 + MINOR_VERSION = 2 def __init__(self) -> None: """Initialize.""" @@ -126,13 +176,11 @@ async def async_step_zeroconf_confirm( title = f"{self.brother.model} {self.brother.serial}" return self.async_create_entry( title=title, - data={CONF_HOST: self.host, CONF_TYPE: user_input[CONF_TYPE]}, + data={CONF_HOST: self.host, **user_input}, ) return self.async_show_form( step_id="zeroconf_confirm", - data_schema=vol.Schema( - {vol.Optional(CONF_TYPE, default="laser"): vol.In(PRINTER_TYPES)} - ), + data_schema=ZEROCONF_SCHEMA, description_placeholders={ "serial_number": self.brother.serial, "model": self.brother.model, @@ -160,7 +208,7 @@ async def async_step_reconfigure( else: return self.async_update_reload_and_abort( entry, - data_updates={CONF_HOST: user_input[CONF_HOST]}, + data_updates=user_input, ) return self.async_show_form( diff --git a/homeassistant/components/brother/const.py b/homeassistant/components/brother/const.py index c0ae7cf60b0929..85b8a2a4a551dd 100644 --- a/homeassistant/components/brother/const.py +++ b/homeassistant/components/brother/const.py @@ -10,3 +10,10 @@ PRINTER_TYPES: Final = ["laser", "ink"] UPDATE_INTERVAL = timedelta(seconds=30) + +SECTION_ADVANCED_SETTINGS = "advanced_settings" + +CONF_COMMUNITY = "community" + +DEFAULT_COMMUNITY = "public" +DEFAULT_PORT = 161 diff --git a/homeassistant/components/brother/strings.json b/homeassistant/components/brother/strings.json index d0714a199c41ba..f5da85ebb77e5a 100644 --- a/homeassistant/components/brother/strings.json +++ b/homeassistant/components/brother/strings.json @@ -8,7 +8,21 @@ "type": "Type of the printer" }, "data_description": { - "host": "The hostname or IP address of the Brother printer to control." + "host": "The hostname or IP address of the Brother printer to control.", + "type": "Brother printer type: ink or laser." + }, + "sections": { + "advanced_settings": { + "name": "Advanced settings", + "data": { + "port": "[%key:common::config_flow::data::port%]", + "community": "SNMP Community" + }, + "data_description": { + "port": "The SNMP port of the Brother printer.", + "community": "A simple password for devices to communicate to each other." + } + } } }, "zeroconf_confirm": { @@ -16,6 +30,22 @@ "title": "Discovered Brother Printer", "data": { "type": "[%key:component::brother::config::step::user::data::type%]" + }, + "data_description": { + "type": "[%key:component::brother::config::step::user::data_description::type%]" + }, + "sections": { + "advanced_settings": { + "name": "Advanced settings", + "data": { + "port": "[%key:common::config_flow::data::port%]", + "community": "SNMP Community" + }, + "data_description": { + "port": "The SNMP port of the Brother printer.", + "community": "A simple password for devices to communicate to each other." + } + } } }, "reconfigure": { @@ -25,6 +55,19 @@ }, "data_description": { "host": "[%key:component::brother::config::step::user::data_description::host%]" + }, + "sections": { + "advanced_settings": { + "name": "Advanced settings", + "data": { + "port": "[%key:common::config_flow::data::port%]", + "community": "SNMP Community" + }, + "data_description": { + "port": "The SNMP port of the Brother printer.", + "community": "A simple password for devices to communicate to each other." + } + } } } }, diff --git a/homeassistant/components/derivative/manifest.json b/homeassistant/components/derivative/manifest.json index 4c5684bae75dcd..d29c75dfaede08 100644 --- a/homeassistant/components/derivative/manifest.json +++ b/homeassistant/components/derivative/manifest.json @@ -6,5 +6,6 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/derivative", "integration_type": "helper", - "iot_class": "calculated" + "iot_class": "calculated", + "quality_scale": "internal" } diff --git a/homeassistant/components/generic_thermostat/manifest.json b/homeassistant/components/generic_thermostat/manifest.json index 320de2aeb3e3c9..4fe8654d94706a 100644 --- a/homeassistant/components/generic_thermostat/manifest.json +++ b/homeassistant/components/generic_thermostat/manifest.json @@ -6,5 +6,6 @@ "dependencies": ["sensor", "switch"], "documentation": "https://www.home-assistant.io/integrations/generic_thermostat", "integration_type": "helper", - "iot_class": "local_polling" + "iot_class": "local_polling", + "quality_scale": "internal" } diff --git a/homeassistant/components/homee/alarm_control_panel.py b/homeassistant/components/homee/alarm_control_panel.py index fd7371b31e41c3..74aa6e36884538 100644 --- a/homeassistant/components/homee/alarm_control_panel.py +++ b/homeassistant/components/homee/alarm_control_panel.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from pyHomee.const import AttributeChangedBy, AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntity, @@ -17,7 +17,7 @@ from . import DOMAIN, HomeeConfigEntry from .entity import HomeeEntity -from .helpers import get_name_for_enum +from .helpers import get_name_for_enum, setup_homee_platform PARALLEL_UPDATES = 0 @@ -60,21 +60,32 @@ def get_supported_features( return supported_features -async def async_setup_entry( - hass: HomeAssistant, +async def add_alarm_control_panel_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the alarm control panel component.""" - + """Add homee alarm control panel entities.""" async_add_entities( HomeeAlarmPanel(attribute, config_entry, ALARM_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in ALARM_DESCRIPTIONS and attribute.editable ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the alarm control panel component.""" + + await setup_homee_platform( + add_alarm_control_panel_entities, async_add_entities, config_entry + ) + + class HomeeAlarmPanel(HomeeEntity, AlarmControlPanelEntity): """Representation of a Homee alarm control panel.""" diff --git a/homeassistant/components/homee/binary_sensor.py b/homeassistant/components/homee/binary_sensor.py index 3f5f5c46a29679..10eb5ea9121de4 100644 --- a/homeassistant/components/homee/binary_sensor.py +++ b/homeassistant/components/homee/binary_sensor.py @@ -1,7 +1,7 @@ """The Homee binary sensor platform.""" from pyHomee.const import AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -14,6 +14,7 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -152,23 +153,34 @@ } -async def async_setup_entry( - hass: HomeAssistant, +async def add_binary_sensor_entities( config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the binary sensor component.""" - - async_add_devices( + """Add homee binary sensor entities.""" + async_add_entities( HomeeBinarySensor( attribute, config_entry, BINARY_SENSOR_DESCRIPTIONS[attribute.type] ) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in BINARY_SENSOR_DESCRIPTIONS and not attribute.editable ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the binary sensor component.""" + + await setup_homee_platform( + add_binary_sensor_entities, async_add_entities, config_entry + ) + + class HomeeBinarySensor(HomeeEntity, BinarySensorEntity): """Representation of a Homee binary sensor.""" diff --git a/homeassistant/components/homee/button.py b/homeassistant/components/homee/button.py index 33a8b5f23c83dc..41dd111cf8471d 100644 --- a/homeassistant/components/homee/button.py +++ b/homeassistant/components/homee/button.py @@ -1,7 +1,7 @@ """The homee button platform.""" from pyHomee.const import AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.button import ( ButtonDeviceClass, @@ -14,6 +14,7 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -39,21 +40,30 @@ } -async def async_setup_entry( - hass: HomeAssistant, +async def add_button_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the button component.""" - + """Add homee button entities.""" async_add_entities( HomeeButton(attribute, config_entry, BUTTON_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in BUTTON_DESCRIPTIONS and attribute.editable ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the button component.""" + + await setup_homee_platform(add_button_entities, async_add_entities, config_entry) + + class HomeeButton(HomeeEntity, ButtonEntity): """Representation of a Homee button.""" diff --git a/homeassistant/components/homee/climate.py b/homeassistant/components/homee/climate.py index f6027522243a29..0aa3467f760226 100644 --- a/homeassistant/components/homee/climate.py +++ b/homeassistant/components/homee/climate.py @@ -21,6 +21,7 @@ from . import HomeeConfigEntry from .const import CLIMATE_PROFILES, DOMAIN, HOMEE_UNIT_TO_HA_UNIT, PRESET_MANUAL from .entity import HomeeNodeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -31,18 +32,27 @@ } +async def add_climate_entities( + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], +) -> None: + """Add homee climate entities.""" + async_add_entities( + HomeeClimate(node, config_entry) + for node in nodes + if node.profile in CLIMATE_PROFILES + ) + + async def async_setup_entry( hass: HomeAssistant, config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Add the Homee platform for the climate component.""" - async_add_devices( - HomeeClimate(node, config_entry) - for node in config_entry.runtime_data.nodes - if node.profile in CLIMATE_PROFILES - ) + await setup_homee_platform(add_climate_entities, async_add_entities, config_entry) class HomeeClimate(HomeeNodeEntity, ClimateEntity): diff --git a/homeassistant/components/homee/cover.py b/homeassistant/components/homee/cover.py index 79a9b00ffbaf57..b48d965512e86d 100644 --- a/homeassistant/components/homee/cover.py +++ b/homeassistant/components/homee/cover.py @@ -18,6 +18,7 @@ from . import HomeeConfigEntry from .entity import HomeeNodeEntity +from .helpers import setup_homee_platform _LOGGER = logging.getLogger(__name__) @@ -77,18 +78,25 @@ def get_device_class(node: HomeeNode) -> CoverDeviceClass | None: return COVER_DEVICE_PROFILES.get(node.profile) +async def add_cover_entities( + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], +) -> None: + """Add homee cover entities.""" + async_add_entities( + HomeeCover(node, config_entry) for node in nodes if is_cover_node(node) + ) + + async def async_setup_entry( hass: HomeAssistant, config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Add the homee platform for the cover integration.""" - async_add_devices( - HomeeCover(node, config_entry) - for node in config_entry.runtime_data.nodes - if is_cover_node(node) - ) + await setup_homee_platform(add_cover_entities, async_add_entities, config_entry) def is_cover_node(node: HomeeNode) -> bool: diff --git a/homeassistant/components/homee/event.py b/homeassistant/components/homee/event.py index 73c315e86957e7..5c4fa0af38013d 100644 --- a/homeassistant/components/homee/event.py +++ b/homeassistant/components/homee/event.py @@ -1,7 +1,7 @@ """The homee event platform.""" from pyHomee.const import AttributeType, NodeProfile -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.event import ( EventDeviceClass, @@ -13,6 +13,7 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -49,16 +50,15 @@ } -async def async_setup_entry( - hass: HomeAssistant, +async def add_event_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add event entities for homee.""" - + """Add homee event entities.""" async_add_entities( HomeeEvent(attribute, config_entry, EVENT_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in EVENT_DESCRIPTIONS and node.profile in REMOTE_PROFILES @@ -66,6 +66,16 @@ async def async_setup_entry( ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add event entities for homee.""" + + await setup_homee_platform(add_event_entities, async_add_entities, config_entry) + + class HomeeEvent(HomeeEntity, EventEntity): """Representation of a homee event.""" diff --git a/homeassistant/components/homee/fan.py b/homeassistant/components/homee/fan.py index d4694ee8d66b03..7904f008742a41 100644 --- a/homeassistant/components/homee/fan.py +++ b/homeassistant/components/homee/fan.py @@ -19,22 +19,32 @@ from . import HomeeConfigEntry from .const import DOMAIN, PRESET_AUTO, PRESET_MANUAL, PRESET_SUMMER from .entity import HomeeNodeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 +async def add_fan_entities( + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], +) -> None: + """Add homee fan entities.""" + async_add_entities( + HomeeFan(node, config_entry) + for node in nodes + if node.profile == NodeProfile.VENTILATION_CONTROL + ) + + async def async_setup_entry( hass: HomeAssistant, config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the Homee fan platform.""" - async_add_devices( - HomeeFan(node, config_entry) - for node in config_entry.runtime_data.nodes - if node.profile == NodeProfile.VENTILATION_CONTROL - ) + await setup_homee_platform(add_fan_entities, async_add_entities, config_entry) class HomeeFan(HomeeNodeEntity, FanEntity): diff --git a/homeassistant/components/homee/helpers.py b/homeassistant/components/homee/helpers.py index b73b1ae2bc9c60..f9f675a631d027 100644 --- a/homeassistant/components/homee/helpers.py +++ b/homeassistant/components/homee/helpers.py @@ -1,11 +1,42 @@ """Helper functions for the homee custom component.""" +from collections.abc import Callable, Coroutine from enum import IntEnum import logging +from typing import Any + +from pyHomee.model import HomeeNode + +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from . import HomeeConfigEntry _LOGGER = logging.getLogger(__name__) +async def setup_homee_platform( + add_platform_entities: Callable[ + [HomeeConfigEntry, AddConfigEntryEntitiesCallback, list[HomeeNode]], + Coroutine[Any, Any, None], + ], + async_add_entities: AddConfigEntryEntitiesCallback, + config_entry: HomeeConfigEntry, +) -> None: + """Set up a homee platform.""" + await add_platform_entities( + config_entry, async_add_entities, config_entry.runtime_data.nodes + ) + + async def add_device(node: HomeeNode, add: bool) -> None: + """Dynamically add entities.""" + if add: + await add_platform_entities(config_entry, async_add_entities, [node]) + + config_entry.async_on_unload( + config_entry.runtime_data.add_nodes_listener(add_device) + ) + + def get_name_for_enum(att_class: type[IntEnum], att_id: int) -> str | None: """Return the enum item name for a given integer.""" try: diff --git a/homeassistant/components/homee/light.py b/homeassistant/components/homee/light.py index 9c66764760ee6d..3fbfcbeba2263b 100644 --- a/homeassistant/components/homee/light.py +++ b/homeassistant/components/homee/light.py @@ -24,6 +24,7 @@ from . import HomeeConfigEntry from .const import LIGHT_PROFILES from .entity import HomeeNodeEntity +from .helpers import setup_homee_platform LIGHT_ATTRIBUTES = [ AttributeType.COLOR, @@ -85,21 +86,30 @@ def decimal_to_rgb_list(color: float) -> list[int]: ] -async def async_setup_entry( - hass: HomeAssistant, +async def add_light_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the light entity.""" - + """Add homee light entities.""" async_add_entities( HomeeLight(node, light, config_entry) - for node in config_entry.runtime_data.nodes + for node in nodes for light in get_light_attribute_sets(node) if is_light_node(node) ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the light entity.""" + + await setup_homee_platform(add_light_entities, async_add_entities, config_entry) + + class HomeeLight(HomeeNodeEntity, LightEntity): """Representation of a Homee light.""" diff --git a/homeassistant/components/homee/lock.py b/homeassistant/components/homee/lock.py index 8b3bf58040d102..f061e2eefae50c 100644 --- a/homeassistant/components/homee/lock.py +++ b/homeassistant/components/homee/lock.py @@ -3,6 +3,7 @@ from typing import Any from pyHomee.const import AttributeChangedBy, AttributeType +from pyHomee.model import HomeeNode from homeassistant.components.lock import LockEntity from homeassistant.core import HomeAssistant @@ -10,26 +11,35 @@ from . import HomeeConfigEntry from .entity import HomeeEntity -from .helpers import get_name_for_enum +from .helpers import get_name_for_enum, setup_homee_platform PARALLEL_UPDATES = 0 -async def async_setup_entry( - hass: HomeAssistant, +async def add_lock_entities( config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the lock component.""" - - async_add_devices( + """Add homee lock entities.""" + async_add_entities( HomeeLock(attribute, config_entry) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if (attribute.type == AttributeType.LOCK_STATE and attribute.editable) ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the lock component.""" + + await setup_homee_platform(add_lock_entities, async_add_entities, config_entry) + + class HomeeLock(HomeeEntity, LockEntity): """Representation of a Homee lock.""" diff --git a/homeassistant/components/homee/number.py b/homeassistant/components/homee/number.py index 5b824f1885139a..2015f9953fbcc2 100644 --- a/homeassistant/components/homee/number.py +++ b/homeassistant/components/homee/number.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from pyHomee.const import AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.number import ( NumberDeviceClass, @@ -18,6 +18,7 @@ from . import HomeeConfigEntry from .const import HOMEE_UNIT_TO_HA_UNIT from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -136,21 +137,30 @@ class HomeeNumberEntityDescription(NumberEntityDescription): } -async def async_setup_entry( - hass: HomeAssistant, +async def add_number_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the number component.""" - + """Add homee number entities.""" async_add_entities( HomeeNumber(attribute, config_entry, NUMBER_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in NUMBER_DESCRIPTIONS and attribute.data != "fixed_value" ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the number component.""" + + await setup_homee_platform(add_number_entities, async_add_entities, config_entry) + + class HomeeNumber(HomeeEntity, NumberEntity): """Representation of a Homee number.""" diff --git a/homeassistant/components/homee/quality_scale.yaml b/homeassistant/components/homee/quality_scale.yaml index 5a8f987c1f94c4..f27876b1725171 100644 --- a/homeassistant/components/homee/quality_scale.yaml +++ b/homeassistant/components/homee/quality_scale.yaml @@ -54,7 +54,7 @@ rules: docs-supported-functions: todo docs-troubleshooting: done docs-use-cases: todo - dynamic-devices: todo + dynamic-devices: done entity-category: done entity-device-class: done entity-disabled-by-default: done diff --git a/homeassistant/components/homee/select.py b/homeassistant/components/homee/select.py index 694d1bc74562f3..9466305c2758d0 100644 --- a/homeassistant/components/homee/select.py +++ b/homeassistant/components/homee/select.py @@ -1,7 +1,7 @@ """The Homee select platform.""" from pyHomee.const import AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.const import EntityCategory @@ -10,6 +10,7 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -27,21 +28,30 @@ } -async def async_setup_entry( - hass: HomeAssistant, +async def add_select_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the select component.""" - + """Add homee select entities.""" async_add_entities( HomeeSelect(attribute, config_entry, SELECT_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in SELECT_DESCRIPTIONS and attribute.editable ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the select component.""" + + await setup_homee_platform(add_select_entities, async_add_entities, config_entry) + + class HomeeSelect(HomeeEntity, SelectEntity): """Representation of a Homee select entity.""" diff --git a/homeassistant/components/homee/sensor.py b/homeassistant/components/homee/sensor.py index f977f705eb81f4..71508c5d669b1d 100644 --- a/homeassistant/components/homee/sensor.py +++ b/homeassistant/components/homee/sensor.py @@ -35,7 +35,7 @@ WINDOW_MAP_REVERSED, ) from .entity import HomeeEntity, HomeeNodeEntity -from .helpers import get_name_for_enum +from .helpers import get_name_for_enum, setup_homee_platform PARALLEL_UPDATES = 0 @@ -304,16 +304,16 @@ def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]: async def async_setup_entry( hass: HomeAssistant, config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Add the homee platform for the sensor components.""" ent_reg = er.async_get(hass) - devices: list[HomeeSensor | HomeeNodeSensor] = [] def add_deprecated_entity( attribute: HomeeAttribute, description: HomeeSensorEntityDescription - ) -> None: + ) -> list[HomeeSensor]: """Add deprecated entities.""" + deprecated_entities: list[HomeeSensor] = [] entity_uid = f"{config_entry.runtime_data.settings.uid}-{attribute.node_id}-{attribute.id}" if entity_id := ent_reg.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, entity_uid): entity_entry = ent_reg.async_get(entity_id) @@ -325,7 +325,9 @@ def add_deprecated_entity( f"deprecated_entity_{entity_uid}", ) elif entity_entry: - devices.append(HomeeSensor(attribute, config_entry, description)) + deprecated_entities.append( + HomeeSensor(attribute, config_entry, description) + ) if entity_used_in(hass, entity_id): async_create_issue( hass, @@ -342,27 +344,42 @@ def add_deprecated_entity( "entity": entity_id, }, ) + return deprecated_entities - for node in config_entry.runtime_data.nodes: - # Node properties that are sensors. - devices.extend( - HomeeNodeSensor(node, config_entry, description) - for description in NODE_SENSOR_DESCRIPTIONS - ) - - # Node attributes that are sensors. - for attribute in node.attributes: - if attribute.type == AttributeType.CURRENT_VALVE_POSITION: - add_deprecated_entity(attribute, SENSOR_DESCRIPTIONS[attribute.type]) - elif attribute.type in SENSOR_DESCRIPTIONS and not attribute.editable: - devices.append( - HomeeSensor( - attribute, config_entry, SENSOR_DESCRIPTIONS[attribute.type] + async def add_sensor_entities( + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], + ) -> None: + """Add homee sensor entities.""" + entities: list[HomeeSensor | HomeeNodeSensor] = [] + + for node in nodes: + # Node properties that are sensors. + entities.extend( + HomeeNodeSensor(node, config_entry, description) + for description in NODE_SENSOR_DESCRIPTIONS + ) + + # Node attributes that are sensors. + for attribute in node.attributes: + if attribute.type == AttributeType.CURRENT_VALVE_POSITION: + entities.extend( + add_deprecated_entity( + attribute, SENSOR_DESCRIPTIONS[attribute.type] + ) ) - ) + elif attribute.type in SENSOR_DESCRIPTIONS and not attribute.editable: + entities.append( + HomeeSensor( + attribute, config_entry, SENSOR_DESCRIPTIONS[attribute.type] + ) + ) + + if entities: + async_add_entities(entities) - if devices: - async_add_devices(devices) + await setup_homee_platform(add_sensor_entities, async_add_entities, config_entry) class HomeeSensor(HomeeEntity, SensorEntity): diff --git a/homeassistant/components/homee/siren.py b/homeassistant/components/homee/siren.py index da158c82f46aa5..9970f396ef900c 100644 --- a/homeassistant/components/homee/siren.py +++ b/homeassistant/components/homee/siren.py @@ -3,6 +3,7 @@ from typing import Any from pyHomee.const import AttributeType +from pyHomee.model import HomeeNode from homeassistant.components.siren import SirenEntity, SirenEntityFeature from homeassistant.core import HomeAssistant @@ -10,25 +11,35 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 -async def async_setup_entry( - hass: HomeAssistant, +async def add_siren_entities( config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add siren entities for homee.""" - - async_add_devices( + """Add homee siren entities.""" + async_add_entities( HomeeSiren(attribute, config_entry) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type == AttributeType.SIREN ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add siren entities for homee.""" + + await setup_homee_platform(add_siren_entities, async_add_entities, config_entry) + + class HomeeSiren(HomeeEntity, SirenEntity): """Representation of a homee siren device.""" diff --git a/homeassistant/components/homee/switch.py b/homeassistant/components/homee/switch.py index 5e87a1b40023ee..b620cb55c265bf 100644 --- a/homeassistant/components/homee/switch.py +++ b/homeassistant/components/homee/switch.py @@ -5,7 +5,7 @@ from typing import Any from pyHomee.const import AttributeType, NodeProfile -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.switch import ( SwitchDeviceClass, @@ -19,6 +19,7 @@ from . import HomeeConfigEntry from .const import CLIMATE_PROFILES, LIGHT_PROFILES from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -65,27 +66,35 @@ class HomeeSwitchEntityDescription(SwitchEntityDescription): } +async def add_switch_entities( + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], +) -> None: + """Add homee switch entities.""" + async_add_entities( + HomeeSwitch(attribute, config_entry, SWITCH_DESCRIPTIONS[attribute.type]) + for node in nodes + for attribute in node.attributes + if (attribute.type in SWITCH_DESCRIPTIONS and attribute.editable) + and not ( + attribute.type == AttributeType.ON_OFF and node.profile in LIGHT_PROFILES + ) + and not ( + attribute.type == AttributeType.MANUAL_OPERATION + and node.profile in CLIMATE_PROFILES + ) + ) + + async def async_setup_entry( hass: HomeAssistant, config_entry: HomeeConfigEntry, - async_add_devices: AddConfigEntryEntitiesCallback, + async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the switch platform for the Homee component.""" - for node in config_entry.runtime_data.nodes: - async_add_devices( - HomeeSwitch(attribute, config_entry, SWITCH_DESCRIPTIONS[attribute.type]) - for attribute in node.attributes - if (attribute.type in SWITCH_DESCRIPTIONS and attribute.editable) - and not ( - attribute.type == AttributeType.ON_OFF - and node.profile in LIGHT_PROFILES - ) - and not ( - attribute.type == AttributeType.MANUAL_OPERATION - and node.profile in CLIMATE_PROFILES - ) - ) + await setup_homee_platform(add_switch_entities, async_add_entities, config_entry) class HomeeSwitch(HomeeEntity, SwitchEntity): diff --git a/homeassistant/components/homee/valve.py b/homeassistant/components/homee/valve.py index 995716d7ef8974..64b1eac0efc4c8 100644 --- a/homeassistant/components/homee/valve.py +++ b/homeassistant/components/homee/valve.py @@ -1,7 +1,7 @@ """The Homee valve platform.""" from pyHomee.const import AttributeType -from pyHomee.model import HomeeAttribute +from pyHomee.model import HomeeAttribute, HomeeNode from homeassistant.components.valve import ( ValveDeviceClass, @@ -14,6 +14,7 @@ from . import HomeeConfigEntry from .entity import HomeeEntity +from .helpers import setup_homee_platform PARALLEL_UPDATES = 0 @@ -25,21 +26,30 @@ } -async def async_setup_entry( - hass: HomeAssistant, +async def add_valve_entities( config_entry: HomeeConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, + nodes: list[HomeeNode], ) -> None: - """Add the Homee platform for the valve component.""" - + """Add homee valve entities.""" async_add_entities( HomeeValve(attribute, config_entry, VALVE_DESCRIPTIONS[attribute.type]) - for node in config_entry.runtime_data.nodes + for node in nodes for attribute in node.attributes if attribute.type in VALVE_DESCRIPTIONS ) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: HomeeConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Add the homee platform for the valve component.""" + + await setup_homee_platform(add_valve_entities, async_add_entities, config_entry) + + class HomeeValve(HomeeEntity, ValveEntity): """Representation of a Homee valve.""" diff --git a/homeassistant/components/homewizard/number.py b/homeassistant/components/homewizard/number.py index a703043a63b89b..a4c5c5c64a02bc 100644 --- a/homeassistant/components/homewizard/number.py +++ b/homeassistant/components/homewizard/number.py @@ -20,7 +20,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up numbers for device.""" - if entry.runtime_data.data.device.supports_state(): + if entry.runtime_data.data.device.supports_led_brightness(): async_add_entities([HWEnergyNumberEntity(entry.runtime_data)]) diff --git a/tests/components/brother/conftest.py b/tests/components/brother/conftest.py index de22158da001f8..82a8d52a76ef19 100644 --- a/tests/components/brother/conftest.py +++ b/tests/components/brother/conftest.py @@ -7,8 +7,12 @@ from brother import BrotherSensors import pytest -from homeassistant.components.brother.const import DOMAIN -from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.components.brother.const import ( + CONF_COMMUNITY, + DOMAIN, + SECTION_ADVANCED_SETTINGS, +) +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE from tests.common import MockConfigEntry @@ -122,5 +126,10 @@ def mock_config_entry() -> MockConfigEntry: domain=DOMAIN, title="HL-L2340DW 0123456789", unique_id="0123456789", - data={CONF_HOST: "localhost", CONF_TYPE: "laser"}, + data={ + CONF_HOST: "localhost", + CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, + minor_version=2, ) diff --git a/tests/components/brother/snapshots/test_diagnostics.ambr b/tests/components/brother/snapshots/test_diagnostics.ambr index 614588bf829c20..2bd9adffbe1138 100644 --- a/tests/components/brother/snapshots/test_diagnostics.ambr +++ b/tests/components/brother/snapshots/test_diagnostics.ambr @@ -66,6 +66,10 @@ }), 'firmware': '1.2.3', 'info': dict({ + 'advanced_settings': dict({ + 'community': 'public', + 'port': 161, + }), 'host': 'localhost', 'type': 'laser', }), diff --git a/tests/components/brother/test_config_flow.py b/tests/components/brother/test_config_flow.py index 945f5549bbedd7..dfec3077832b20 100644 --- a/tests/components/brother/test_config_flow.py +++ b/tests/components/brother/test_config_flow.py @@ -6,9 +6,13 @@ from brother import SnmpError, UnsupportedModelError import pytest -from homeassistant.components.brother.const import DOMAIN +from homeassistant.components.brother.const import ( + CONF_COMMUNITY, + DOMAIN, + SECTION_ADVANCED_SETTINGS, +) from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF -from homeassistant.const import CONF_HOST, CONF_TYPE +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo @@ -17,7 +21,11 @@ from tests.common import MockConfigEntry -CONFIG = {CONF_HOST: "127.0.0.1", CONF_TYPE: "laser"} +CONFIG = { + CONF_HOST: "127.0.0.1", + CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, +} pytestmark = pytest.mark.usefixtures("mock_setup_entry", "mock_unload_entry") @@ -37,16 +45,21 @@ async def test_create_entry( hass: HomeAssistant, host: str, mock_brother_client: AsyncMock ) -> None: """Test that the user step works with printer hostname/IPv4/IPv6.""" + config = CONFIG.copy() + config[CONF_HOST] = host + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, - data={CONF_HOST: host, CONF_TYPE: "laser"}, + data=config, ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "HL-L2340DW 0123456789" assert result["data"][CONF_HOST] == host assert result["data"][CONF_TYPE] == "laser" + assert result["data"][SECTION_ADVANCED_SETTINGS][CONF_PORT] == 161 + assert result["data"][SECTION_ADVANCED_SETTINGS][CONF_COMMUNITY] == "public" async def test_invalid_hostname(hass: HomeAssistant) -> None: @@ -54,7 +67,11 @@ async def test_invalid_hostname(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, - data={CONF_HOST: "invalid/hostname", CONF_TYPE: "laser"}, + data={ + CONF_HOST: "invalid/hostname", + CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["errors"] == {CONF_HOST: "wrong_host"} @@ -241,13 +258,19 @@ async def test_zeroconf_confirm_create_entry( assert result["type"] is FlowResultType.FORM result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_TYPE: "laser"} + result["flow_id"], + user_input={ + CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "HL-L2340DW 0123456789" assert result["data"][CONF_HOST] == "127.0.0.1" assert result["data"][CONF_TYPE] == "laser" + assert result["data"][SECTION_ADVANCED_SETTINGS][CONF_PORT] == 161 + assert result["data"][SECTION_ADVANCED_SETTINGS][CONF_COMMUNITY] == "public" async def test_reconfigure_successful( @@ -265,7 +288,10 @@ async def test_reconfigure_successful( result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_HOST: "10.10.10.10"}, + user_input={ + CONF_HOST: "10.10.10.10", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.ABORT @@ -273,6 +299,7 @@ async def test_reconfigure_successful( assert mock_config_entry.data == { CONF_HOST: "10.10.10.10", CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, } @@ -303,7 +330,10 @@ async def test_reconfigure_not_successful( result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_HOST: "10.10.10.10"}, + user_input={ + CONF_HOST: "10.10.10.10", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.FORM @@ -314,7 +344,10 @@ async def test_reconfigure_not_successful( result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_HOST: "10.10.10.10"}, + user_input={ + CONF_HOST: "10.10.10.10", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.ABORT @@ -322,6 +355,7 @@ async def test_reconfigure_not_successful( assert mock_config_entry.data == { CONF_HOST: "10.10.10.10", CONF_TYPE: "laser", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, } @@ -340,7 +374,10 @@ async def test_reconfigure_invalid_hostname( result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_HOST: "invalid/hostname"}, + user_input={ + CONF_HOST: "invalid/hostname", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.FORM @@ -365,7 +402,10 @@ async def test_reconfigure_not_the_same_device( result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_HOST: "10.10.10.10"}, + user_input={ + CONF_HOST: "10.10.10.10", + SECTION_ADVANCED_SETTINGS: {CONF_PORT: 161, CONF_COMMUNITY: "public"}, + }, ) assert result["type"] is FlowResultType.FORM diff --git a/tests/components/brother/test_init.py b/tests/components/brother/test_init.py index 1a2c6bf23f2994..45702d91f2063a 100644 --- a/tests/components/brother/test_init.py +++ b/tests/components/brother/test_init.py @@ -5,8 +5,13 @@ from brother import SnmpError import pytest -from homeassistant.components.brother.const import DOMAIN +from homeassistant.components.brother.const import ( + CONF_COMMUNITY, + DOMAIN, + SECTION_ADVANCED_SETTINGS, +) from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE from homeassistant.core import HomeAssistant from . import init_integration @@ -68,3 +73,26 @@ async def test_unload_entry( assert mock_config_entry.state is ConfigEntryState.NOT_LOADED assert not hass.data.get(DOMAIN) + + +async def test_migrate_entry( + hass: HomeAssistant, + mock_brother_client: AsyncMock, +) -> None: + """Test entry migration to minor_version=2.""" + + config_entry = MockConfigEntry( + domain=DOMAIN, + title="HL-L2340DW 0123456789", + unique_id="0123456789", + data={CONF_HOST: "localhost", CONF_TYPE: "laser"}, + minor_version=1, + ) + config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.minor_version == 2 + assert config_entry.data[SECTION_ADVANCED_SETTINGS][CONF_PORT] == 161 + assert config_entry.data[SECTION_ADVANCED_SETTINGS][CONF_COMMUNITY] == "public" diff --git a/tests/components/homee/fixtures/add_device.json b/tests/components/homee/fixtures/add_device.json new file mode 100644 index 00000000000000..e0876c30732751 --- /dev/null +++ b/tests/components/homee/fixtures/add_device.json @@ -0,0 +1,176 @@ +{ + "id": 3, + "name": "Added Device", + "profile": 4010, + "image": "default", + "favorite": 0, + "order": 20, + "protocol": 1, + "routing": 0, + "state": 1, + "state_changed": 1709379826, + "added": 1676199446, + "history": 1, + "cube_type": 1, + "note": "", + "services": 5, + "phonetic_name": "", + "owner": 2, + "security": 0, + "attributes": [ + { + "id": 21, + "node_id": 3, + "instance": 1, + "minimum": 0, + "maximum": 200000, + "current_value": 555.591, + "target_value": 555.591, + "last_value": 555.586, + "unit": "kWh", + "step_value": 1.0, + "editable": 0, + "type": 4, + "state": 1, + "last_changed": 1694175270, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 22, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 1, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "", + "step_value": 1.0, + "editable": 0, + "type": 17, + "state": 1, + "last_changed": 1691668428, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "automations": ["reset"], + "history": { + "day": 182, + "week": 26, + "month": 6, + "stepped": true + } + } + }, + { + "id": 27, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 100, + "current_value": 100.0, + "target_value": 100.0, + "last_value": 100.0, + "unit": "%", + "step_value": 0.5, + "editable": 1, + "type": 349, + "state": 1, + "last_changed": 1624446307, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 28, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 1, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "", + "step_value": 1.0, + "editable": 1, + "type": 346, + "state": 1, + "last_changed": 1624806728, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "" + }, + { + "id": 29, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 1, + "current_value": 0.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "n/a", + "step_value": 1.0, + "editable": 1, + "type": 13, + "state": 1, + "last_changed": 1736003985, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "automations": ["toggle"], + "history": { + "day": 35, + "week": 5, + "month": 1, + "stepped": true + } + } + }, + { + "id": 30, + "node_id": 3, + "instance": 0, + "minimum": 0, + "maximum": 1, + "current_value": 1.0, + "target_value": 0.0, + "last_value": 0.0, + "unit": "n/a", + "step_value": 1.0, + "editable": 1, + "type": 1, + "state": 1, + "last_changed": 1736743294, + "changed_by": 1, + "changed_by_id": 0, + "based_on": 1, + "data": "", + "name": "", + "options": { + "can_observe": [300], + "automations": ["toggle"], + "history": { + "day": 35, + "week": 5, + "month": 1, + "stepped": true + } + } + } + ] +} diff --git a/tests/components/homee/snapshots/test_binary_sensor.ambr b/tests/components/homee/snapshots/test_binary_sensor.ambr index 0e9f02edf6c741..f9f905bfc449b1 100644 --- a/tests/components/homee/snapshots/test_binary_sensor.ambr +++ b/tests/components/homee/snapshots/test_binary_sensor.ambr @@ -1,4 +1,1473 @@ # serializer version: 1 +# name: test_add_device[binary_sensor.added_device_blackout-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.added_device_blackout', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Blackout', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'blackout_alarm', + 'unique_id': '00055511EECC-3-22', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.added_device_blackout-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Added Device Blackout', + }), + 'context': , + 'entity_id': 'binary_sensor.added_device_blackout', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_battery-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery', + 'unique_id': '00055511EECC-1-1', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_battery-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'battery', + 'friendly_name': 'Test Binary Sensor Battery', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_battery', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_blackout-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_blackout', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Blackout', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'blackout_alarm', + 'unique_id': '00055511EECC-1-2', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_blackout-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Blackout', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_blackout', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_carbon_dioxide-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_carbon_dioxide', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Carbon dioxide', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'carbon_dioxide', + 'unique_id': '00055511EECC-1-4', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_carbon_dioxide-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Carbon dioxide', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_carbon_dioxide', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_carbon_monoxide-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_carbon_monoxide', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Carbon monoxide', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'carbon_monoxide', + 'unique_id': '00055511EECC-1-3', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_carbon_monoxide-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'carbon_monoxide', + 'friendly_name': 'Test Binary Sensor Carbon monoxide', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_carbon_monoxide', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_flood-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_flood', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Flood', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'flood', + 'unique_id': '00055511EECC-1-5', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_flood-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'moisture', + 'friendly_name': 'Test Binary Sensor Flood', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_flood', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_high_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_high_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'High temperature', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'high_temperature', + 'unique_id': '00055511EECC-1-6', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_high_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'heat', + 'friendly_name': 'Test Binary Sensor High temperature', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_high_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_leak-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_leak', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Leak', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'leak_alarm', + 'unique_id': '00055511EECC-1-7', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_leak-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Leak', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_leak', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_load-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_load', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Load', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'load_alarm', + 'unique_id': '00055511EECC-1-8', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_load-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Test Binary Sensor Load', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_load', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_lock-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_lock', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock', + 'unique_id': '00055511EECC-1-9', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_lock-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'lock', + 'friendly_name': 'Test Binary Sensor Lock', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_lock', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_low_temperature-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_low_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Low temperature', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'low_temperature', + 'unique_id': '00055511EECC-1-10', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_low_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'cold', + 'friendly_name': 'Test Binary Sensor Low temperature', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_low_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_malfunction-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_malfunction', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Malfunction', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'malfunction', + 'unique_id': '00055511EECC-1-11', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_malfunction-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Malfunction', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_malfunction', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_maximum_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_maximum_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Maximum level', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'maximum', + 'unique_id': '00055511EECC-1-12', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_maximum_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Maximum level', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_maximum_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_minimum_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_minimum_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Minimum level', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'minimum', + 'unique_id': '00055511EECC-1-13', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_minimum_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Minimum level', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_minimum_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_motion-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_motion', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Motion', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'motion', + 'unique_id': '00055511EECC-1-14', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_motion-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'motion', + 'friendly_name': 'Test Binary Sensor Motion', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_motion', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_motor_blocked-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_motor_blocked', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Motor blocked', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'motor_blocked', + 'unique_id': '00055511EECC-1-15', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_motor_blocked-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Motor blocked', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_motor_blocked', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_opening-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_opening', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Opening', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'opening', + 'unique_id': '00055511EECC-1-17', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_opening-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'opening', + 'friendly_name': 'Test Binary Sensor Opening', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_opening', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_overcurrent-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_overcurrent', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Overcurrent', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'overcurrent', + 'unique_id': '00055511EECC-1-18', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_overcurrent-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Overcurrent', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_overcurrent', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_overload-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_overload', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Overload', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'overload', + 'unique_id': '00055511EECC-1-19', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_overload-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Overload', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_overload', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_plug-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_plug', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Plug', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'plug', + 'unique_id': '00055511EECC-1-16', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_plug-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'plug', + 'friendly_name': 'Test Binary Sensor Plug', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_plug', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_power-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Power', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'power', + 'unique_id': '00055511EECC-1-21', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_power-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'power', + 'friendly_name': 'Test Binary Sensor Power', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_power', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_presence-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_presence', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Presence', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'presence', + 'unique_id': '00055511EECC-1-20', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_presence-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'presence', + 'friendly_name': 'Test Binary Sensor Presence', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_presence', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_rain-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_rain', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Rain', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'rain', + 'unique_id': '00055511EECC-1-22', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_rain-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'moisture', + 'friendly_name': 'Test Binary Sensor Rain', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_rain', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_replace_filter-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_replace_filter', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Replace filter', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'replace_filter', + 'unique_id': '00055511EECC-1-23', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_replace_filter-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Replace filter', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_replace_filter', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_smoke-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_binary_sensor_smoke', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Smoke', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'smoke', + 'unique_id': '00055511EECC-1-24', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_smoke-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'smoke', + 'friendly_name': 'Test Binary Sensor Smoke', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_smoke', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_storage-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_storage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Storage', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage', + 'unique_id': '00055511EECC-1-25', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_storage-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Storage', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_storage', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_surge-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_surge', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Surge', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'surge', + 'unique_id': '00055511EECC-1-26', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_surge-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Surge', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_surge', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_tamper-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_tamper', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Tamper', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'tamper', + 'unique_id': '00055511EECC-1-27', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_tamper-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'tamper', + 'friendly_name': 'Test Binary Sensor Tamper', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_tamper', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_voltage_drop-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_voltage_drop', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Voltage drop', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'voltage_drop', + 'unique_id': '00055511EECC-1-28', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_voltage_drop-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'problem', + 'friendly_name': 'Test Binary Sensor Voltage drop', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_voltage_drop', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_water-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_binary_sensor_water', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Water', + 'platform': 'homee', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'water', + 'unique_id': '00055511EECC-1-29', + 'unit_of_measurement': None, + }) +# --- +# name: test_add_device[binary_sensor.test_binary_sensor_water-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'moisture', + 'friendly_name': 'Test Binary Sensor Water', + }), + 'context': , + 'entity_id': 'binary_sensor.test_binary_sensor_water', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_sensor_snapshot[binary_sensor.test_binary_sensor_battery-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/homee/test_binary_sensor.py b/tests/components/homee/test_binary_sensor.py index 50662616379f3e..ef3cf8ecee350e 100644 --- a/tests/components/homee/test_binary_sensor.py +++ b/tests/components/homee/test_binary_sensor.py @@ -27,3 +27,26 @@ async def test_sensor_snapshot( await setup_integration(hass, mock_config_entry) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_add_device( + hass: HomeAssistant, + mock_homee: MagicMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Test adding a device.""" + mock_homee.nodes = [build_mock_node("binary_sensors.json")] + mock_homee.get_node_by_id.return_value = mock_homee.nodes[0] + with patch("homeassistant.components.homee.PLATFORMS", [Platform.BINARY_SENSOR]): + await setup_integration(hass, mock_config_entry) + + # Add a new device + added_node = build_mock_node("add_device.json") + mock_homee.nodes.append(added_node) + mock_homee.get_node_by_id.return_value = mock_homee.nodes[1] + await mock_homee.add_nodes_listener.call_args_list[0][0][0](added_node, True) + await hass.async_block_till_done() + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)