diff --git a/homeassistant/components/bthome/manifest.json b/homeassistant/components/bthome/manifest.json index 86cab723a07850..99800a5903b76d 100644 --- a/homeassistant/components/bthome/manifest.json +++ b/homeassistant/components/bthome/manifest.json @@ -20,5 +20,5 @@ "dependencies": ["bluetooth_adapters"], "documentation": "https://www.home-assistant.io/integrations/bthome", "iot_class": "local_push", - "requirements": ["bthome-ble==3.14.2"] + "requirements": ["bthome-ble==3.15.0"] } diff --git a/homeassistant/components/shelly/strings.json b/homeassistant/components/shelly/strings.json index 90e41a1b63af5d..c1f61a95e0b654 100644 --- a/homeassistant/components/shelly/strings.json +++ b/homeassistant/components/shelly/strings.json @@ -137,15 +137,15 @@ "btn_down": "Button down", "btn_up": "Button up", "double_push": "Double push", - "double": "Double push", + "double": "[%key:component::shelly::entity::event::input::state_attributes::event_type::state::double_push%]", "long_push": "Long push", "long_single": "Long push and then short push", - "long": "Long push", + "long": "[%key:component::shelly::entity::event::input::state_attributes::event_type::state::long_push%]", "single_long": "Short push and then long push", "single_push": "Single push", - "single": "Single push", + "single": "[%key:component::shelly::entity::event::input::state_attributes::event_type::state::single_push%]", "triple_push": "Triple push", - "triple": "Triple push" + "triple": "[%key:component::shelly::entity::event::input::state_attributes::event_type::state::triple_push%]" } } } @@ -276,7 +276,7 @@ "fix_flow": { "step": { "confirm": { - "title": "{device_name} is running unsupported firmware", + "title": "[%key:component::shelly::issues::ble_scanner_firmware_unsupported::title%]", "description": "Your Shelly device {device_name} with IP address {ip_address} is running firmware {firmware} and acts as BLE scanner with active mode. This firmware version is not supported for BLE scanner active mode.\n\nSelect **Submit** button to start the OTA update to the latest stable firmware version." } }, @@ -303,7 +303,7 @@ "fix_flow": { "step": { "confirm": { - "title": "Outbound WebSocket is enabled for {device_name}", + "title": "[%key:component::shelly::issues::outbound_websocket_incorrectly_enabled::title%]", "description": "Your Shelly device {device_name} with IP address {ip_address} is a non-sleeping device and Outbound WebSocket should be disabled in its configuration.\n\nSelect **Submit** button to disable Outbound WebSocket." } }, @@ -317,7 +317,7 @@ "fix_flow": { "step": { "confirm": { - "title": "{device_name} is running outdated firmware", + "title": "[%key:component::shelly::issues::deprecated_firmware::title%]", "description": "Your Shelly device {device_name} with IP address {ip_address} is running firmware {firmware}. This firmware version will not be supported by Shelly integration starting from Home Assistant {ha_version}.\n\nSelect **Submit** button to start the OTA update to the latest stable firmware version." } }, diff --git a/homeassistant/components/sma/__init__.py b/homeassistant/components/sma/__init__.py index 0dc8fb83fac67b..7097308ebf709a 100644 --- a/homeassistant/components/sma/__init__.py +++ b/homeassistant/components/sma/__init__.py @@ -1,153 +1,69 @@ -"""The sma integration.""" +"""The SMA integration.""" from __future__ import annotations -from datetime import timedelta import logging -from typing import TYPE_CHECKING -import pysma +from pysma import SMA from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ATTR_CONNECTIONS, CONF_HOST, - CONF_MAC, CONF_PASSWORD, - CONF_SCAN_INTERVAL, CONF_SSL, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP, + Platform, ) -from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import device_registry as dr +from homeassistant.core import Event, HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed - -from .const import ( - CONF_GROUP, - DEFAULT_SCAN_INTERVAL, - DOMAIN, - PLATFORMS, - PYSMA_COORDINATOR, - PYSMA_DEVICE_INFO, - PYSMA_OBJECT, - PYSMA_REMOVE_LISTENER, - PYSMA_SENSORS, -) + +from .const import CONF_GROUP +from .coordinator import SMADataUpdateCoordinator + +PLATFORMS = [Platform.SENSOR] _LOGGER = logging.getLogger(__name__) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +type SMAConfigEntry = ConfigEntry[SMADataUpdateCoordinator] + + +async def async_setup_entry(hass: HomeAssistant, entry: SMAConfigEntry) -> bool: """Set up sma from a config entry.""" - # Init the SMA interface + protocol = "https" if entry.data[CONF_SSL] else "http" url = f"{protocol}://{entry.data[CONF_HOST]}" - verify_ssl = entry.data[CONF_VERIFY_SSL] - group = entry.data[CONF_GROUP] - password = entry.data[CONF_PASSWORD] - - session = async_get_clientsession(hass, verify_ssl=verify_ssl) - sma = pysma.SMA(session, url, password, group) - - try: - # Get updated device info - sma_device_info = await sma.device_info() - # Get all device sensors - sensor_def = await sma.get_sensors() - except ( - pysma.exceptions.SmaReadException, - pysma.exceptions.SmaConnectionException, - ) as exc: - raise ConfigEntryNotReady from exc - except pysma.exceptions.SmaAuthenticationException as exc: - raise ConfigEntryAuthFailed from exc - - if TYPE_CHECKING: - assert entry.unique_id - - # Create DeviceInfo object from sma_device_info - device_info = DeviceInfo( - configuration_url=url, - identifiers={(DOMAIN, entry.unique_id)}, - manufacturer=sma_device_info["manufacturer"], - model=sma_device_info["type"], - name=sma_device_info["name"], - sw_version=sma_device_info["sw_version"], - serial_number=sma_device_info["serial"], - ) - # Add the MAC address to connections, if it comes via DHCP - if CONF_MAC in entry.data: - device_info[ATTR_CONNECTIONS] = { - (dr.CONNECTION_NETWORK_MAC, entry.data[CONF_MAC]) - } - - # Define the coordinator - async def async_update_data(): - """Update the used SMA sensors.""" - try: - await sma.read(sensor_def) - except ( - pysma.exceptions.SmaReadException, - pysma.exceptions.SmaConnectionException, - ) as exc: - raise UpdateFailed(exc) from exc - - interval = timedelta( - seconds=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + sma = SMA( + session=async_get_clientsession( + hass=hass, verify_ssl=entry.data[CONF_VERIFY_SSL] + ), + url=url, + password=entry.data[CONF_PASSWORD], + group=entry.data[CONF_GROUP], ) - coordinator = DataUpdateCoordinator( - hass, - _LOGGER, - config_entry=entry, - name="sma", - update_method=async_update_data, - update_interval=interval, - ) + coordinator = SMADataUpdateCoordinator(hass, entry, sma) + await coordinator.async_config_entry_first_refresh() - try: - await coordinator.async_config_entry_first_refresh() - except ConfigEntryNotReady: - await sma.close_session() - raise + entry.runtime_data = coordinator + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - # Ensure we logout on shutdown - async def async_close_session(event): - """Close the session.""" - await sma.close_session() + # Ensure the SMA session closes when Home Assistant stops + async def _async_handle_shutdown(event: Event) -> None: + await coordinator.async_close_sma_session() - remove_stop_listener = hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STOP, async_close_session + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_handle_shutdown) ) - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = { - PYSMA_OBJECT: sma, - PYSMA_COORDINATOR: coordinator, - PYSMA_SENSORS: sensor_def, - PYSMA_REMOVE_LISTENER: remove_stop_listener, - PYSMA_DEVICE_INFO: device_info, - } - - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: SMAConfigEntry) -> bool: """Unload a config entry.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - if unload_ok: - data = hass.data[DOMAIN].pop(entry.entry_id) - await data[PYSMA_OBJECT].close_session() - data[PYSMA_REMOVE_LISTENER]() - - return unload_ok + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -156,7 +72,6 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Migrating from version %s", entry.version) if entry.version == 1: - # 1 -> 2: Unique ID from integer to string if entry.minor_version == 1: minor_version = 2 hass.config_entries.async_update_entry( diff --git a/homeassistant/components/sma/const.py b/homeassistant/components/sma/const.py index dec99c1d1afe5d..0ebf5bf83c6c26 100644 --- a/homeassistant/components/sma/const.py +++ b/homeassistant/components/sma/const.py @@ -1,7 +1,5 @@ """Constants for the sma integration.""" -from homeassistant.const import Platform - DOMAIN = "sma" PYSMA_COORDINATOR = "coordinator" @@ -10,7 +8,6 @@ PYSMA_SENSORS = "pysma_sensors" PYSMA_DEVICE_INFO = "device_info" -PLATFORMS = [Platform.SENSOR] CONF_GROUP = "group" diff --git a/homeassistant/components/sma/coordinator.py b/homeassistant/components/sma/coordinator.py new file mode 100644 index 00000000000000..b4adc4eb9d6d37 --- /dev/null +++ b/homeassistant/components/sma/coordinator.py @@ -0,0 +1,113 @@ +"""Coordinator for the SMA integration.""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import timedelta +import logging + +from pysma import SMA +from pysma.exceptions import ( + SmaAuthenticationException, + SmaConnectionException, + SmaReadException, +) +from pysma.sensor import Sensor + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_SCAN_INTERVAL +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DEFAULT_SCAN_INTERVAL, DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +@dataclass(slots=True) +class SMACoordinatorData: + """Data class for SMA sensors.""" + + sma_device_info: dict[str, str] + sensors: list[Sensor] + + +class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]): + """Data Update Coordinator for SMA.""" + + config_entry: ConfigEntry + + def __init__( + self, + hass: HomeAssistant, + config_entry: ConfigEntry, + sma: SMA, + ) -> None: + """Initialize the SMA Data Update Coordinator.""" + super().__init__( + hass, + _LOGGER, + config_entry=config_entry, + name=DOMAIN, + update_interval=timedelta( + seconds=config_entry.options.get( + CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL + ) + ), + ) + self.sma = sma + self._sma_device_info: dict[str, str] = {} + self._sensors: list[Sensor] = [] + + async def _async_setup(self) -> None: + """Setup the SMA Data Update Coordinator.""" + try: + self._sma_device_info = await self.sma.device_info() + self._sensors = await self.sma.get_sensors() + except ( + SmaReadException, + SmaConnectionException, + ) as err: + await self.async_close_sma_session() + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="cannot_connect", + translation_placeholders={"error": repr(err)}, + ) from err + except SmaAuthenticationException as err: + raise ConfigEntryAuthFailed( + translation_domain=DOMAIN, + translation_key="invalid_auth", + translation_placeholders={"error": repr(err)}, + ) from err + + async def _async_update_data(self) -> SMACoordinatorData: + """Update the used SMA sensors.""" + try: + await self.sma.read(self._sensors) + except ( + SmaReadException, + SmaConnectionException, + ) as err: + raise UpdateFailed( + translation_domain=DOMAIN, + translation_key="cannot_connect", + translation_placeholders={"error": repr(err)}, + ) from err + except SmaAuthenticationException as err: + raise ConfigEntryAuthFailed( + translation_domain=DOMAIN, + translation_key="invalid_auth", + translation_placeholders={"error": repr(err)}, + ) from err + + return SMACoordinatorData( + sma_device_info=self._sma_device_info, + sensors=self._sensors, + ) + + async def async_close_sma_session(self) -> None: + """Close the SMA session.""" + await self.sma.close_session() + _LOGGER.debug("SMA session closed") diff --git a/homeassistant/components/sma/sensor.py b/homeassistant/components/sma/sensor.py index ffef026aaedf28..49cba402b611d3 100644 --- a/homeassistant/components/sma/sensor.py +++ b/homeassistant/components/sma/sensor.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import pysma from homeassistant.components.sensor import ( @@ -12,8 +10,9 @@ SensorEntityDescription, SensorStateClass, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + CONF_HOST, + CONF_SSL, PERCENTAGE, EntityCategory, UnitOfApparentPower, @@ -29,12 +28,11 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, PYSMA_COORDINATOR, PYSMA_DEVICE_INFO, PYSMA_SENSORS +from . import SMAConfigEntry +from .const import DOMAIN +from .coordinator import SMADataUpdateCoordinator SENSOR_ENTITIES: dict[str, SensorEntityDescription] = { "status": SensorEntityDescription( @@ -837,41 +835,32 @@ async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + entry: SMAConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: - """Set up SMA sensors.""" - sma_data = hass.data[DOMAIN][config_entry.entry_id] - - coordinator = sma_data[PYSMA_COORDINATOR] - used_sensors = sma_data[PYSMA_SENSORS] - device_info = sma_data[PYSMA_DEVICE_INFO] - - if TYPE_CHECKING: - assert config_entry.unique_id + """Setup SMA sensors.""" + coordinator = entry.runtime_data async_add_entities( SMAsensor( coordinator, - config_entry.unique_id, SENSOR_ENTITIES.get(sensor.name), - device_info, sensor, + entry, ) - for sensor in used_sensors + for sensor in coordinator.data.sensors ) -class SMAsensor(CoordinatorEntity, SensorEntity): +class SMAsensor(CoordinatorEntity[SMADataUpdateCoordinator], SensorEntity): """Representation of a SMA sensor.""" def __init__( self, - coordinator: DataUpdateCoordinator, - config_entry_unique_id: str, + coordinator: SMADataUpdateCoordinator, description: SensorEntityDescription | None, - device_info: DeviceInfo, pysma_sensor: pysma.sensor.Sensor, + entry: SMAConfigEntry, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -880,11 +869,23 @@ def __init__( else: self._attr_name = pysma_sensor.name + protocol = "https" if entry.data[CONF_SSL] else "http" + url = f"{protocol}://{entry.data[CONF_HOST]}" + self._sensor = pysma_sensor + assert entry.unique_id - self._attr_device_info = device_info + self._attr_device_info = DeviceInfo( + configuration_url=url, + identifiers={(DOMAIN, entry.unique_id)}, + manufacturer=coordinator.data.sma_device_info["manufacturer"], + model=coordinator.data.sma_device_info["type"], + name=coordinator.data.sma_device_info["name"], + sw_version=coordinator.data.sma_device_info["sw_version"], + serial_number=coordinator.data.sma_device_info["serial"], + ) self._attr_unique_id = ( - f"{config_entry_unique_id}-{pysma_sensor.key}_{pysma_sensor.key_idx}" + f"{entry.unique_id}-{pysma_sensor.key}_{pysma_sensor.key_idx}" ) # Set sensor enabled to False. diff --git a/homeassistant/components/vicare/const.py b/homeassistant/components/vicare/const.py index c228020753118a..83a759020ea9ff 100644 --- a/homeassistant/components/vicare/const.py +++ b/homeassistant/components/vicare/const.py @@ -21,8 +21,6 @@ "Heatbox2_SRC", "E3_TCU10_x07", "E3_TCU41_x04", - "E3_FloorHeatingCircuitChannel", - "E3_FloorHeatingCircuitDistributorBox", "E3_RoomControl_One_522", ] diff --git a/homeassistant/components/vicare/entity.py b/homeassistant/components/vicare/entity.py index fdf250a60c0eb0..8de008f602ce71 100644 --- a/homeassistant/components/vicare/entity.py +++ b/homeassistant/components/vicare/entity.py @@ -29,6 +29,7 @@ def __init__( gateway_serial = device_config.getConfig().serial device_id = device_config.getId() model = device_config.getModel().replace("_", " ") + via_device_identifier: tuple[str, str] = ("", "") identifier = ( f"{gateway_serial}_{device_serial.replace('-', '_')}" @@ -36,6 +37,11 @@ def __init__( else f"{gateway_serial}_{device_id}" ) + if device_serial is not None and device_serial.startswith("zigbee-"): + parts = device_serial.split("-") + if len(parts) == 3: # expect format zigbee-- + via_device_identifier = (DOMAIN, f"{gateway_serial}_zigbee_{parts[1]}") + self._api: PyViCareDevice | PyViCareHeatingDeviceComponent = ( component if component else device ) @@ -50,4 +56,5 @@ def __init__( manufacturer="Viessmann", model=model, configuration_url=VIESSMANN_DEVELOPER_PORTAL, + via_device=via_device_identifier, ) diff --git a/requirements_all.txt b/requirements_all.txt index 1e3184452798ff..7a7d350802d04d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -706,7 +706,7 @@ brunt==1.2.0 bt-proximity==0.2.1 # homeassistant.components.bthome -bthome-ble==3.14.2 +bthome-ble==3.15.0 # homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 diff --git a/requirements_test.txt b/requirements_test.txt index 1135d258535e97..aca1c20b35d581 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -35,7 +35,7 @@ pytest-xdist==3.8.0 pytest==8.4.2 requests-mock==1.12.1 respx==0.22.0 -syrupy==4.9.1 +syrupy==5.0.0 tqdm==4.67.1 types-aiofiles==24.1.0.20250822 types-atomicwrites==1.4.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 65a5e05b424b3d..8dc556c848f338 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -630,7 +630,7 @@ brottsplatskartan==1.0.5 brunt==1.2.0 # homeassistant.components.bthome -bthome-ble==3.14.2 +bthome-ble==3.15.0 # homeassistant.components.buienradar buienradar==1.0.6 diff --git a/tests/components/sma/conftest.py b/tests/components/sma/conftest.py index 5b4ab23213c494..d06cbe0912ebd6 100644 --- a/tests/components/sma/conftest.py +++ b/tests/components/sma/conftest.py @@ -46,13 +46,19 @@ def mock_setup_entry() -> Generator[AsyncMock]: @pytest.fixture def mock_sma_client() -> Generator[MagicMock]: """Mock the SMA client.""" - with patch("homeassistant.components.sma.pysma.SMA", autospec=True) as client: - client.return_value.device_info.return_value = MOCK_DEVICE - client.new_session.return_value = True - client.return_value.get_sensors.return_value = Sensors( - sensor_map[GENERIC_SENSORS] - + sensor_map[OPTIMIZERS_VIA_INVERTER] - + sensor_map[ENERGY_METER_VIA_INVERTER] + with patch( + "homeassistant.components.sma.coordinator.SMA", autospec=True + ) as sma_cls: + sma_instance: MagicMock = sma_cls.return_value + sma_instance.device_info = AsyncMock(return_value=MOCK_DEVICE) + sma_instance.new_session = AsyncMock(return_value=True) + sma_instance.close_session = AsyncMock(return_value=True) + sma_instance.get_sensors = AsyncMock( + return_value=Sensors( + sensor_map[GENERIC_SENSORS] + + sensor_map[OPTIMIZERS_VIA_INVERTER] + + sensor_map[ENERGY_METER_VIA_INVERTER] + ) ) default_sensor_values = { @@ -65,12 +71,17 @@ def mock_sma_client() -> Generator[MagicMock]: "6100_00499700": 1000, } - def mock_read(sensors): + async def _async_mock_read(sensors) -> bool: for sensor in sensors: if sensor.key in default_sensor_values: sensor.value = default_sensor_values[sensor.key] return True - client.return_value.read.side_effect = mock_read + sma_instance.read = AsyncMock(side_effect=_async_mock_read) - yield client + with ( + patch("homeassistant.components.sma.config_flow.pysma.SMA", new=sma_cls), + patch("homeassistant.components.sma.SMA", new=sma_cls), + patch("pysma.SMA", new=sma_cls), + ): + yield sma_instance diff --git a/tests/components/sma/test_config_flow.py b/tests/components/sma/test_config_flow.py index b2e488318a54b8..7bcc2bcfee5e91 100644 --- a/tests/components/sma/test_config_flow.py +++ b/tests/components/sma/test_config_flow.py @@ -1,6 +1,6 @@ """Test the sma config flow.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, MagicMock, patch from pysma.exceptions import ( SmaAuthenticationException, @@ -47,7 +47,7 @@ async def test_form( - hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_sma_client: AsyncMock + hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_sma_client: MagicMock ) -> None: """Test we get the form.""" @@ -91,7 +91,8 @@ async def test_form_exceptions( ) with patch( - "homeassistant.components.sma.pysma.SMA.new_session", side_effect=exception + "homeassistant.components.sma.config_flow.pysma.SMA.new_session", + side_effect=exception, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -210,7 +211,7 @@ async def test_dhcp_exceptions( data=DHCP_DISCOVERY, ) - with patch("homeassistant.components.sma.pysma.SMA") as mock_sma: + with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma: mock_sma_instance = mock_sma.return_value mock_sma_instance.new_session = AsyncMock(side_effect=exception) @@ -222,7 +223,7 @@ async def test_dhcp_exceptions( assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": error} - with patch("homeassistant.components.sma.pysma.SMA") as mock_sma: + with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma: mock_sma_instance = mock_sma.return_value mock_sma_instance.new_session = AsyncMock(return_value=True) mock_sma_instance.device_info = AsyncMock(return_value=MOCK_DEVICE) @@ -290,7 +291,7 @@ async def test_reauth_flow_exceptions( result = await entry.start_reauth_flow(hass) - with patch("homeassistant.components.sma.pysma.SMA") as mock_sma: + with patch("homeassistant.components.sma.config_flow.pysma.SMA") as mock_sma: mock_sma_instance = mock_sma.return_value mock_sma_instance.new_session = AsyncMock(side_effect=exception) result = await hass.config_entries.flow.async_configure( diff --git a/tests/components/vicare/fixtures/FHTChannel.json b/tests/components/vicare/fixtures/FHTChannel.json new file mode 100644 index 00000000000000..40aabf9cd6e592 --- /dev/null +++ b/tests/components/vicare/fixtures/FHTChannel.json @@ -0,0 +1,72 @@ +{ + "data": [ + { + "apiVersion": 1, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 40, + "minLength": 1, + "regEx": "^[\\p{L}0-9]+( [\\p{L}0-9]+)*$" + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################-2/features/device.name/commands/setName" + } + }, + "deviceId": "zigbee-################-2", + "feature": "device.name", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "name": { + "type": "string", + "value": "Zone EG Bad" + } + }, + "timestamp": "2025-10-05T14:18:35.990Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################-2/features/device.name" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################-2", + "feature": "fht.valve", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "type": { + "type": "string", + "value": "normallyClosed" + } + }, + "timestamp": "2025-10-05T14:18:35.990Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################-2/features/fht.valve" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################-2", + "feature": "fht.valve.state", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "closed" + } + }, + "timestamp": "2025-10-05T14:18:35.990Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################-2/features/fht.valve.state" + } + ] +} diff --git a/tests/components/vicare/fixtures/FHTMain.json b/tests/components/vicare/fixtures/FHTMain.json new file mode 100644 index 00000000000000..374274034b4eb1 --- /dev/null +++ b/tests/components/vicare/fixtures/FHTMain.json @@ -0,0 +1,305 @@ +{ + "data": [ + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.heatingCircuitId", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 1 + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.heatingCircuitId" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.identification", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "triggered": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.identification" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.messages.status.raw", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "entries": { + "type": "array", + "value": [] + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.messages.status.raw" + }, + { + "apiVersion": 1, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 40, + "minLength": 1, + "regEx": "^[\\p{L}0-9]+( [\\p{L}0-9]+)*$" + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.name/commands/setName" + } + }, + "deviceId": "zigbee-################", + "feature": "device.name", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "name": { + "type": "string", + "value": "EG Fußbodenthermostat" + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.name" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.zigbee.lqi", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "strength": { + "type": "number", + "unit": "percent", + "value": 37 + } + }, + "timestamp": "2025-10-06T10:24:37.109Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.zigbee.lqi" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.zigbee.parent.id", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "################" + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.zigbee.parent.id" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.zigbee.parent.rx", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 99 + } + }, + "timestamp": "2025-10-06T11:07:24.624Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.zigbee.parent.rx" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "device.zigbee.parent.tx", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 96 + } + }, + "timestamp": "2025-10-06T10:48:03.918Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/device.zigbee.parent.tx" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.configuration.floorCoolingCondensationShutdownMargin", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 2 + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.configuration.floorCoolingCondensationShutdownMargin" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.configuration.floorCoolingCondensationThreshold", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 10 + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.configuration.floorCoolingCondensationThreshold" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.configuration.floorHeatingDamageProtectionThreshold", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 40 + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.configuration.floorHeatingDamageProtectionThreshold" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.operating.modes.active", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "heating" + } + }, + "timestamp": "2025-10-06T04:04:17.700Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.operating.modes.active" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.operating.modes.cooling", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-10-05T14:18:34.837Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.operating.modes.cooling" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.operating.modes.heating", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true + } + }, + "timestamp": "2025-10-06T04:04:17.700Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.operating.modes.heating" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.operating.modes.standby", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-10-06T04:04:17.700Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.operating.modes.standby" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "zigbee-################", + "feature": "fht.sensors.temperature.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 31 + } + }, + "timestamp": "2025-10-06T11:00:20.977Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/zigbee-################/features/fht.sensors.temperature.supply" + } + ] +} diff --git a/tests/components/vicare/snapshots/test_binary_sensor.ambr b/tests/components/vicare/snapshots/test_binary_sensor.ambr index 6240729cc24962..32e97b6e632f79 100644 --- a/tests/components/vicare/snapshots/test_binary_sensor.ambr +++ b/tests/components/vicare/snapshots/test_binary_sensor.ambr @@ -679,6 +679,103 @@ 'state': 'unavailable', }) # --- +# name: test_all_entities[binary_sensor.model3_identification_mode-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.model3_identification_mode', + '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': 'Identification mode', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'identification_mode', + 'unique_id': 'gateway3_zigbee_################-identification_mode', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.model3_identification_mode-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model3 Identification mode', + }), + 'context': , + 'entity_id': 'binary_sensor.model3_identification_mode', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- +# name: test_all_entities[binary_sensor.model4_valve-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.model4_valve', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Valve', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'valve', + 'unique_id': 'gateway4_zigbee_################_2-valve', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.model4_valve-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'door', + 'friendly_name': 'model4 Valve', + }), + 'context': , + 'entity_id': 'binary_sensor.model4_valve', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unavailable', + }) +# --- # name: test_binary_sensors[burner] StateSnapshot({ 'attributes': ReadOnlyDict({ diff --git a/tests/components/vicare/snapshots/test_sensor.ambr b/tests/components/vicare/snapshots/test_sensor.ambr index f0f21a82fed7c2..1f6d620c498996 100644 --- a/tests/components/vicare/snapshots/test_sensor.ambr +++ b/tests/components/vicare/snapshots/test_sensor.ambr @@ -2920,7 +2920,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway5_zigbee_################-battery_level', + 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-battery_level', 'unit_of_measurement': '%', }) # --- @@ -2937,10 +2937,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '36', + 'state': '89', }) # --- -# name: test_all_entities[sensor.model5_signal_strength-entry] +# name: test_all_entities[sensor.model5_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -2954,8 +2954,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.model5_signal_strength', + 'entity_category': None, + 'entity_id': 'sensor.model5_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2965,31 +2965,32 @@ 'name': None, 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': 'Signal strength', + 'original_name': 'Humidity', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'zigbee_signal_strength', - 'unique_id': 'gateway5_zigbee_################-zigbee_signal_strength', + 'translation_key': None, + 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-room_humidity', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model5_signal_strength-state] +# name: test_all_entities[sensor.model5_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model5 Signal strength', + 'device_class': 'humidity', + 'friendly_name': 'model5 Humidity', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model5_signal_strength', + 'entity_id': 'sensor.model5_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '90', + 'state': '53', }) # --- # name: test_all_entities[sensor.model5_temperature-entry] @@ -3028,7 +3029,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway5_zigbee_################-room_temperature', + 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-room_temperature', 'unit_of_measurement': , }) # --- @@ -3045,10 +3046,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '21.5', + 'state': '17.5', }) # --- -# name: test_all_entities[sensor.model5_valve_position-entry] +# name: test_all_entities[sensor.model6_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3063,7 +3064,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model5_valve_position', + 'entity_id': 'sensor.model6_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3073,34 +3074,35 @@ 'name': None, 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': 'Valve position', + 'original_name': 'Humidity', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'valve_position', - 'unique_id': 'gateway5_zigbee_################-valve_position', + 'translation_key': None, + 'unique_id': 'gateway6_zigbee_5cc7c1fffea33a3b-room_humidity', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model5_valve_position-state] +# name: test_all_entities[sensor.model6_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model5 Valve position', + 'device_class': 'humidity', + 'friendly_name': 'model6 Humidity', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model5_valve_position', + 'entity_id': 'sensor.model6_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '2', + 'state': '52', }) # --- -# name: test_all_entities[sensor.model6_signal_strength-entry] +# name: test_all_entities[sensor.model6_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3114,8 +3116,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.model6_signal_strength', + 'entity_category': None, + 'entity_id': 'sensor.model6_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3124,32 +3126,36 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': 'Signal strength', + 'original_name': 'Temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'zigbee_signal_strength', - 'unique_id': 'gateway6_zigbee_################-zigbee_signal_strength', - 'unit_of_measurement': '%', + 'translation_key': None, + 'unique_id': 'gateway6_zigbee_5cc7c1fffea33a3b-room_temperature', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model6_signal_strength-state] +# name: test_all_entities[sensor.model6_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model6 Signal strength', + 'device_class': 'temperature', + 'friendly_name': 'model6 Temperature', 'state_class': , - 'unit_of_measurement': '%', + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model6_signal_strength', + 'entity_id': 'sensor.model6_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '46', + 'state': '16.9', }) # --- # name: test_all_entities[sensor.model7_battery-entry] @@ -3185,7 +3191,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-battery_level', + 'unique_id': 'gateway7_zigbee_################-battery_level', 'unit_of_measurement': '%', }) # --- @@ -3202,10 +3208,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '89', + 'state': '36', }) # --- -# name: test_all_entities[sensor.model7_humidity-entry] +# name: test_all_entities[sensor.model7_signal_strength-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3219,8 +3225,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.model7_humidity', + 'entity_category': , + 'entity_id': 'sensor.model7_signal_strength', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3230,32 +3236,31 @@ 'name': None, 'options': dict({ }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Humidity', + 'original_name': 'Signal strength', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-room_humidity', + 'translation_key': 'zigbee_signal_strength', + 'unique_id': 'gateway7_zigbee_################-zigbee_signal_strength', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model7_humidity-state] +# name: test_all_entities[sensor.model7_signal_strength-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'humidity', - 'friendly_name': 'model7 Humidity', + 'friendly_name': 'model7 Signal strength', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model7_humidity', + 'entity_id': 'sensor.model7_signal_strength', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '53', + 'state': '90', }) # --- # name: test_all_entities[sensor.model7_temperature-entry] @@ -3294,7 +3299,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-room_temperature', + 'unique_id': 'gateway7_zigbee_################-room_temperature', 'unit_of_measurement': , }) # --- @@ -3311,10 +3316,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '17.5', + 'state': '21.5', }) # --- -# name: test_all_entities[sensor.model8_humidity-entry] +# name: test_all_entities[sensor.model7_valve_position-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3329,7 +3334,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model8_humidity', + 'entity_id': 'sensor.model7_valve_position', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3339,35 +3344,34 @@ 'name': None, 'options': dict({ }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Humidity', + 'original_name': 'Valve position', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway8_zigbee_5cc7c1fffea33a3b-room_humidity', + 'translation_key': 'valve_position', + 'unique_id': 'gateway7_zigbee_################-valve_position', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model8_humidity-state] +# name: test_all_entities[sensor.model7_valve_position-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'humidity', - 'friendly_name': 'model8 Humidity', + 'friendly_name': 'model7 Valve position', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model8_humidity', + 'entity_id': 'sensor.model7_valve_position', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '52', + 'state': '2', }) # --- -# name: test_all_entities[sensor.model8_temperature-entry] +# name: test_all_entities[sensor.model8_signal_strength-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3381,8 +3385,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.model8_temperature', + 'entity_category': , + 'entity_id': 'sensor.model8_signal_strength', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3391,36 +3395,84 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Temperature', + 'original_name': 'Signal strength', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway8_zigbee_5cc7c1fffea33a3b-room_temperature', - 'unit_of_measurement': , + 'translation_key': 'zigbee_signal_strength', + 'unique_id': 'gateway8_zigbee_################-zigbee_signal_strength', + 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model8_temperature-state] +# name: test_all_entities[sensor.model8_signal_strength-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'model8 Temperature', + 'friendly_name': 'model8 Signal strength', 'state_class': , - 'unit_of_measurement': , + 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model8_temperature', + 'entity_id': 'sensor.model8_signal_strength', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '16.9', + 'state': '46', + }) +# --- +# name: test_all_entities[sensor.model9_signal_strength-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.model9_signal_strength', + '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': 'Signal strength', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'zigbee_signal_strength', + 'unique_id': 'gateway9_zigbee_################-zigbee_signal_strength', + 'unit_of_measurement': '%', + }) +# --- +# name: test_all_entities[sensor.model9_signal_strength-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model9 Signal strength', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.model9_signal_strength', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '37', }) # --- # name: test_all_entities[sensor.vitovalor_hydraulic_separator_temperature-entry] diff --git a/tests/components/vicare/test_binary_sensor.py b/tests/components/vicare/test_binary_sensor.py index f3fba147c7e355..e69f8276c4132b 100644 --- a/tests/components/vicare/test_binary_sensor.py +++ b/tests/components/vicare/test_binary_sensor.py @@ -45,6 +45,8 @@ async def test_all_entities( Fixture({"type:boiler"}, "vicare/Vitodens300W.json"), Fixture({"type:radiator"}, "vicare/ZigbeeTRV.json"), Fixture({"type:repeater"}, "vicare/ZigbeeRepeater.json"), + Fixture({"type:fhtMain"}, "vicare/FHTMain.json"), + Fixture({"type:fhtChannel"}, "vicare/FHTChannel.json"), ] with ( patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)), diff --git a/tests/components/vicare/test_sensor.py b/tests/components/vicare/test_sensor.py index 2d71aaad3eb396..fb6f8e207ed26f 100644 --- a/tests/components/vicare/test_sensor.py +++ b/tests/components/vicare/test_sensor.py @@ -29,10 +29,12 @@ async def test_all_entities( Fixture({"type:ventilation"}, "vicare/ViAir300F.json"), Fixture({"type:ess"}, "vicare/VitoChargeVX3.json"), Fixture({None}, "vicare/VitoValor.json"), - Fixture({"type:radiator"}, "vicare/ZigbeeTRV.json"), - Fixture({"type:repeater"}, "vicare/ZigbeeRepeater.json"), Fixture({"type:climateSensor"}, "vicare/RoomSensor1.json"), Fixture({"type:climateSensor"}, "vicare/RoomSensor2.json"), + Fixture({"type:radiator"}, "vicare/ZigbeeTRV.json"), + Fixture({"type:repeater"}, "vicare/ZigbeeRepeater.json"), + Fixture({"type:fhtMain"}, "vicare/FHTMain.json"), + Fixture({"type:fhtChannel"}, "vicare/FHTChannel.json"), ] with ( patch(f"{MODULE}.login", return_value=MockPyViCare(fixtures)),