diff --git a/homeassistant/components/ecobee/manifest.json b/homeassistant/components/ecobee/manifest.json index e1b0b38ae25fc..dc8c973577c0d 100644 --- a/homeassistant/components/ecobee/manifest.json +++ b/homeassistant/components/ecobee/manifest.json @@ -9,7 +9,7 @@ }, "iot_class": "cloud_polling", "loggers": ["pyecobee"], - "requirements": ["python-ecobee-api==0.2.20"], + "requirements": ["python-ecobee-api==0.3.2"], "single_config_entry": true, "zeroconf": [ { diff --git a/homeassistant/components/home_connect/manifest.json b/homeassistant/components/home_connect/manifest.json index 4c10548476cb3..f5083c25a0cdb 100644 --- a/homeassistant/components/home_connect/manifest.json +++ b/homeassistant/components/home_connect/manifest.json @@ -22,6 +22,6 @@ "iot_class": "cloud_push", "loggers": ["aiohomeconnect"], "quality_scale": "platinum", - "requirements": ["aiohomeconnect==0.23.0"], + "requirements": ["aiohomeconnect==0.23.1"], "zeroconf": ["_homeconnect._tcp.local."] } diff --git a/homeassistant/components/home_connect/select.py b/homeassistant/components/home_connect/select.py index 7f060ea1df50c..a35a8f177645e 100644 --- a/homeassistant/components/home_connect/select.py +++ b/homeassistant/components/home_connect/select.py @@ -412,8 +412,8 @@ def update_native_value(self) -> None: """Set the program value.""" event = self.appliance.events.get(cast(EventKey, self.bsh_key)) self._attr_current_option = ( - PROGRAMS_TRANSLATION_KEYS_MAP.get(cast(ProgramKey, event.value)) - if event + PROGRAMS_TRANSLATION_KEYS_MAP.get(ProgramKey(event_value)) + if event and isinstance(event_value := event.value, str) else None ) diff --git a/homeassistant/components/home_connect/sensor.py b/homeassistant/components/home_connect/sensor.py index d8fda46385d75..977be18366342 100644 --- a/homeassistant/components/home_connect/sensor.py +++ b/homeassistant/components/home_connect/sensor.py @@ -556,8 +556,11 @@ def update_native_value(self) -> None: status = self.appliance.status[cast(StatusKey, self.bsh_key)].value self._update_native_value(status) - def _update_native_value(self, status: str | float) -> None: + def _update_native_value(self, status: str | float | None) -> None: """Set the value of the sensor based on the given value.""" + if status is None: + self._attr_native_value = None + return match self.device_class: case SensorDeviceClass.TIMESTAMP: self._attr_native_value = dt_util.utcnow() + timedelta( diff --git a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py index 0da98b562ec87..de5b868f07785 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/config_flow.py +++ b/homeassistant/components/homeassistant_connect_zbt2/config_flow.py @@ -76,9 +76,18 @@ class ZBT2FirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): """Mixin for Home Assistant Connect ZBT-2 firmware methods.""" context: ConfigFlowContext - BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR] + ZIGBEE_BAUDRATE = 460800 + # Early ZBT-2 samples used RTS/DTR to trigger the bootloader, later ones use the + # baudrate method. Since the two are mutually exclusive we just use both. + BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR, ResetTarget.BAUDRATE] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + ] + async def async_step_install_zigbee_firmware( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: diff --git a/homeassistant/components/homeassistant_connect_zbt2/update.py b/homeassistant/components/homeassistant_connect_zbt2/update.py index db1993217ce80..2e7fe0bb6ec40 100644 --- a/homeassistant/components/homeassistant_connect_zbt2/update.py +++ b/homeassistant/components/homeassistant_connect_zbt2/update.py @@ -14,7 +14,6 @@ from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, - ResetTarget, ) from homeassistant.components.update import UpdateDeviceClass from homeassistant.const import EntityCategory @@ -24,6 +23,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantConnectZBT2ConfigEntry +from .config_flow import ZBT2FirmwareMixin from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, HARDWARE_NAME, SERIAL_NUMBER _LOGGER = logging.getLogger(__name__) @@ -134,7 +134,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Connect ZBT-2 firmware update entity.""" - bootloader_reset_methods = [ResetTarget.RTS_DTR] + BOOTLOADER_RESET_METHODS = ZBT2FirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = ZBT2FirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py index cc7393d7d5c2a..39b45bd884382 100644 --- a/homeassistant/components/homeassistant_hardware/firmware_config_flow.py +++ b/homeassistant/components/homeassistant_hardware/firmware_config_flow.py @@ -81,6 +81,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC): ZIGBEE_BAUDRATE = 115200 # Default, subclasses may override BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] # Default, subclasses may override + APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]] = [] _picked_firmware_type: PickedFirmwareType _zigbee_flow_strategy: ZigbeeFlowStrategy = ZigbeeFlowStrategy.RECOMMENDED @@ -230,7 +231,11 @@ async def _install_firmware( # Installing new firmware is only truly required if the wrong type is # installed: upgrading to the latest release of the current firmware type # isn't strictly necessary for functionality. - self._probed_firmware_info = await probe_silabs_firmware_info(self._device) + self._probed_firmware_info = await probe_silabs_firmware_info( + self._device, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, + ) firmware_install_required = self._probed_firmware_info is None or ( self._probed_firmware_info.firmware_type != expected_installed_firmware_type @@ -295,6 +300,7 @@ async def _install_firmware( fw_data=fw_data, expected_installed_firmware_type=expected_installed_firmware_type, bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, progress_callback=lambda offset, total: self.async_update_progress( offset / total ), diff --git a/homeassistant/components/homeassistant_hardware/manifest.json b/homeassistant/components/homeassistant_hardware/manifest.json index f3e78454209ed..6911dbd5eefa9 100644 --- a/homeassistant/components/homeassistant_hardware/manifest.json +++ b/homeassistant/components/homeassistant_hardware/manifest.json @@ -7,7 +7,7 @@ "documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware", "integration_type": "system", "requirements": [ - "universal-silabs-flasher==0.0.37", + "universal-silabs-flasher==0.1.0", "ha-silabs-firmware-client==0.3.0" ] } diff --git a/homeassistant/components/homeassistant_hardware/update.py b/homeassistant/components/homeassistant_hardware/update.py index 96bacc79a031b..17f232b0e6f0f 100644 --- a/homeassistant/components/homeassistant_hardware/update.py +++ b/homeassistant/components/homeassistant_hardware/update.py @@ -86,7 +86,8 @@ class BaseFirmwareUpdateEntity( # Subclasses provide the mapping between firmware types and entity descriptions entity_description: FirmwareUpdateEntityDescription - bootloader_reset_methods: list[ResetTarget] = [] + BOOTLOADER_RESET_METHODS: list[ResetTarget] + APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]] _attr_supported_features = ( UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS @@ -278,7 +279,8 @@ async def async_install( device=self._current_device, fw_data=fw_data, expected_installed_firmware_type=self.entity_description.expected_firmware_type, - bootloader_reset_methods=self.bootloader_reset_methods, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, progress_callback=self._update_progress, domain=self._config_entry.domain, ) diff --git a/homeassistant/components/homeassistant_hardware/util.py b/homeassistant/components/homeassistant_hardware/util.py index 713806de7dcdd..2a8c9121692fa 100644 --- a/homeassistant/components/homeassistant_hardware/util.py +++ b/homeassistant/components/homeassistant_hardware/util.py @@ -4,7 +4,7 @@ import asyncio from collections import defaultdict -from collections.abc import AsyncIterator, Callable, Iterable, Sequence +from collections.abc import AsyncIterator, Callable, Sequence from contextlib import AsyncExitStack, asynccontextmanager from dataclasses import dataclass from enum import StrEnum @@ -309,15 +309,20 @@ async def guess_firmware_info(hass: HomeAssistant, device_path: str) -> Firmware async def probe_silabs_firmware_info( - device: str, *, probe_methods: Iterable[ApplicationType] | None = None + device: str, + *, + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], ) -> FirmwareInfo | None: """Probe the running firmware on a SiLabs device.""" flasher = Flasher( device=device, - **( - {"probe_methods": [m.as_flasher_application_type() for m in probe_methods]} - if probe_methods - else {} + probe_methods=tuple( + (m.as_flasher_application_type(), baudrate) + for m, baudrate in application_probe_methods + ), + bootloader_reset=tuple( + m.as_flasher_reset_target() for m in bootloader_reset_methods ), ) @@ -343,11 +348,18 @@ async def probe_silabs_firmware_info( async def probe_silabs_firmware_type( - device: str, *, probe_methods: Iterable[ApplicationType] | None = None + device: str, + *, + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], ) -> ApplicationType | None: """Probe the running firmware type on a SiLabs device.""" - fw_info = await probe_silabs_firmware_info(device, probe_methods=probe_methods) + fw_info = await probe_silabs_firmware_info( + device, + bootloader_reset_methods=bootloader_reset_methods, + application_probe_methods=application_probe_methods, + ) if fw_info is None: return None @@ -359,12 +371,22 @@ async def async_flash_silabs_firmware( device: str, fw_data: bytes, expected_installed_firmware_type: ApplicationType, - bootloader_reset_methods: Sequence[ResetTarget] = (), + bootloader_reset_methods: Sequence[ResetTarget], + application_probe_methods: Sequence[tuple[ApplicationType, int]], progress_callback: Callable[[int, int], None] | None = None, *, domain: str = DOMAIN, ) -> FirmwareInfo: """Flash firmware to the SiLabs device.""" + if not any( + method == expected_installed_firmware_type + for method, _ in application_probe_methods + ): + raise ValueError( + f"Expected installed firmware type {expected_installed_firmware_type!r}" + f" not in application probe methods {application_probe_methods!r}" + ) + async with async_firmware_update_context(hass, device, domain): firmware_info = await guess_firmware_info(hass, device) _LOGGER.debug("Identified firmware info: %s", firmware_info) @@ -373,11 +395,9 @@ async def async_flash_silabs_firmware( flasher = Flasher( device=device, - probe_methods=( - ApplicationType.GECKO_BOOTLOADER.as_flasher_application_type(), - ApplicationType.EZSP.as_flasher_application_type(), - ApplicationType.SPINEL.as_flasher_application_type(), - ApplicationType.CPC.as_flasher_application_type(), + probe_methods=tuple( + (m.as_flasher_application_type(), baudrate) + for m, baudrate in application_probe_methods ), bootloader_reset=tuple( m.as_flasher_reset_target() for m in bootloader_reset_methods @@ -401,7 +421,13 @@ async def async_flash_silabs_firmware( probed_firmware_info = await probe_silabs_firmware_info( device, - probe_methods=(expected_installed_firmware_type,), + bootloader_reset_methods=bootloader_reset_methods, + # Only probe for the expected installed firmware type + application_probe_methods=[ + (method, baudrate) + for method, baudrate in application_probe_methods + if method == expected_installed_firmware_type + ], ) if probed_firmware_info is None: diff --git a/homeassistant/components/homeassistant_sky_connect/config_flow.py b/homeassistant/components/homeassistant_sky_connect/config_flow.py index 7924a3291340e..0f0ab8084eef2 100644 --- a/homeassistant/components/homeassistant_sky_connect/config_flow.py +++ b/homeassistant/components/homeassistant_sky_connect/config_flow.py @@ -16,6 +16,7 @@ from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, + ResetTarget, ) from homeassistant.components.usb import ( usb_service_info_from_device, @@ -79,6 +80,20 @@ class SkyConnectFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): context: ConfigFlowContext + ZIGBEE_BAUDRATE = 115200 + # There is no hardware bootloader trigger + BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + # CPC baudrates can be removed once multiprotocol is removed + (ApplicationType.CPC, 115200), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 460800), + (ApplicationType.ROUTER, 115200), + ] + def _get_translation_placeholders(self) -> dict[str, str]: """Shared translation placeholders.""" placeholders = { diff --git a/homeassistant/components/homeassistant_sky_connect/update.py b/homeassistant/components/homeassistant_sky_connect/update.py index 58177249b62ce..1f52781d044c4 100644 --- a/homeassistant/components/homeassistant_sky_connect/update.py +++ b/homeassistant/components/homeassistant_sky_connect/update.py @@ -23,6 +23,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantSkyConnectConfigEntry +from .config_flow import SkyConnectFirmwareMixin from .const import ( DOMAIN, FIRMWARE, @@ -151,8 +152,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """SkyConnect firmware update entity.""" - # The ZBT-1 does not have a hardware bootloader trigger - bootloader_reset_methods = [] + BOOTLOADER_RESET_METHODS = SkyConnectFirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = SkyConnectFirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/homeassistant_yellow/config_flow.py b/homeassistant/components/homeassistant_yellow/config_flow.py index a42075c639545..a8532e1ca7cc2 100644 --- a/homeassistant/components/homeassistant_yellow/config_flow.py +++ b/homeassistant/components/homeassistant_yellow/config_flow.py @@ -82,7 +82,18 @@ async def _install_firmware_step( class YellowFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol): """Mixin for Home Assistant Yellow firmware methods.""" + ZIGBEE_BAUDRATE = 115200 BOOTLOADER_RESET_METHODS = [ResetTarget.YELLOW] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, ZIGBEE_BAUDRATE), + (ApplicationType.SPINEL, 460800), + # CPC baudrates can be removed once multiprotocol is removed + (ApplicationType.CPC, 115200), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 460800), + (ApplicationType.ROUTER, 115200), + ] async def async_step_install_zigbee_firmware( self, user_input: dict[str, Any] | None = None @@ -146,7 +157,11 @@ async def async_step_system( assert self._device is not None # We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this - self._probed_firmware_info = await probe_silabs_firmware_info(self._device) + self._probed_firmware_info = await probe_silabs_firmware_info( + self._device, + bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS, + application_probe_methods=self.APPLICATION_PROBE_METHODS, + ) # Kick off ZHA hardware discovery automatically if Zigbee firmware is running if ( diff --git a/homeassistant/components/homeassistant_yellow/update.py b/homeassistant/components/homeassistant_yellow/update.py index f62ecb06aa5e4..963d7c963eb8b 100644 --- a/homeassistant/components/homeassistant_yellow/update.py +++ b/homeassistant/components/homeassistant_yellow/update.py @@ -14,7 +14,6 @@ from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, - ResetTarget, ) from homeassistant.components.update import UpdateDeviceClass from homeassistant.const import EntityCategory @@ -24,6 +23,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from . import HomeAssistantYellowConfigEntry +from .config_flow import YellowFirmwareMixin from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, MANUFACTURER, MODEL, RADIO_DEVICE _LOGGER = logging.getLogger(__name__) @@ -150,7 +150,8 @@ async def async_setup_entry( class FirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Yellow firmware update entity.""" - bootloader_reset_methods = [ResetTarget.YELLOW] # Triggers a GPIO reset + BOOTLOADER_RESET_METHODS = YellowFirmwareMixin.BOOTLOADER_RESET_METHODS + APPLICATION_PROBE_METHODS = YellowFirmwareMixin.APPLICATION_PROBE_METHODS def __init__( self, diff --git a/homeassistant/components/lamarzocco/update.py b/homeassistant/components/lamarzocco/update.py index 33e64623256fc..3dbce542ff38b 100644 --- a/homeassistant/components/lamarzocco/update.py +++ b/homeassistant/components/lamarzocco/update.py @@ -125,7 +125,7 @@ def _raise_timeout_error() -> None: # to avoid TRY301 await self.coordinator.device.update_firmware() while ( update_progress := await self.coordinator.device.get_firmware() - ).command_status is UpdateStatus.IN_PROGRESS: + ).command_status is not UpdateStatus.UPDATED: if counter >= MAX_UPDATE_WAIT: _raise_timeout_error() self._attr_update_percentage = update_progress.progress_percentage diff --git a/homeassistant/components/lunatone/manifest.json b/homeassistant/components/lunatone/manifest.json index 9cc7f2579ffb8..6b0f3d06b6efa 100644 --- a/homeassistant/components/lunatone/manifest.json +++ b/homeassistant/components/lunatone/manifest.json @@ -7,5 +7,5 @@ "integration_type": "hub", "iot_class": "local_polling", "quality_scale": "silver", - "requirements": ["lunatone-rest-api-client==0.5.3"] + "requirements": ["lunatone-rest-api-client==0.5.7"] } diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 8f8b8d9729542..c8638aa37a316 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -61,10 +61,12 @@ def handle_sensor_registration(data): async_add_entities([MobileAppBinarySensor(data, config_entry)]) - async_dispatcher_connect( - hass, - f"{DOMAIN}_{ENTITY_TYPE}_register", - handle_sensor_registration, + config_entry.async_on_unload( + async_dispatcher_connect( + hass, + f"{DOMAIN}_{ENTITY_TYPE}_register", + handle_sensor_registration, + ) ) diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index 8200ad1fccde3..6a2c55d2fd79c 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -72,10 +72,12 @@ def handle_sensor_registration(data): async_add_entities([MobileAppSensor(data, config_entry)]) - async_dispatcher_connect( - hass, - f"{DOMAIN}_{ENTITY_TYPE}_register", - handle_sensor_registration, + config_entry.async_on_unload( + async_dispatcher_connect( + hass, + f"{DOMAIN}_{ENTITY_TYPE}_register", + handle_sensor_registration, + ) ) diff --git a/homeassistant/components/satel_integra/alarm_control_panel.py b/homeassistant/components/satel_integra/alarm_control_panel.py index 510193b5de7b0..ffb46d4dbe8ef 100644 --- a/homeassistant/components/satel_integra/alarm_control_panel.py +++ b/homeassistant/components/satel_integra/alarm_control_panel.py @@ -3,10 +3,9 @@ from __future__ import annotations import asyncio -from collections import OrderedDict import logging -from satel_integra.satel_integra import AlarmState +from satel_integra.satel_integra import AlarmState, AsyncSatel from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntity, @@ -16,17 +15,31 @@ ) from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( CONF_ARM_HOME_MODE, CONF_PARTITION_NUMBER, + DOMAIN, SIGNAL_PANEL_MESSAGE, SUBENTRY_TYPE_PARTITION, SatelConfigEntry, ) +ALARM_STATE_MAP = { + AlarmState.TRIGGERED: AlarmControlPanelState.TRIGGERED, + AlarmState.TRIGGERED_FIRE: AlarmControlPanelState.TRIGGERED, + AlarmState.ENTRY_TIME: AlarmControlPanelState.PENDING, + AlarmState.ARMED_MODE3: AlarmControlPanelState.ARMED_HOME, + AlarmState.ARMED_MODE2: AlarmControlPanelState.ARMED_HOME, + AlarmState.ARMED_MODE1: AlarmControlPanelState.ARMED_HOME, + AlarmState.ARMED_MODE0: AlarmControlPanelState.ARMED_AWAY, + AlarmState.EXIT_COUNTDOWN_OVER_10: AlarmControlPanelState.ARMING, + AlarmState.EXIT_COUNTDOWN_UNDER_10: AlarmControlPanelState.ARMING, +} + _LOGGER = logging.getLogger(__name__) @@ -45,9 +58,9 @@ async def async_setup_entry( ) for subentry in partition_subentries: - partition_num = subentry.data[CONF_PARTITION_NUMBER] - zone_name = subentry.data[CONF_NAME] - arm_home_mode = subentry.data[CONF_ARM_HOME_MODE] + partition_num: int = subentry.data[CONF_PARTITION_NUMBER] + zone_name: str = subentry.data[CONF_NAME] + arm_home_mode: int = subentry.data[CONF_ARM_HOME_MODE] async_add_entities( [ @@ -73,20 +86,31 @@ class SatelIntegraAlarmPanel(AlarmControlPanelEntity): | AlarmControlPanelEntityFeature.ARM_AWAY ) + _attr_has_entity_name = True + _attr_name = None + def __init__( - self, controller, name, arm_home_mode, partition_id, config_entry_id + self, + controller: AsyncSatel, + device_name: str, + arm_home_mode: int, + partition_id: int, + config_entry_id: str, ) -> None: """Initialize the alarm panel.""" - self._attr_name = name self._attr_unique_id = f"{config_entry_id}_alarm_panel_{partition_id}" self._arm_home_mode = arm_home_mode self._partition_id = partition_id self._satel = controller + self._attr_device_info = DeviceInfo( + name=device_name, identifiers={(DOMAIN, self._attr_unique_id)} + ) + async def async_added_to_hass(self) -> None: """Update alarm status and register callbacks for future updates.""" - _LOGGER.debug("Starts listening for panel messages") - self._update_alarm_status() + self._attr_alarm_state = self._read_alarm_state() + self.async_on_remove( async_dispatcher_connect( self.hass, SIGNAL_PANEL_MESSAGE, self._update_alarm_status @@ -94,55 +118,29 @@ async def async_added_to_hass(self) -> None: ) @callback - def _update_alarm_status(self): + def _update_alarm_status(self) -> None: """Handle alarm status update.""" state = self._read_alarm_state() - _LOGGER.debug("Got status update, current status: %s", state) + if state != self._attr_alarm_state: self._attr_alarm_state = state self.async_write_ha_state() - else: - _LOGGER.debug("Ignoring alarm status message, same state") - def _read_alarm_state(self): + def _read_alarm_state(self) -> AlarmControlPanelState | None: """Read current status of the alarm and translate it into HA status.""" - # Default - disarmed: - hass_alarm_status = AlarmControlPanelState.DISARMED - if not self._satel.connected: + _LOGGER.debug("Alarm panel not connected") return None - state_map = OrderedDict( - [ - (AlarmState.TRIGGERED, AlarmControlPanelState.TRIGGERED), - (AlarmState.TRIGGERED_FIRE, AlarmControlPanelState.TRIGGERED), - (AlarmState.ENTRY_TIME, AlarmControlPanelState.PENDING), - (AlarmState.ARMED_MODE3, AlarmControlPanelState.ARMED_HOME), - (AlarmState.ARMED_MODE2, AlarmControlPanelState.ARMED_HOME), - (AlarmState.ARMED_MODE1, AlarmControlPanelState.ARMED_HOME), - (AlarmState.ARMED_MODE0, AlarmControlPanelState.ARMED_AWAY), - ( - AlarmState.EXIT_COUNTDOWN_OVER_10, - AlarmControlPanelState.PENDING, - ), - ( - AlarmState.EXIT_COUNTDOWN_UNDER_10, - AlarmControlPanelState.PENDING, - ), - ] - ) - _LOGGER.debug("State map of Satel: %s", self._satel.partition_states) - - for satel_state, ha_state in state_map.items(): + for satel_state, ha_state in ALARM_STATE_MAP.items(): if ( satel_state in self._satel.partition_states and self._partition_id in self._satel.partition_states[satel_state] ): - hass_alarm_status = ha_state - break + return ha_state - return hass_alarm_status + return AlarmControlPanelState.DISARMED async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" @@ -154,8 +152,6 @@ async def async_alarm_disarm(self, code: str | None = None) -> None: self._attr_alarm_state == AlarmControlPanelState.TRIGGERED ) - _LOGGER.debug("Disarming, self._attr_alarm_state: %s", self._attr_alarm_state) - await self._satel.disarm(code, [self._partition_id]) if clear_alarm_necessary: @@ -165,14 +161,12 @@ async def async_alarm_disarm(self, code: str | None = None) -> None: async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" - _LOGGER.debug("Arming away") if code: await self._satel.arm(code, [self._partition_id]) async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" - _LOGGER.debug("Arming home") if code: await self._satel.arm(code, [self._partition_id], self._arm_home_mode) diff --git a/homeassistant/components/sensor/icons.json b/homeassistant/components/sensor/icons.json index fac1a681b4424..59d57da280346 100644 --- a/homeassistant/components/sensor/icons.json +++ b/homeassistant/components/sensor/icons.json @@ -118,6 +118,9 @@ "pm25": { "default": "mdi:molecule" }, + "pm4": { + "default": "mdi:molecule" + }, "power": { "default": "mdi:flash" }, diff --git a/homeassistant/components/velux/button.py b/homeassistant/components/velux/button.py index f782a186171f6..e60b1c89eb518 100644 --- a/homeassistant/components/velux/button.py +++ b/homeassistant/components/velux/button.py @@ -14,6 +14,8 @@ from . import VeluxConfigEntry from .const import DOMAIN +PARALLEL_UPDATES = 1 + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/velux/quality_scale.yaml b/homeassistant/components/velux/quality_scale.yaml index 10854dc56afc6..f19c3487ba72e 100644 --- a/homeassistant/components/velux/quality_scale.yaml +++ b/homeassistant/components/velux/quality_scale.yaml @@ -37,9 +37,7 @@ rules: entity-unavailable: todo integration-owner: done log-when-unavailable: todo - parallel-updates: - status: todo - comment: button still needs it + parallel-updates: done reauthentication-flow: todo test-coverage: status: todo diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json index 0ec823e6122a4..c456da47f1af8 100644 --- a/homeassistant/components/vesync/manifest.json +++ b/homeassistant/components/vesync/manifest.json @@ -13,5 +13,5 @@ "documentation": "https://www.home-assistant.io/integrations/vesync", "iot_class": "cloud_polling", "loggers": ["pyvesync"], - "requirements": ["pyvesync==3.2.1"] + "requirements": ["pyvesync==3.2.2"] } diff --git a/homeassistant/components/vicare/binary_sensor.py b/homeassistant/components/vicare/binary_sensor.py index f1fe7d457545e..940b27a4bc8d7 100644 --- a/homeassistant/components/vicare/binary_sensor.py +++ b/homeassistant/components/vicare/binary_sensor.py @@ -144,6 +144,11 @@ class ViCareBinarySensorEntityDescription( device_class=BinarySensorDeviceClass.DOOR, value_getter=lambda api: api.isValveOpen(), ), + ViCareBinarySensorEntityDescription( + key="ventilation_frost_protection", + translation_key="ventilation_frost_protection", + value_getter=lambda api: api.getHeatExchangerFrostProtectionActive(), + ), ) diff --git a/homeassistant/components/vicare/const.py b/homeassistant/components/vicare/const.py index 83a759020ea9f..ff57508c34b4b 100644 --- a/homeassistant/components/vicare/const.py +++ b/homeassistant/components/vicare/const.py @@ -35,6 +35,7 @@ DEFAULT_CACHE_DURATION = 60 VICARE_BAR = "bar" +VICARE_CELSIUS = "celsius" VICARE_CUBIC_METER = "cubicMeter" VICARE_KW = "kilowatt" VICARE_KWH = "kilowattHour" diff --git a/homeassistant/components/vicare/icons.json b/homeassistant/components/vicare/icons.json index 2fa5a42c10c6a..bafcac80c39b7 100644 --- a/homeassistant/components/vicare/icons.json +++ b/homeassistant/components/vicare/icons.json @@ -16,6 +16,15 @@ "domestic_hot_water_pump": { "default": "mdi:pump" }, + "filter_hours": { + "default": "mdi:counter" + }, + "filter_overdue_hours": { + "default": "mdi:counter" + }, + "filter_remaining_hours": { + "default": "mdi:counter" + }, "frost_protection": { "default": "mdi:snowflake" }, @@ -28,6 +37,12 @@ "solar_pump": { "default": "mdi:pump" }, + "supply_fan_hours": { + "default": "mdi:counter" + }, + "supply_fan_speed": { + "default": "mdi:rotate-right" + }, "valve": { "default": "mdi:pipe-valve" } @@ -101,6 +116,12 @@ "ess_state_of_charge": { "default": "mdi:home-battery" }, + "heating_rod_hours": { + "default": "mdi:counter" + }, + "heating_rod_starts": { + "default": "mdi:counter" + }, "pcc_energy_consumption": { "default": "mdi:transmission-tower-export" }, @@ -116,9 +137,15 @@ "valve_position": { "default": "mdi:pipe-valve" }, + "ventilation_input_volumeflow": { + "default": "mdi:air-filter" + }, "ventilation_level": { "default": "mdi:fan" }, + "ventilation_output_volumeflow": { + "default": "mdi:air-filter" + }, "volumetric_flow": { "default": "mdi:gauge" }, diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py index ca204ff47a584..c1f672a44791c 100644 --- a/homeassistant/components/vicare/sensor.py +++ b/homeassistant/components/vicare/sensor.py @@ -26,7 +26,9 @@ SensorStateClass, ) from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, PERCENTAGE, + REVOLUTIONS_PER_MINUTE, EntityCategory, UnitOfEnergy, UnitOfMass, @@ -42,6 +44,7 @@ from .const import ( VICARE_BAR, + VICARE_CELSIUS, VICARE_CUBIC_METER, VICARE_KW, VICARE_KWH, @@ -56,7 +59,9 @@ get_burners, get_circuits, get_compressors, + get_condensors, get_device_serial, + get_evaporators, is_supported, normalize_state, ) @@ -74,6 +79,7 @@ VICARE_UNIT_TO_HA_UNIT = { VICARE_BAR: UnitOfPressure.BAR, + VICARE_CELSIUS: UnitOfTemperature.CELSIUS, VICARE_CUBIC_METER: UnitOfVolume.CUBIC_METERS, VICARE_KW: UnitOfPower.KILO_WATT, VICARE_KWH: UnitOfEnergy.KILO_WATT_HOUR, @@ -111,6 +117,14 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), + ViCareSensorEntityDescription( + key="outside_humidity", + translation_key="outside_humidity", + native_unit_of_measurement=PERCENTAGE, + value_getter=lambda api: api.getOutsideHumidity(), + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), ViCareSensorEntityDescription( key="return_temperature", translation_key="return_temperature", @@ -992,6 +1006,101 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM value_getter=lambda api: api.getHydraulicSeparatorTemperature(), ), SUPPLY_TEMPERATURE_SENSOR, + ViCareSensorEntityDescription( + key="supply_humidity", + translation_key="supply_humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value_getter=lambda api: api.getSupplyHumidity(), + ), + ViCareSensorEntityDescription( + key="supply_fan_hours", + translation_key="supply_fan_hours", + native_unit_of_measurement=UnitOfTime.HOURS, + value_getter=lambda api: api.getSupplyFanHours(), + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="supply_fan_speed", + translation_key="supply_fan_speed", + native_unit_of_measurement=REVOLUTIONS_PER_MINUTE, + value_getter=lambda api: api.getSupplyFanSpeed(), + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="filter_hours", + translation_key="filter_hours", + native_unit_of_measurement=UnitOfTime.HOURS, + value_getter=lambda api: api.getFilterHours(), + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="filter_remaining_hours", + translation_key="filter_remaining_hours", + native_unit_of_measurement=UnitOfTime.HOURS, + value_getter=lambda api: api.getFilterRemainingHours(), + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="filter_overdue_hours", + translation_key="filter_overdue_hours", + native_unit_of_measurement=UnitOfTime.HOURS, + value_getter=lambda api: api.getFilterOverdueHours(), + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.TOTAL_INCREASING, + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="pm01", + device_class=SensorDeviceClass.PM1, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_getter=lambda api: api.getAirborneDustPM1(), + ), + ViCareSensorEntityDescription( + key="pm02", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_getter=lambda api: api.getAirborneDustPM2d5(), + ), + ViCareSensorEntityDescription( + key="pm04", + device_class=SensorDeviceClass.PM4, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_getter=lambda api: api.getAirborneDustPM4(), + ), + ViCareSensorEntityDescription( + key="pm10", + device_class=SensorDeviceClass.PM10, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_getter=lambda api: api.getAirborneDustPM10(), + ), + ViCareSensorEntityDescription( + key="ventilation_input_volumeflow", + translation_key="ventilation_input_volumeflow", + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + value_getter=lambda api: api.getSupplyVolumeFlow(), + state_class=SensorStateClass.MEASUREMENT, + ), + ViCareSensorEntityDescription( + key="ventilation_output_volumeflow", + translation_key="ventilation_output_volumeflow", + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + value_getter=lambda api: api.getExhaustVolumeFlow(), + state_class=SensorStateClass.MEASUREMENT, + ), ) CIRCUIT_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( @@ -1090,6 +1199,84 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM value_getter=lambda api: normalize_state(api.getPhase()), entity_category=EntityCategory.DIAGNOSTIC, ), + ViCareSensorEntityDescription( + key="compressor_inlet_temperature", + translation_key="compressor_inlet_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getCompressorInletTemperature(), + unit_getter=lambda api: api.getCompressorInletTemperatureUnit(), + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="compressor_outlet_temperature", + translation_key="compressor_outlet_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getCompressorOutletTemperature(), + unit_getter=lambda api: api.getCompressorOutletTemperatureUnit(), + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="compressor_inlet_pressure", + translation_key="compressor_inlet_pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.BAR, + value_getter=lambda api: api.getCompressorInletPressure(), + unit_getter=lambda api: api.getCompressorInletPressureUnit(), + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="compressor_outlet_pressure", + translation_key="compressor_outlet_pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.BAR, + value_getter=lambda api: api.getCompressorOutletPressure(), + unit_getter=lambda api: api.getCompressorOutletPressureUnit(), + entity_registry_enabled_default=False, + ), +) + +CONDENSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( + ViCareSensorEntityDescription( + key="condensor_liquid_temperature", + translation_key="condensor_liquid_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getCondensorLiquidTemperature(), + unit_getter=lambda api: api.getCondensorLiquidTemperatureUnit(), + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="condensor_subcooling_temperature", + translation_key="condensor_subcooling_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getCondensorSubcoolingTemperature(), + unit_getter=lambda api: api.getCondensorSubcoolingTemperatureUnit(), + entity_registry_enabled_default=False, + ), +) + +EVAPORATOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( + ViCareSensorEntityDescription( + key="evaporator_overheat_temperature", + translation_key="evaporator_overheat_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getEvaporatorOverheatTemperature(), + unit_getter=lambda api: api.getEvaporatorOverheatTemperatureUnit(), + entity_registry_enabled_default=False, + ), + ViCareSensorEntityDescription( + key="evaporator_liquid_temperature", + translation_key="evaporator_liquid_temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_getter=lambda api: api.getEvaporatorLiquidTemperature(), + unit_getter=lambda api: api.getEvaporatorLiquidTemperatureUnit(), + entity_registry_enabled_default=False, + ), ) @@ -1116,6 +1303,8 @@ def _build_entities( (get_circuits(device.api), CIRCUIT_SENSORS), (get_burners(device.api), BURNER_SENSORS), (get_compressors(device.api), COMPRESSOR_SENSORS), + (get_condensors(device.api), CONDENSOR_SENSORS), + (get_evaporators(device.api), EVAPORATOR_SENSORS), ): entities.extend( ViCareSensor( diff --git a/homeassistant/components/vicare/strings.json b/homeassistant/components/vicare/strings.json index 17eed6fae9842..b6fc180010992 100644 --- a/homeassistant/components/vicare/strings.json +++ b/homeassistant/components/vicare/strings.json @@ -78,6 +78,9 @@ }, "valve": { "name": "Valve" + }, + "ventilation_frost_protection": { + "name": "Ventilation frost protection" } }, "button": { @@ -212,6 +215,18 @@ "compressor_hours_loadclass5": { "name": "Compressor hours load class 5" }, + "compressor_inlet_pressure": { + "name": "Compressor inlet pressure" + }, + "compressor_inlet_temperature": { + "name": "Compressor inlet temperature" + }, + "compressor_outlet_pressure": { + "name": "Compressor outlet pressure" + }, + "compressor_outlet_temperature": { + "name": "Compressor outlet temperature" + }, "compressor_phase": { "name": "Compressor phase", "state": { @@ -229,6 +244,12 @@ "compressor_starts": { "name": "Compressor starts" }, + "condensor_liquid_temperature": { + "name": "Condensor liquid temperature" + }, + "condensor_subcooling_temperature": { + "name": "Condensor subcooling temperature" + }, "dhw_storage_bottom_temperature": { "name": "DHW storage bottom temperature" }, @@ -303,6 +324,21 @@ "standby": "[%key:common::state::standby%]" } }, + "evaporator_liquid_temperature": { + "name": "Evaporator liquid temperature" + }, + "evaporator_overheat_temperature": { + "name": "Evaporator overheat temperature" + }, + "filter_hours": { + "name": "Filter hours" + }, + "filter_overdue_hours": { + "name": "Filter overdue hours" + }, + "filter_remaining_hours": { + "name": "Filter remaining hours" + }, "fuel_need": { "name": "Fuel need" }, @@ -396,6 +432,9 @@ "hydraulic_separator_temperature": { "name": "Hydraulic separator temperature" }, + "outside_humidity": { + "name": "Outside humidity" + }, "outside_temperature": { "name": "Outside temperature" }, @@ -499,6 +538,15 @@ "spf_total": { "name": "Seasonal performance factor" }, + "supply_fan_hours": { + "name": "Supply fan hours" + }, + "supply_fan_speed": { + "name": "Supply fan speed" + }, + "supply_humidity": { + "name": "Supply humidity" + }, "supply_pressure": { "name": "Supply pressure" }, @@ -508,6 +556,9 @@ "valve_position": { "name": "Valve position" }, + "ventilation_input_volumeflow": { + "name": "Ventilation input volume flow" + }, "ventilation_level": { "name": "Ventilation level", "state": { @@ -518,6 +569,9 @@ "standby": "[%key:common::state::standby%]" } }, + "ventilation_output_volumeflow": { + "name": "Ventilation output volume flow" + }, "ventilation_reason": { "name": "Ventilation reason", "state": { diff --git a/homeassistant/components/vicare/utils.py b/homeassistant/components/vicare/utils.py index 4d831cf625a12..f8d8b7b3c2e68 100644 --- a/homeassistant/components/vicare/utils.py +++ b/homeassistant/components/vicare/utils.py @@ -130,6 +130,28 @@ def get_compressors(device: PyViCareDevice) -> list[PyViCareHeatingDeviceCompone return [] +def get_condensors(device: PyViCareDevice) -> list[PyViCareHeatingDeviceComponent]: + """Return the list of condensors.""" + try: + return device.condensors + except PyViCareNotSupportedFeatureError: + _LOGGER.debug("No condensors found") + except AttributeError as error: + _LOGGER.debug("No condensors found: %s", error) + return [] + + +def get_evaporators(device: PyViCareDevice) -> list[PyViCareHeatingDeviceComponent]: + """Return the list of evaporators.""" + try: + return device.evaporators + except PyViCareNotSupportedFeatureError: + _LOGGER.debug("No evaporators found") + except AttributeError as error: + _LOGGER.debug("No evaporators found: %s", error) + return [] + + def filter_state(state: str) -> str | None: """Return the state if not 'nothing' or 'unknown'.""" return None if state in ("nothing", "unknown") else state diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 411c0a35a077b..73d3e4354322e 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -9,7 +9,12 @@ from homeassistant.helpers import config_validation as cv from .const import DOMAIN -from .coordinator import XboxConfigEntry, XboxUpdateCoordinator +from .coordinator import ( + XboxConfigEntry, + XboxConsolesCoordinator, + XboxCoordinators, + XboxUpdateCoordinator, +) _LOGGER = logging.getLogger(__name__) @@ -30,7 +35,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool coordinator = XboxUpdateCoordinator(hass, entry) await coordinator.async_config_entry_first_refresh() - entry.runtime_data = coordinator + consoles = XboxConsolesCoordinator(hass, entry, coordinator) + + entry.runtime_data = XboxCoordinators(coordinator, consoles) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -53,16 +60,14 @@ async def async_migrate_unique_id(hass: HomeAssistant, entry: XboxConfigEntry) - if entry.version == 1 and entry.minor_version < 2: # Migrate unique_id from `xbox` to account xuid and # change generic entry name to user's gamertag + coordinator = entry.runtime_data.status + xuid = coordinator.client.xuid + gamertag = coordinator.data.presence[xuid].gamertag + return hass.config_entries.async_update_entry( entry, - unique_id=entry.runtime_data.client.xuid, - title=( - entry.runtime_data.data.presence[ - entry.runtime_data.client.xuid - ].gamertag - if entry.title == "Home Assistant Cloud" - else entry.title - ), + unique_id=xuid, + title=(gamertag if entry.title == "Home Assistant Cloud" else entry.title), minor_version=2, ) diff --git a/homeassistant/components/xbox/binary_sensor.py b/homeassistant/components/xbox/binary_sensor.py index cfad0bcef9040..2a1a18f6f1db2 100644 --- a/homeassistant/components/xbox/binary_sensor.py +++ b/homeassistant/components/xbox/binary_sensor.py @@ -44,7 +44,6 @@ class XboxBinarySensorEntityDescription( """Xbox binary sensor description.""" is_on_fn: Callable[[Person], bool | None] - deprecated: bool | None = None def profile_attributes(person: Person, _: Title | None) -> dict[str, Any]: @@ -112,7 +111,7 @@ async def async_setup_entry( ) -> None: """Set up Xbox Live friends.""" xuids_added: set[str] = set() - coordinator = entry.runtime_data + coordinator = entry.runtime_data.status @callback def add_entities() -> None: @@ -120,16 +119,16 @@ def add_entities() -> None: current_xuids = set(coordinator.data.presence) if new_xuids := current_xuids - xuids_added: - for xuid in new_xuids: - async_add_entities( - [ - XboxBinarySensorEntity(coordinator, xuid, description) - for description in SENSOR_DESCRIPTIONS - if check_deprecated_entity( - hass, xuid, description, BINARY_SENSOR_DOMAIN - ) - ] - ) + async_add_entities( + [ + XboxBinarySensorEntity(coordinator, xuid, description) + for xuid in new_xuids + for description in SENSOR_DESCRIPTIONS + if check_deprecated_entity( + hass, xuid, description, BINARY_SENSOR_DOMAIN + ) + ] + ) xuids_added |= new_xuids xuids_added &= current_xuids diff --git a/homeassistant/components/xbox/const.py b/homeassistant/components/xbox/const.py index 1a38e0b3cdd1e..8879ef6d90728 100644 --- a/homeassistant/components/xbox/const.py +++ b/homeassistant/components/xbox/const.py @@ -4,5 +4,3 @@ OAUTH2_AUTHORIZE = "https://login.live.com/oauth20_authorize.srf" OAUTH2_TOKEN = "https://login.live.com/oauth20_token.srf" - -EVENT_NEW_FAVORITE = "xbox/new_favorite" diff --git a/homeassistant/components/xbox/coordinator.py b/homeassistant/components/xbox/coordinator.py index cf25e3be73480..05cf25af9b1fe 100644 --- a/homeassistant/components/xbox/coordinator.py +++ b/homeassistant/components/xbox/coordinator.py @@ -35,7 +35,7 @@ _LOGGER = logging.getLogger(__name__) -type XboxConfigEntry = ConfigEntry[XboxUpdateCoordinator] +type XboxConfigEntry = ConfigEntry[XboxCoordinators] @dataclass @@ -55,17 +55,25 @@ class XboxData: title_info: dict[str, Title] = field(default_factory=dict) +@dataclass +class XboxCoordinators: + """Xbox coordinators.""" + + status: XboxUpdateCoordinator + consoles: XboxConsolesCoordinator + + class XboxUpdateCoordinator(DataUpdateCoordinator[XboxData]): """Store Xbox Console Status.""" - config_entry: ConfigEntry + config_entry: XboxConfigEntry consoles: SmartglassConsoleList client: XboxLiveClient def __init__( self, hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: XboxConfigEntry, ) -> None: """Initialize.""" super().__init__( @@ -280,3 +288,43 @@ def configured_as_entry(self) -> set[str]: for entry in self.hass.config_entries.async_entries(DOMAIN) if entry.unique_id is not None } + + +class XboxConsolesCoordinator(DataUpdateCoordinator[SmartglassConsoleList]): + """Update list of Xbox consoles.""" + + config_entry: XboxConfigEntry + + def __init__( + self, + hass: HomeAssistant, + config_entry: XboxConfigEntry, + coordinator: XboxUpdateCoordinator, + ) -> None: + """Initialize.""" + super().__init__( + hass, + _LOGGER, + config_entry=config_entry, + name=DOMAIN, + update_interval=timedelta(minutes=10), + ) + self.client = coordinator.client + self.async_set_updated_data(coordinator.consoles) + + async def _async_update_data(self) -> SmartglassConsoleList: + """Fetch console data.""" + + try: + return await self.client.smartglass.get_console_list() + except TimeoutException as e: + raise UpdateFailed( + translation_domain=DOMAIN, + translation_key="timeout_exception", + ) from e + except (RequestError, HTTPStatusError) as e: + _LOGGER.debug("Xbox exception:", exc_info=True) + raise UpdateFailed( + translation_domain=DOMAIN, + translation_key="request_exception", + ) from e diff --git a/homeassistant/components/xbox/entity.py b/homeassistant/components/xbox/entity.py index 84e296f58eaad..4aa633a4266cd 100644 --- a/homeassistant/components/xbox/entity.py +++ b/homeassistant/components/xbox/entity.py @@ -94,8 +94,7 @@ def extra_state_attributes(self) -> Mapping[str, float | None] | None: """Return entity specific state attributes.""" return ( fn(self.data, self.title_info) - if hasattr(self.entity_description, "attributes_fn") - and (fn := self.entity_description.attributes_fn) + if (fn := self.entity_description.attributes_fn) else super().extra_state_attributes ) @@ -122,7 +121,7 @@ def __init__( self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, console.id)}, manufacturer="Microsoft", - model=MAP_MODEL.get(self._console.console_type, "Unknown"), + model=MAP_MODEL.get(self._console.console_type), name=console.name, ) @@ -135,11 +134,11 @@ def data(self) -> ConsoleData: def check_deprecated_entity( hass: HomeAssistant, xuid: str, - entity_description: EntityDescription, + entity_description: XboxBaseEntityDescription, entity_domain: str, ) -> bool: """Check for deprecated entity and remove it.""" - if not getattr(entity_description, "deprecated", False): + if not entity_description.deprecated: return True ent_reg = er.async_get(hass) if entity_id := ent_reg.async_get_entity_id( diff --git a/homeassistant/components/xbox/image.py b/homeassistant/components/xbox/image.py index 60d574b33309c..ac5d3d580a945 100644 --- a/homeassistant/components/xbox/image.py +++ b/homeassistant/components/xbox/image.py @@ -64,7 +64,7 @@ async def async_setup_entry( ) -> None: """Set up Xbox images.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.status xuids_added: set[str] = set() diff --git a/homeassistant/components/xbox/manifest.json b/homeassistant/components/xbox/manifest.json index f2b27bd3bacad..17e21ef7d1929 100644 --- a/homeassistant/components/xbox/manifest.json +++ b/homeassistant/components/xbox/manifest.json @@ -3,7 +3,7 @@ "name": "Xbox", "codeowners": ["@hunterjm", "@tr4nt0r"], "config_flow": true, - "dependencies": ["auth", "application_credentials"], + "dependencies": ["application_credentials"], "dhcp": [ { "hostname": "xbox*" diff --git a/homeassistant/components/xbox/media_player.py b/homeassistant/components/xbox/media_player.py index c43a063475163..73bee4ea901f3 100644 --- a/homeassistant/components/xbox/media_player.py +++ b/homeassistant/components/xbox/media_player.py @@ -56,7 +56,7 @@ async def async_setup_entry( ) -> None: """Set up Xbox media_player from a config entry.""" - coordinator = entry.runtime_data + coordinator = entry.runtime_data.status async_add_entities( [ diff --git a/homeassistant/components/xbox/media_source.py b/homeassistant/components/xbox/media_source.py index bcefa6829a91a..76baeee166ebb 100644 --- a/homeassistant/components/xbox/media_source.py +++ b/homeassistant/components/xbox/media_source.py @@ -112,7 +112,7 @@ async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia: translation_key="account_not_configured", ) from e - client = entry.runtime_data.client + client = entry.runtime_data.status.client if identifier.media_type in (ATTR_GAMECLIPS, ATTR_COMMUNITY_GAMECLIPS): try: @@ -302,7 +302,7 @@ async def _build_game_library(self, entry: XboxConfigEntry) -> BrowseMediaSource async def _build_games(self, entry: XboxConfigEntry) -> list[BrowseMediaSource]: """List Xbox games for the selected account.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client if TYPE_CHECKING: assert entry.unique_id fields = [ @@ -346,7 +346,7 @@ async def _build_game_title( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> BrowseMediaSource: """Display game title.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client try: game = (await client.titlehub.get_title_info(identifier.title_id)).titles[0] except TimeoutException as e: @@ -402,7 +402,7 @@ async def _build_game_media( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> BrowseMediaSource: """List game media.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client try: game = (await client.titlehub.get_title_info(identifier.title_id)).titles[0] except TimeoutException as e: @@ -439,7 +439,7 @@ async def _build_media_items_gameclips( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> list[BrowseMediaSource]: """List media items.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client if identifier.media_type != ATTR_GAMECLIPS: return [] @@ -483,7 +483,7 @@ async def _build_media_items_community_gameclips( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> list[BrowseMediaSource]: """List media items.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client if identifier.media_type != ATTR_COMMUNITY_GAMECLIPS: return [] @@ -527,7 +527,7 @@ async def _build_media_items_screenshots( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> list[BrowseMediaSource]: """List media items.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client if identifier.media_type != ATTR_SCREENSHOTS: return [] @@ -571,7 +571,7 @@ async def _build_media_items_community_screenshots( self, entry: XboxConfigEntry, identifier: XboxMediaSourceIdentifier ) -> list[BrowseMediaSource]: """List media items.""" - client = entry.runtime_data.client + client = entry.runtime_data.status.client if identifier.media_type != ATTR_COMMUNITY_SCREENSHOTS: return [] @@ -640,7 +640,7 @@ def _build_media_items_promotional( def gamerpic(config_entry: XboxConfigEntry) -> str | None: """Return gamerpic.""" - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.status if TYPE_CHECKING: assert config_entry.unique_id person = coordinator.data.presence[coordinator.client.xuid] diff --git a/homeassistant/components/xbox/remote.py b/homeassistant/components/xbox/remote.py index 9b42a6b265cfe..2e10ce89f68ea 100644 --- a/homeassistant/components/xbox/remote.py +++ b/homeassistant/components/xbox/remote.py @@ -27,7 +27,7 @@ async def async_setup_entry( async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Xbox media_player from a config entry.""" - coordinator = entry.runtime_data + coordinator = entry.runtime_data.status async_add_entities( [XboxRemote(console, coordinator) for console in coordinator.consoles.result] diff --git a/homeassistant/components/xbox/sensor.py b/homeassistant/components/xbox/sensor.py index 69bd9d4f5046c..45d5c7a6e47f7 100644 --- a/homeassistant/components/xbox/sensor.py +++ b/homeassistant/components/xbox/sensor.py @@ -9,20 +9,32 @@ from typing import Any from pythonxbox.api.provider.people.models import Person +from pythonxbox.api.provider.smartglass.models import SmartglassConsole, StorageDevice from pythonxbox.api.provider.titlehub.models import Title from homeassistant.components.sensor import ( DOMAIN as SENSOR_DOMAIN, + EntityCategory, SensorDeviceClass, SensorEntity, SensorEntityDescription, + SensorStateClass, ) +from homeassistant.const import CONF_NAME, UnitOfInformation from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.typing import StateType - -from .coordinator import XboxConfigEntry -from .entity import XboxBaseEntity, XboxBaseEntityDescription, check_deprecated_entity +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import XboxConfigEntry, XboxConsolesCoordinator +from .entity import ( + MAP_MODEL, + XboxBaseEntity, + XboxBaseEntityDescription, + check_deprecated_entity, +) MAP_JOIN_RESTRICTIONS = { "local": "invite_only", @@ -44,6 +56,8 @@ class XboxSensor(StrEnum): FRIENDS = "friends" IN_PARTY = "in_party" JOIN_RESTRICTIONS = "join_restrictions" + TOTAL_STORAGE = "total_storage" + FREE_STORAGE = "free_storage" @dataclass(kw_only=True, frozen=True) @@ -51,7 +65,15 @@ class XboxSensorEntityDescription(XboxBaseEntityDescription, SensorEntityDescrip """Xbox sensor description.""" value_fn: Callable[[Person, Title | None], StateType | datetime] - deprecated: bool | None = None + + +@dataclass(kw_only=True, frozen=True) +class XboxStorageDeviceSensorEntityDescription( + XboxBaseEntityDescription, SensorEntityDescription +): + """Xbox console sensor description.""" + + value_fn: Callable[[StorageDevice], StateType] def now_playing_attributes(_: Person, title: Title | None) -> dict[str, Any]: @@ -196,6 +218,31 @@ def title_logo(_: Person, title: Title | None) -> str | None: ), ) +STORAGE_SENSOR_DESCRIPTIONS: tuple[XboxStorageDeviceSensorEntityDescription, ...] = ( + XboxStorageDeviceSensorEntityDescription( + key=XboxSensor.TOTAL_STORAGE, + translation_key=XboxSensor.TOTAL_STORAGE, + value_fn=lambda x: x.total_space_bytes, + device_class=SensorDeviceClass.DATA_SIZE, + native_unit_of_measurement=UnitOfInformation.BYTES, + suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES, + suggested_display_precision=0, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + ), + XboxStorageDeviceSensorEntityDescription( + key=XboxSensor.FREE_STORAGE, + translation_key=XboxSensor.FREE_STORAGE, + value_fn=lambda x: x.free_space_bytes, + device_class=SensorDeviceClass.DATA_SIZE, + native_unit_of_measurement=UnitOfInformation.BYTES, + suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES, + suggested_display_precision=0, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -204,7 +251,7 @@ async def async_setup_entry( ) -> None: """Set up Xbox Live friends.""" xuids_added: set[str] = set() - coordinator = config_entry.runtime_data + coordinator = config_entry.runtime_data.status @callback def add_entities() -> None: @@ -212,22 +259,34 @@ def add_entities() -> None: current_xuids = set(coordinator.data.presence) if new_xuids := current_xuids - xuids_added: - for xuid in new_xuids: - async_add_entities( - [ - XboxSensorEntity(coordinator, xuid, description) - for description in SENSOR_DESCRIPTIONS - if check_deprecated_entity( - hass, xuid, description, SENSOR_DOMAIN - ) - ] - ) + async_add_entities( + [ + XboxSensorEntity(coordinator, xuid, description) + for xuid in new_xuids + for description in SENSOR_DESCRIPTIONS + if check_deprecated_entity(hass, xuid, description, SENSOR_DOMAIN) + ] + ) xuids_added |= new_xuids xuids_added &= current_xuids coordinator.async_add_listener(add_entities) add_entities() + consoles_coordinator = config_entry.runtime_data.consoles + + async_add_entities( + [ + XboxStorageDeviceSensorEntity( + console, storage_device, consoles_coordinator, description + ) + for description in STORAGE_SENSOR_DESCRIPTIONS + for console in coordinator.consoles.result + if console.storage_devices + for storage_device in console.storage_devices + ] + ) + class XboxSensorEntity(XboxBaseEntity, SensorEntity): """Representation of a Xbox presence state.""" @@ -238,3 +297,62 @@ class XboxSensorEntity(XboxBaseEntity, SensorEntity): def native_value(self) -> StateType | datetime: """Return the state of the requested attribute.""" return self.entity_description.value_fn(self.data, self.title_info) + + +class XboxStorageDeviceSensorEntity( + CoordinatorEntity[XboxConsolesCoordinator], SensorEntity +): + """Console storage device entity for the Xbox integration.""" + + _attr_has_entity_name = True + entity_description: XboxStorageDeviceSensorEntityDescription + + def __init__( + self, + console: SmartglassConsole, + storage_device: StorageDevice, + coordinator: XboxConsolesCoordinator, + entity_description: XboxStorageDeviceSensorEntityDescription, + ) -> None: + """Initialize the Xbox Console entity.""" + super().__init__(coordinator) + self.entity_description = entity_description + self.client = coordinator.client + self._console = console + self._storage_device = storage_device + self._attr_unique_id = ( + f"{console.id}_{storage_device.storage_device_id}_{entity_description.key}" + ) + self._attr_translation_placeholders = { + CONF_NAME: storage_device.storage_device_name + } + + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, console.id)}, + manufacturer="Microsoft", + model=MAP_MODEL.get(self._console.console_type, "Unknown"), + name=console.name, + ) + + @property + def data(self): + """Storage device data.""" + consoles = self.coordinator.data.result + console = next((c for c in consoles if c.id == self._console.id), None) + if not console or not console.storage_devices: + return None + + return next( + ( + d + for d in console.storage_devices + if d.storage_device_id == self._storage_device.storage_device_id + ), + None, + ) + + @property + def native_value(self) -> StateType: + """Return the state of the requested attribute.""" + + return self.entity_description.value_fn(self.data) if self.data else None diff --git a/homeassistant/components/xbox/strings.json b/homeassistant/components/xbox/strings.json index 61e958f0d1dab..096747c816ece 100644 --- a/homeassistant/components/xbox/strings.json +++ b/homeassistant/components/xbox/strings.json @@ -65,6 +65,9 @@ "name": "Following", "unit_of_measurement": "people" }, + "free_storage": { + "name": "Free space - {name}" + }, "friends": { "name": "Friends", "unit_of_measurement": "[%key:component::xbox::entity::sensor::following::unit_of_measurement%]" @@ -105,6 +108,9 @@ }, "status": { "name": "Status" + }, + "total_storage": { + "name": "Total space - {name}" } } }, diff --git a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py index 5b1eed18014d6..1754de9f7738f 100644 --- a/homeassistant/components/zha/repairs/wrong_silabs_firmware.py +++ b/homeassistant/components/zha/repairs/wrong_silabs_firmware.py @@ -71,7 +71,20 @@ async def warn_on_wrong_silabs_firmware(hass: HomeAssistant, device: str) -> boo if device.startswith("socket://"): return False - app_type = await probe_silabs_firmware_type(device) + app_type = await probe_silabs_firmware_type( + device, + bootloader_reset_methods=(), + application_probe_methods=[ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, 115200), + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + (ApplicationType.CPC, 460800), + (ApplicationType.CPC, 230400), + (ApplicationType.CPC, 115200), + (ApplicationType.ROUTER, 115200), + ], + ) if app_type is None: # Failed to probe, we can't tell if the wrong firmware is installed diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 51dcd3fa13e29..beb86c1fd4689 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -645,12 +645,24 @@ class FlowHandler(Generic[_FlowContextT, _FlowResultT, _HandlerT]): __progress_task: asyncio.Task[Any] | None = None __no_progress_task_reported = False deprecated_show_progress = False - _progress_step_data: ProgressStepData[_FlowResultT] = { - "tasks": {}, - "abort_reason": "", - "abort_description_placeholders": MappingProxyType({}), - "next_step_result": None, - } + __progress_step_data: ProgressStepData[_FlowResultT] | None = None + + @property + def _progress_step_data(self) -> ProgressStepData[_FlowResultT]: + """Return progress step data. + + A property is used instead of a simple attribute as derived classes + do not call super().__init__. + The property makes sure that the dict is initialized if needed. + """ + if not self.__progress_step_data: + self.__progress_step_data = { + "tasks": {}, + "abort_reason": "", + "abort_description_placeholders": MappingProxyType({}), + "next_step_result": None, + } + return self.__progress_step_data @property def source(self) -> str | None: @@ -777,9 +789,10 @@ async def async_step__progress_step_abort( self, user_input: dict[str, Any] | None = None ) -> _FlowResultT: """Abort the flow.""" + progress_step_data = self._progress_step_data return self.async_abort( - reason=self._progress_step_data["abort_reason"], - description_placeholders=self._progress_step_data[ + reason=progress_step_data["abort_reason"], + description_placeholders=progress_step_data[ "abort_description_placeholders" ], ) @@ -795,14 +808,15 @@ async def async_step__progress_step_progress_done( without using async_show_progress_done. If no next step is set, abort the flow. """ - if self._progress_step_data["next_step_result"] is None: + progress_step_data = self._progress_step_data + if (next_step_result := progress_step_data["next_step_result"]) is None: return self.async_abort( - reason=self._progress_step_data["abort_reason"], - description_placeholders=self._progress_step_data[ + reason=progress_step_data["abort_reason"], + description_placeholders=progress_step_data[ "abort_description_placeholders" ], ) - return self._progress_step_data["next_step_result"] + return next_step_result @callback def async_external_step( @@ -1021,9 +1035,9 @@ async def wrapper( self: FlowHandler[Any, ResultT], *args: P.args, **kwargs: P.kwargs ) -> ResultT: step_id = func.__name__.replace("async_step_", "") - + progress_step_data = self._progress_step_data # Check if we have a progress task running - progress_task = self._progress_step_data["tasks"].get(step_id) + progress_task = progress_step_data["tasks"].get(step_id) if progress_task is None: # First call - create and start the progress task @@ -1031,30 +1045,30 @@ async def wrapper( func(self, *args, **kwargs), # type: ignore[arg-type] f"Progress step {step_id}", ) - self._progress_step_data["tasks"][step_id] = progress_task - - if not progress_task.done(): - # Handle description placeholders - placeholders = None - if description_placeholders is not None: - if callable(description_placeholders): - placeholders = description_placeholders(self) - else: - placeholders = description_placeholders - - return self.async_show_progress( - step_id=step_id, - progress_action=step_id, - progress_task=progress_task, - description_placeholders=placeholders, - ) + progress_step_data["tasks"][step_id] = progress_task + + if not progress_task.done(): + # Handle description placeholders + placeholders = None + if description_placeholders is not None: + if callable(description_placeholders): + placeholders = description_placeholders(self) + else: + placeholders = description_placeholders + + return self.async_show_progress( + step_id=step_id, + progress_action=step_id, + progress_task=progress_task, + description_placeholders=placeholders, + ) # Task is done or this is a subsequent call try: - self._progress_step_data["next_step_result"] = await progress_task + progress_step_data["next_step_result"] = await progress_task except AbortFlow as err: - self._progress_step_data["abort_reason"] = err.reason - self._progress_step_data["abort_description_placeholders"] = ( + progress_step_data["abort_reason"] = err.reason + progress_step_data["abort_description_placeholders"] = ( err.description_placeholders or {} ) return self.async_show_progress_done( @@ -1062,7 +1076,7 @@ async def wrapper( ) finally: # Clean up task reference - self._progress_step_data["tasks"].pop(step_id, None) + progress_step_data["tasks"].pop(step_id, None) return self.async_show_progress_done( next_step_id="_progress_step_progress_done" diff --git a/homeassistant/helpers/template/extensions/base.py b/homeassistant/helpers/template/extensions/base.py index 8fbe7312f5560..0449cf974fc88 100644 --- a/homeassistant/helpers/template/extensions/base.py +++ b/homeassistant/helpers/template/extensions/base.py @@ -4,12 +4,14 @@ from collections.abc import Callable from dataclasses import dataclass -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, NoReturn from jinja2.ext import Extension from jinja2.nodes import Node from jinja2.parser import Parser +from homeassistant.exceptions import TemplateError + if TYPE_CHECKING: from homeassistant.core import HomeAssistant from homeassistant.helpers.template import TemplateEnvironment @@ -50,8 +52,17 @@ def __init__( if template_func.requires_hass and self.environment.hass is None: continue - # Skip functions not allowed in limited environments + # Register unsupported stub for functions not allowed in limited environments if self.environment.limited and not template_func.limited_ok: + unsupported_func = self._create_unsupported_function( + template_func.name + ) + if template_func.as_global: + environment.globals[template_func.name] = unsupported_func + if template_func.as_filter: + environment.filters[template_func.name] = unsupported_func + if template_func.as_test: + environment.tests[template_func.name] = unsupported_func continue if template_func.as_global: @@ -61,6 +72,17 @@ def __init__( if template_func.as_test: environment.tests[template_func.name] = template_func.func + @staticmethod + def _create_unsupported_function(name: str) -> Callable[[], NoReturn]: + """Create a function that raises an error for unsupported functions in limited templates.""" + + def unsupported(*args: Any, **kwargs: Any) -> NoReturn: + raise TemplateError( + f"Use of '{name}' is not supported in limited templates" + ) + + return unsupported + @property def hass(self) -> HomeAssistant: """Return the Home Assistant instance. diff --git a/requirements_all.txt b/requirements_all.txt index 232d1a7b79380..51afa65165d7a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -273,7 +273,7 @@ aioharmony==0.5.3 aiohasupervisor==0.3.3 # homeassistant.components.home_connect -aiohomeconnect==0.23.0 +aiohomeconnect==0.23.1 # homeassistant.components.homekit_controller aiohomekit==3.2.20 @@ -1395,7 +1395,7 @@ loqedAPI==2.1.10 luftdaten==0.7.4 # homeassistant.components.lunatone -lunatone-rest-api-client==0.5.3 +lunatone-rest-api-client==0.5.7 # homeassistant.components.lupusec lupupy==0.3.2 @@ -2449,7 +2449,7 @@ python-clementine-remote==1.0.1 python-digitalocean==1.13.2 # homeassistant.components.ecobee -python-ecobee-api==0.2.20 +python-ecobee-api==0.3.2 # homeassistant.components.etherscan python-etherscan-api==0.0.3 @@ -2604,7 +2604,7 @@ pyvera==0.3.16 pyversasense==0.0.6 # homeassistant.components.vesync -pyvesync==3.2.1 +pyvesync==3.2.2 # homeassistant.components.vizio pyvizio==0.1.61 @@ -3047,7 +3047,7 @@ unifi_ap==0.0.2 unifiled==0.11 # homeassistant.components.homeassistant_hardware -universal-silabs-flasher==0.0.37 +universal-silabs-flasher==0.1.0 # homeassistant.components.upb upb-lib==0.6.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8ea343b8a35ec..16735a4eb3aec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -258,7 +258,7 @@ aioharmony==0.5.3 aiohasupervisor==0.3.3 # homeassistant.components.home_connect -aiohomeconnect==0.23.0 +aiohomeconnect==0.23.1 # homeassistant.components.homekit_controller aiohomekit==3.2.20 @@ -1199,7 +1199,7 @@ loqedAPI==2.1.10 luftdaten==0.7.4 # homeassistant.components.lunatone -lunatone-rest-api-client==0.5.3 +lunatone-rest-api-client==0.5.7 # homeassistant.components.lupusec lupupy==0.3.2 @@ -2036,7 +2036,7 @@ python-awair==0.2.4 python-bsblan==3.1.0 # homeassistant.components.ecobee -python-ecobee-api==0.2.20 +python-ecobee-api==0.3.2 # homeassistant.components.fully_kiosk python-fullykiosk==0.0.14 @@ -2158,7 +2158,7 @@ pyuptimerobot==22.2.0 pyvera==0.3.16 # homeassistant.components.vesync -pyvesync==3.2.1 +pyvesync==3.2.2 # homeassistant.components.vizio pyvizio==0.1.61 @@ -2514,7 +2514,7 @@ ultraheat-api==0.5.7 unifi-discovery==1.2.0 # homeassistant.components.homeassistant_hardware -universal-silabs-flasher==0.0.37 +universal-silabs-flasher==0.1.0 # homeassistant.components.upb upb-lib==0.6.1 diff --git a/script/licenses.py b/script/licenses.py index 3c77618f4219e..15d10643fec35 100644 --- a/script/licenses.py +++ b/script/licenses.py @@ -203,7 +203,6 @@ def from_dict(cls, data: PackageMetadata) -> PackageDefinition: "sharp_aquos_rc", # https://github.com/jmoore987/sharp_aquos_rc/pull/14 "tapsaff", # https://github.com/bazwilliams/python-taps-aff/pull/5 "ujson", # https://github.com/ultrajson/ultrajson/blob/main/LICENSE.txt - "wsproto", # https://github.com/python-hyper/wsproto/pull/194 } # fmt: off diff --git a/tests/components/aussie_broadband/test_init.py b/tests/components/aussie_broadband/test_init.py index 825c10e92eefe..3b17938a611d9 100644 --- a/tests/components/aussie_broadband/test_init.py +++ b/tests/components/aussie_broadband/test_init.py @@ -28,6 +28,8 @@ async def test_auth_failure(hass: HomeAssistant) -> None: "type": FlowResultType.FORM, "flow_id": "mock_flow", "step_id": "reauth_confirm", + "description_placeholders": {"username": "test", "name": "test"}, + "data_schema": None, }, ) as mock_async_step_reauth: await setup_platform(hass, side_effect=AuthenticationException()) diff --git a/tests/components/home_connect/test_select.py b/tests/components/home_connect/test_select.py index a426380827622..80f2ecaa7200e 100644 --- a/tests/components/home_connect/test_select.py +++ b/tests/components/home_connect/test_select.py @@ -347,6 +347,7 @@ async def test_filter_programs( ], indirect=["appliance"], ) +@pytest.mark.parametrize("program_value", ["A not known program", None]) async def test_select_program_functionality( hass: HomeAssistant, client: MagicMock, @@ -359,6 +360,7 @@ async def test_select_program_functionality( program_key: ProgramKey, program_to_set: str, event_key: EventKey, + program_value: str, ) -> None: """Test select functionality.""" assert await integration_setup(client) @@ -389,7 +391,7 @@ async def test_select_program_functionality( timestamp=0, level="", handling="", - value="A not known program", + value=program_value, ) ] ), diff --git a/tests/components/home_connect/test_sensor.py b/tests/components/home_connect/test_sensor.py index fe8a3ab4be076..0fac889237bce 100644 --- a/tests/components/home_connect/test_sensor.py +++ b/tests/components/home_connect/test_sensor.py @@ -75,6 +75,17 @@ }, } + +EVENT_PROG_UPDATE_3 = { + EventType.EVENT: { + EventKey.BSH_COMMON_OPTION_REMAINING_PROGRAM_TIME: None, + EventKey.BSH_COMMON_OPTION_PROGRAM_PROGRESS: 99, + }, + EventType.STATUS: { + EventKey.BSH_COMMON_STATUS_OPERATION_STATE: "BSH.Common.EnumType.OperationState.Run", + }, +} + EVENT_PROG_END = { EventType.STATUS: { EventKey.BSH_COMMON_STATUS_OPERATION_STATE: "BSH.Common.EnumType.OperationState.Ready", @@ -266,6 +277,7 @@ async def test_sensor_entity_availability( EVENT_PROG_RUN, EVENT_PROG_UPDATE_1, EVENT_PROG_UPDATE_2, + EVENT_PROG_UPDATE_3, EVENT_PROG_END, ) @@ -276,6 +288,7 @@ async def test_sensor_entity_availability( "run", "run", "run", + "run", "ready", ), "sensor.dishwasher_program_finish_time": ( @@ -283,6 +296,7 @@ async def test_sensor_entity_availability( "2021-01-09T12:00:00+00:00", "2021-01-09T12:00:00+00:00", "2021-01-09T12:00:20+00:00", + STATE_UNKNOWN, "unavailable", ), "sensor.dishwasher_program_progress": ( @@ -290,6 +304,7 @@ async def test_sensor_entity_availability( "60", "80", "99", + "99", "unavailable", ), } @@ -535,6 +550,14 @@ async def test_remaining_prog_time_edge_cases( "open", "Dishwasher", ), + ( + "sensor.dishwasher_door", + EventKey.BSH_COMMON_STATUS_DOOR_STATE, + EventType.STATUS, + None, + STATE_UNKNOWN, + "Dishwasher", + ), ( "sensor.fridgefreezer_freezer_door_alarm", "EVENT_NOT_IN_STATUS_YET_SO_SET_TO_OFF", @@ -567,6 +590,14 @@ async def test_remaining_prog_time_edge_cases( "confirmed", "FridgeFreezer", ), + ( + "sensor.fridgefreezer_freezer_door_alarm", + EventKey.REFRIGERATION_FRIDGE_FREEZER_EVENT_DOOR_ALARM_FREEZER, + EventType.EVENT, + None, + STATE_UNKNOWN, + "FridgeFreezer", + ), ( "sensor.coffeemaker_bean_container_empty", EventType.EVENT, @@ -599,6 +630,14 @@ async def test_remaining_prog_time_edge_cases( "confirmed", "CoffeeMaker", ), + ( + "sensor.coffeemaker_bean_container_empty", + EventKey.CONSUMER_PRODUCTS_COFFEE_MAKER_EVENT_BEAN_CONTAINER_EMPTY, + EventType.EVENT, + None, + STATE_UNKNOWN, + "CoffeeMaker", + ), ], indirect=["appliance"], ) @@ -610,7 +649,7 @@ async def test_sensors_states( entity_id: str, event_key: EventKey, event_type: EventType, - event_value_update: str, + event_value_update: str | None, appliance: HomeAppliance, expected: str, ) -> None: diff --git a/tests/components/homeassistant_connect_zbt2/test_config_flow.py b/tests/components/homeassistant_connect_zbt2/test_config_flow.py index 14033e504cfce..ecf165461e276 100644 --- a/tests/components/homeassistant_connect_zbt2/test_config_flow.py +++ b/tests/components/homeassistant_connect_zbt2/test_config_flow.py @@ -16,6 +16,7 @@ from homeassistant.components.homeassistant_hardware.util import ( ApplicationType, FirmwareInfo, + ResetTarget, ) from homeassistant.components.usb import USBDevice from homeassistant.config_entries import ConfigFlowResult @@ -367,7 +368,10 @@ async def test_options_flow( # Verify async_flash_silabs_firmware was called with ZBT-2's reset methods assert flash_mock.call_count == 1 - assert flash_mock.mock_calls[0].kwargs["bootloader_reset_methods"] == ["rts_dtr"] + assert flash_mock.mock_calls[0].kwargs["bootloader_reset_methods"] == [ + ResetTarget.RTS_DTR, + ResetTarget.BAUDRATE, + ] flows = hass.config_entries.flow.async_progress() diff --git a/tests/components/homeassistant_hardware/test_config_flow.py b/tests/components/homeassistant_hardware/test_config_flow.py index 746ed6070eab5..657a836a581ea 100644 --- a/tests/components/homeassistant_hardware/test_config_flow.py +++ b/tests/components/homeassistant_hardware/test_config_flow.py @@ -304,6 +304,7 @@ async def mock_flash_firmware( fw_data: bytes, expected_installed_firmware_type: ApplicationType, bootloader_reset_methods: Sequence[ResetTarget] = (), + application_probe_methods: Sequence[tuple[ApplicationType, int]] = (), progress_callback: Callable[[int, int], None] | None = None, *, domain: str = "homeassistant_hardware", diff --git a/tests/components/homeassistant_hardware/test_update.py b/tests/components/homeassistant_hardware/test_update.py index 3a463362533a7..86e8e6f820d5c 100644 --- a/tests/components/homeassistant_hardware/test_update.py +++ b/tests/components/homeassistant_hardware/test_update.py @@ -173,7 +173,12 @@ async def mock_async_setup_update_entities( class MockFirmwareUpdateEntity(BaseFirmwareUpdateEntity): """Mock SkyConnect firmware update entity.""" - bootloader_reset_methods = [] + BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR] + APPLICATION_PROBE_METHODS = [ + (ApplicationType.GECKO_BOOTLOADER, 115200), + (ApplicationType.EZSP, 115200), + (ApplicationType.SPINEL, 460800), + ] def __init__( self, @@ -320,6 +325,7 @@ async def mock_flash_firmware( fw_data: bytes, expected_installed_firmware_type: ApplicationType, bootloader_reset_methods: Sequence[ResetTarget] = (), + application_probe_methods: Sequence[tuple[ApplicationType, int]] = (), progress_callback: Callable[[int, int], None] | None = None, *, domain: str = "homeassistant_hardware", diff --git a/tests/components/homeassistant_hardware/test_util.py b/tests/components/homeassistant_hardware/test_util.py index ef064ba4195ba..64019c580026f 100644 --- a/tests/components/homeassistant_hardware/test_util.py +++ b/tests/components/homeassistant_hardware/test_util.py @@ -23,6 +23,7 @@ FirmwareInfo, OwningAddon, OwningIntegration, + ResetTarget, async_flash_silabs_firmware, get_otbr_addon_firmware_info, guess_firmware_info, @@ -502,7 +503,14 @@ def probe_app_type() -> None: "homeassistant.components.homeassistant_hardware.util.Flasher", return_value=mock_flasher, ): - result = await probe_silabs_firmware_info("/dev/ttyUSB0") + result = await probe_silabs_firmware_info( + "/dev/ttyUSB0", + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], + ) assert result == expected_fw_info @@ -531,7 +539,14 @@ async def test_probe_silabs_firmware_type( autospec=True, return_value=probe_result, ): - result = await probe_silabs_firmware_type("/dev/ttyUSB0") + result = await probe_silabs_firmware_type( + "/dev/ttyUSB0", + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], + ) assert result == expected @@ -596,7 +611,11 @@ async def mock_flash_firmware( device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], progress_callback=progress_callback, ) @@ -605,7 +624,9 @@ async def mock_flash_firmware( # Verify Flasher was called with correct bootloader_reset parameter assert flasher_mock.call_count == 1 - assert flasher_mock.mock_calls[0].kwargs["bootloader_reset"] == () + assert flasher_mock.mock_calls[0].kwargs["bootloader_reset"] == ( + ResetTarget.RTS_DTR.as_flasher_reset_target(), + ) # Both owning integrations/addons are stopped and restarted assert owner1.temporarily_stop.mock_calls == [ @@ -623,6 +644,28 @@ async def mock_flash_firmware( ] +async def test_async_flash_silabs_firmware_expected_type_not_probed( + hass: HomeAssistant, +) -> None: + """Test firmware flashing requires probing config to exist for firmware type.""" + with pytest.raises( + ValueError, + match=( + r"Expected installed firmware type .*? not in application probe methods .*?" + ), + ): + await async_flash_silabs_firmware( + hass=hass, + device="/dev/ttyUSB0", + fw_data=b"firmware contents", + expected_installed_firmware_type=ApplicationType.SPINEL, + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + ], + ) + + async def test_async_flash_silabs_firmware_flash_failure(hass: HomeAssistant) -> None: """Test async_flash_silabs_firmware flash failure.""" await async_setup_component(hass, "homeassistant_hardware", {}) @@ -659,7 +702,11 @@ async def test_async_flash_silabs_firmware_flash_failure(hass: HomeAssistant) -> device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], ) # Both owning integrations/addons are stopped and restarted @@ -719,7 +766,11 @@ async def test_async_flash_silabs_firmware_probe_failure(hass: HomeAssistant) -> device="/dev/ttyUSB0", fw_data=b"firmware contents", expected_installed_firmware_type=ApplicationType.SPINEL, - bootloader_reset_methods=(), + bootloader_reset_methods=[ResetTarget.RTS_DTR], + application_probe_methods=[ + (ApplicationType.EZSP, 460800), + (ApplicationType.SPINEL, 460800), + ], ) # Both owning integrations/addons are stopped and restarted diff --git a/tests/components/lamarzocco/test_update.py b/tests/components/lamarzocco/test_update.py index 99f85c213812b..42458902411c4 100644 --- a/tests/components/lamarzocco/test_update.py +++ b/tests/components/lamarzocco/test_update.py @@ -62,7 +62,7 @@ async def test_update_process( ), UpdateDetails( status=UpdateStatus.UPDATED, - command_status=None, + command_status=UpdateStatus.UPDATED, progress_info=None, progress_percentage=None, ), diff --git a/tests/components/mobile_app/test_binary_sensor.py b/tests/components/mobile_app/test_binary_sensor.py index 9ffb61f92ab15..d682cc3c5f3e3 100644 --- a/tests/components/mobile_app/test_binary_sensor.py +++ b/tests/components/mobile_app/test_binary_sensor.py @@ -6,9 +6,18 @@ from aiohttp.test_utils import TestClient import pytest -from homeassistant.const import STATE_UNKNOWN +from homeassistant.components.mobile_app.const import ( + ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_ICON, + ATTR_SENSOR_NAME, + ATTR_SENSOR_STATE, + ATTR_SENSOR_TYPE, + ATTR_SENSOR_UNIQUE_ID, +) +from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.dispatcher import async_dispatcher_send async def test_sensor( @@ -298,3 +307,80 @@ async def test_update_sensor_no_state( updated_entity = hass.states.get("binary_sensor.test_1_is_charging") assert updated_entity.state == STATE_UNKNOWN + + +async def test_dispatcher_cleanup_on_unload( + hass: HomeAssistant, + create_registrations: tuple[dict[str, Any], dict[str, Any]], + webhook_client: TestClient, +) -> None: + """Test that dispatcher connections are cleaned up on config entry unload.""" + + webhook_id = create_registrations[1]["webhook_id"] + entry = hass.config_entries.async_entries("mobile_app")[1] + + # Send a dispatcher signal when config entry is loaded + async_dispatcher_send( + hass, + "mobile_app_binary_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test Before Unload", + ATTR_SENSOR_STATE: True, + ATTR_SENSOR_TYPE: "binary_sensor", + ATTR_SENSOR_UNIQUE_ID: "test_before_unload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # Check binary sensor was created + assert hass.states.get("binary_sensor.test_before_unload") is not None + + # Unload the config entry + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + # Send another dispatcher signal after unload + async_dispatcher_send( + hass, + "mobile_app_binary_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test After Unload", + ATTR_SENSOR_STATE: False, + ATTR_SENSOR_TYPE: "binary_sensor", + ATTR_SENSOR_UNIQUE_ID: "test_after_unload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # The binary sensor should not be created because dispatcher was cleaned up + assert hass.states.get("binary_sensor.test_after_unload") is None + + # Reload the config entry + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # Send dispatcher signal after reload + async_dispatcher_send( + hass, + "mobile_app_binary_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test After Reload", + ATTR_SENSOR_STATE: True, + ATTR_SENSOR_TYPE: "binary_sensor", + ATTR_SENSOR_UNIQUE_ID: "test_after_reload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # This binary sensor should be created successfully after reload + assert hass.states.get("binary_sensor.test_after_reload") is not None + assert hass.states.get("binary_sensor.test_after_reload").state == "on" diff --git a/tests/components/mobile_app/test_sensor.py b/tests/components/mobile_app/test_sensor.py index c12a8f6818b67..b68a9496353e8 100644 --- a/tests/components/mobile_app/test_sensor.py +++ b/tests/components/mobile_app/test_sensor.py @@ -7,8 +7,17 @@ from aiohttp.test_utils import TestClient import pytest +from homeassistant.components.mobile_app.const import ( + ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_ICON, + ATTR_SENSOR_NAME, + ATTR_SENSOR_STATE, + ATTR_SENSOR_TYPE, + ATTR_SENSOR_UNIQUE_ID, +) from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( + CONF_WEBHOOK_ID, PERCENTAGE, STATE_UNAVAILABLE, STATE_UNKNOWN, @@ -16,6 +25,7 @@ ) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.util.unit_system import ( METRIC_SYSTEM, US_CUSTOMARY_SYSTEM, @@ -697,3 +707,80 @@ async def test_recreate_correct_from_entity_registry( assert entity_entry.capabilities == { "state_class": "measurement", } + + +async def test_dispatcher_cleanup_on_unload( + hass: HomeAssistant, + create_registrations: tuple[dict[str, Any], dict[str, Any]], + webhook_client: TestClient, +) -> None: + """Test that dispatcher connections are cleaned up on config entry unload.""" + + webhook_id = create_registrations[1]["webhook_id"] + entry = hass.config_entries.async_entries("mobile_app")[1] + + # Send a dispatcher signal when config entry is loaded + async_dispatcher_send( + hass, + "mobile_app_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test Before Unload", + ATTR_SENSOR_STATE: 42, + ATTR_SENSOR_TYPE: "sensor", + ATTR_SENSOR_UNIQUE_ID: "test_before_unload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # Check sensor was created + assert hass.states.get("sensor.test_before_unload") is not None + + # Unload the config entry + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + # Send another dispatcher signal after unload + async_dispatcher_send( + hass, + "mobile_app_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test After Unload", + ATTR_SENSOR_STATE: 99, + ATTR_SENSOR_TYPE: "sensor", + ATTR_SENSOR_UNIQUE_ID: "test_after_unload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # The sensor should not be created because dispatcher was cleaned up + assert hass.states.get("sensor.test_after_unload") is None + + # Reload the config entry + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # Send dispatcher signal after reload + async_dispatcher_send( + hass, + "mobile_app_sensor_register", + { + CONF_WEBHOOK_ID: webhook_id, + ATTR_SENSOR_NAME: "Test After Reload", + ATTR_SENSOR_STATE: 123, + ATTR_SENSOR_TYPE: "sensor", + ATTR_SENSOR_UNIQUE_ID: "test_after_reload", + ATTR_SENSOR_ICON: "mdi:test", + ATTR_SENSOR_ATTRIBUTES: {}, + }, + ) + await hass.async_block_till_done() + + # This sensor should be created successfully after reload + assert hass.states.get("sensor.test_after_reload") is not None + assert hass.states.get("sensor.test_after_reload").state == "123" diff --git a/tests/components/satel_integra/snapshots/test_alarm_control_panel.ambr b/tests/components/satel_integra/snapshots/test_alarm_control_panel.ambr new file mode 100644 index 0000000000000..33d0214976cd0 --- /dev/null +++ b/tests/components/satel_integra/snapshots/test_alarm_control_panel.ambr @@ -0,0 +1,84 @@ +# serializer version: 1 +# name: test_alarm_control_panel[alarm_control_panel.home-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'alarm_control_panel', + 'entity_category': None, + 'entity_id': 'alarm_control_panel.home', + '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': None, + 'platform': 'satel_integra', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '1234567890_alarm_panel_1', + 'unit_of_measurement': None, + }) +# --- +# name: test_alarm_control_panel[alarm_control_panel.home-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'changed_by': None, + 'code_arm_required': True, + 'code_format': , + 'friendly_name': 'Home', + 'supported_features': , + }), + 'context': , + 'entity_id': 'alarm_control_panel.home', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'disarmed', + }) +# --- +# name: test_alarm_control_panel[device] + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'satel_integra', + '1234567890_alarm_panel_1', + ), + }), + 'labels': set({ + }), + 'manufacturer': None, + 'model': None, + 'model_id': None, + 'name': 'Home', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': None, + 'via_device_id': None, + }) +# --- diff --git a/tests/components/satel_integra/test_alarm_control_panel.py b/tests/components/satel_integra/test_alarm_control_panel.py new file mode 100644 index 0000000000000..36c3ff5578778 --- /dev/null +++ b/tests/components/satel_integra/test_alarm_control_panel.py @@ -0,0 +1,166 @@ +"""Test Satel Integra alarm panel.""" + +from collections.abc import AsyncGenerator +from unittest.mock import AsyncMock, patch + +import pytest +from satel_integra.satel_integra import AlarmState +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.alarm_control_panel import ( + DOMAIN as ALARM_DOMAIN, + AlarmControlPanelState, +) +from homeassistant.components.satel_integra.const import DOMAIN +from homeassistant.const import ( + ATTR_CODE, + ATTR_ENTITY_ID, + SERVICE_ALARM_ARM_AWAY, + SERVICE_ALARM_ARM_HOME, + SERVICE_ALARM_DISARM, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceRegistry +from homeassistant.helpers.entity_registry import EntityRegistry + +from . import MOCK_CODE, MOCK_ENTRY_ID, setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.fixture(autouse=True) +async def alarm_control_panel_only() -> AsyncGenerator[None]: + """Enable only the alarm panel platform.""" + with patch( + "homeassistant.components.satel_integra.PLATFORMS", + [Platform.ALARM_CONTROL_PANEL], + ): + yield + + +@pytest.mark.usefixtures("mock_satel") +async def test_alarm_control_panel( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_config_entry_with_subentries: MockConfigEntry, + entity_registry: EntityRegistry, + device_registry: DeviceRegistry, +) -> None: + """Test alarm control panel correctly being set up.""" + await setup_integration(hass, mock_config_entry_with_subentries) + + await snapshot_platform(hass, entity_registry, snapshot, MOCK_ENTRY_ID) + + device_entry = device_registry.async_get_device( + identifiers={(DOMAIN, "1234567890_alarm_panel_1")} + ) + + assert device_entry == snapshot(name="device") + + +async def test_alarm_control_panel_initial_state_on( + hass: HomeAssistant, + mock_satel: AsyncMock, + mock_config_entry_with_subentries: MockConfigEntry, +) -> None: + """Test alarm control panel has a correct initial state after initialization.""" + mock_satel.partition_states = {AlarmState.ARMED_MODE0: [1]} + + await setup_integration(hass, mock_config_entry_with_subentries) + + assert ( + hass.states.get("alarm_control_panel.home").state + == AlarmControlPanelState.ARMED_AWAY + ) + + +@pytest.mark.parametrize( + ("source_state", "resulting_state"), + [ + (AlarmState.TRIGGERED, AlarmControlPanelState.TRIGGERED), + (AlarmState.TRIGGERED_FIRE, AlarmControlPanelState.TRIGGERED), + (AlarmState.ENTRY_TIME, AlarmControlPanelState.PENDING), + (AlarmState.ARMED_MODE3, AlarmControlPanelState.ARMED_HOME), + (AlarmState.ARMED_MODE2, AlarmControlPanelState.ARMED_HOME), + (AlarmState.ARMED_MODE1, AlarmControlPanelState.ARMED_HOME), + (AlarmState.ARMED_MODE0, AlarmControlPanelState.ARMED_AWAY), + (AlarmState.EXIT_COUNTDOWN_OVER_10, AlarmControlPanelState.ARMING), + (AlarmState.EXIT_COUNTDOWN_UNDER_10, AlarmControlPanelState.ARMING), + ], +) +async def test_alarm_status_callback( + hass: HomeAssistant, + mock_satel: AsyncMock, + mock_config_entry_with_subentries: MockConfigEntry, + source_state: AlarmState, + resulting_state: AlarmControlPanelState, +) -> None: + """Test alarm control panel correctly changes state after a callback from the panel.""" + await setup_integration(hass, mock_config_entry_with_subentries) + + assert ( + hass.states.get("alarm_control_panel.home").state + == AlarmControlPanelState.DISARMED + ) + + monitor_status_call = mock_satel.monitor_status.call_args_list[0][0] + alarm_panel_update_method = monitor_status_call[0] + + mock_satel.partition_states = {source_state: [1]} + + alarm_panel_update_method() + assert hass.states.get("alarm_control_panel.home").state == resulting_state + + +async def test_alarm_control_panel_arming( + hass: HomeAssistant, + mock_satel: AsyncMock, + mock_config_entry_with_subentries: MockConfigEntry, +) -> None: + """Test alarm control panel correctly sending arming commands to the panel.""" + await setup_integration(hass, mock_config_entry_with_subentries) + + # Test Arm home + await hass.services.async_call( + ALARM_DOMAIN, + SERVICE_ALARM_ARM_HOME, + {ATTR_ENTITY_ID: "alarm_control_panel.home", ATTR_CODE: MOCK_CODE}, + blocking=True, + ) + + mock_satel.arm.assert_awaited_once_with(MOCK_CODE, [1], 1) + + mock_satel.arm.reset_mock() + + # Test Arm away + await hass.services.async_call( + ALARM_DOMAIN, + SERVICE_ALARM_ARM_AWAY, + {ATTR_ENTITY_ID: "alarm_control_panel.home", ATTR_CODE: MOCK_CODE}, + blocking=True, + ) + + mock_satel.arm.assert_awaited_once_with(MOCK_CODE, [1]) + + +async def test_alarm_control_panel_disarming( + hass: HomeAssistant, + mock_satel: AsyncMock, + mock_config_entry_with_subentries: MockConfigEntry, +) -> None: + """Test alarm panel correctly disarms and dismisses alarm.""" + mock_satel.partition_states = {AlarmState.TRIGGERED: [1]} + + await setup_integration(hass, mock_config_entry_with_subentries) + + await hass.services.async_call( + ALARM_DOMAIN, + SERVICE_ALARM_DISARM, + {ATTR_ENTITY_ID: "alarm_control_panel.home", ATTR_CODE: MOCK_CODE}, + blocking=True, + ) + + mock_satel.disarm.assert_awaited_once_with(MOCK_CODE, [1]) + + mock_satel.clear_alarm.assert_awaited_once_with(MOCK_CODE, [1]) diff --git a/tests/components/vesync/snapshots/test_fan.ambr b/tests/components/vesync/snapshots/test_fan.ambr index 3098d7e2a0500..fcf4c92e91954 100644 --- a/tests/components/vesync/snapshots/test_fan.ambr +++ b/tests/components/vesync/snapshots/test_fan.ambr @@ -78,7 +78,6 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'active_time': 0, - 'child_lock': False, 'display_status': 'on', 'friendly_name': 'Air Purifier 131s', 'mode': 'sleep', diff --git a/tests/components/vesync/snapshots/test_switch.ambr b/tests/components/vesync/snapshots/test_switch.ambr index 607347f4c1b13..3a577d1486de6 100644 --- a/tests/components/vesync/snapshots/test_switch.ambr +++ b/tests/components/vesync/snapshots/test_switch.ambr @@ -67,54 +67,8 @@ 'unique_id': 'air-purifier-display', 'unit_of_measurement': None, }), - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': None, - 'entity_id': 'switch.air_purifier_131s_child_lock', - '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': 'Child lock', - 'platform': 'vesync', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'child_lock', - 'unique_id': 'air-purifier-child_lock', - 'unit_of_measurement': None, - }), ]) # --- -# name: test_switch_state[Air Purifier 131s][switch.air_purifier_131s_child_lock] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Air Purifier 131s Child lock', - }), - 'context': , - 'entity_id': 'switch.air_purifier_131s_child_lock', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'off', - }) -# --- # name: test_switch_state[Air Purifier 131s][switch.air_purifier_131s_display] StateSnapshot({ 'attributes': ReadOnlyDict({ diff --git a/tests/components/vicare/fixtures/VitoPure.json b/tests/components/vicare/fixtures/VitoPure.json index 1e1cdef97ec53..2b935d5923c8e 100644 --- a/tests/components/vicare/fixtures/VitoPure.json +++ b/tests/components/vicare/fixtures/VitoPure.json @@ -4,72 +4,120 @@ "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation", + "feature": "device.messages.info.raw", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": true + "entries": { + "type": "array", + "value": [] } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.messages.info.raw" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.levels.levelFour", + "feature": "device.messages.service.raw", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelFour" + "properties": { + "entries": { + "type": "array", + "value": [] + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.messages.service.raw" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.levels.levelOne", + "feature": "device.messages.status.raw", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelOne" + "properties": { + "entries": { + "type": "array", + "value": [] + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.messages.status.raw" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.levels.levelThree", + "feature": "device.productIdentification", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelThree" + "properties": { + "product": { + "type": "object", + "value": { + "busAddress": 0, + "busType": "OwnBus", + "productFamily": "B_00059_VP300", + "viessmannIdentificationNumber": "################" + } + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.productIdentification" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.levels.levelTwo", + "feature": "device.setDefaultValues", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, "properties": {}, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelTwo" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.setDefaultValues" }, { "apiVersion": 1, - "commands": {}, + "commands": { + "activate": { + "isExecutable": true, + "name": "activate", + "params": { + "begin": { + "constraints": { + "regEx": "^[\\d]{2}-[\\d]{2}$" + }, + "required": true, + "type": "string" + }, + "end": { + "constraints": { + "regEx": "^[\\d]{2}-[\\d]{2}$" + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving/commands/deactivate" + } + }, "deviceId": "0", - "feature": "ventilation.operating.modes.filterChange", + "feature": "device.time.daylightSaving", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -79,46 +127,114 @@ "value": false } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.filterChange" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "device.variant", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "Vitopure350" + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.variant" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.boiler.serial", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "################" + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.boiler.serial" }, { "apiVersion": 1, "commands": { - "setLevel": { + "setTime": { "isExecutable": true, - "name": "setLevel", + "name": "setTime", "params": { - "level": { + "value": { "constraints": { - "enum": ["levelOne", "levelTwo", "levelThree", "levelFour"] + "regEx": "^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])$" }, "required": true, "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.permanent/commands/setLevel" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.time/commands/setTime" } }, "deviceId": "0", - "feature": "ventilation.operating.modes.permanent", + "feature": "heating.device.time", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T07:52:28.632Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.time" + }, + { + "apiVersion": 1, + "commands": {}, + "deprecated": { + "info": "replaced by device.variant", + "removalDate": "2025-03-15" + }, + "deviceId": "0", + "feature": "heating.device.variant", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false + "value": { + "type": "string", + "value": "Vitopure350" } }, - "timestamp": "2024-12-17T13:24:03.411Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.permanent" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.variant" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.operating.modes.sensorDriven", + "feature": "tcu.wifi", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "strength": { + "type": "number", + "unit": "", + "value": -41 + } + }, + "timestamp": "2025-11-05T07:55:06.737Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/tcu.wifi" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -128,14 +244,14 @@ "value": true } }, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.sensorDriven" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.operating.modes.ventilation", + "feature": "ventilation.control.filterChange", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -145,86 +261,207 @@ "value": false } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.ventilation" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.control.filterChange" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "ventilation.operating.programs.active", + "feature": "ventilation.fan.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "current": { + "type": "number", + "unit": "rotationPerMinute", + "value": 555 + }, + "status": { + "type": "string", + "value": "connected" + }, + "target": { + "type": "number", + "unit": "rotationPerMinute", + "value": 585 + } + }, + "timestamp": "2025-11-05T07:54:56.887Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.fan.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.fan.supply.runtime", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { "value": { + "type": "number", + "unit": "hour", + "value": 819 + } + }, + "timestamp": "2025-11-05T07:20:49.877Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.fan.supply.runtime" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.features.co", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.features.co" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.features.co2", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.features.co2" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.features.dust", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.features.dust" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.features.finedust", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.features.finedust" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.features.organicComponent", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.features.organicComponent" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.filter.pollution.blocked", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { "type": "string", - "value": "levelTwo" + "value": "connected" + }, + "value": { + "type": "number", + "unit": "percent", + "value": 5 } }, - "timestamp": "2024-12-17T13:24:03.411Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.programs.active" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.filter.pollution.blocked" }, { "apiVersion": 1, - "commands": { - "activate": { - "isExecutable": true, - "name": "activate", - "params": { - "timeout": { - "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 - }, - "required": false, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/activate" + "commands": {}, + "deviceId": "0", + "feature": "ventilation.filter.runtime", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "operatingHours": { + "type": "number", + "unit": "hours", + "value": 5795 }, - "deactivate": { - "isExecutable": true, - "name": "deactivate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/deactivate" + "overdueHours": { + "type": "number", + "unit": "hours", + "value": 0 }, - "setDefaultRuntime": { + "remainingHours": { + "type": "number", + "unit": "hours", + "value": 2965 + } + }, + "timestamp": "2025-11-05T07:37:23.452Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.filter.runtime" + }, + { + "apiVersion": 1, + "commands": { + "setMode": { "isExecutable": true, - "name": "setDefaultRuntime", + "name": "setMode", "params": { - "defaultRuntime": { + "mode": { "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 + "enum": ["permanent", "ventilation", "sensorDriven"] }, "required": true, - "type": "number" + "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/setDefaultRuntime" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setMode" }, - "setTimeout": { - "isExecutable": true, - "name": "setTimeout", - "params": { - "timeout": { - "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/setTimeout" + "setModeContinuousSensorOverride": { + "isExecutable": false, + "name": "setModeContinuousSensorOverride", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setModeContinuousSensorOverride" } }, "deviceId": "0", - "feature": "ventilation.quickmodes.forcedLevelFour", + "feature": "ventilation.operating.modes.active", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "sensorDriven" + } + }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.operating.modes.filterChange", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -232,80 +469,31 @@ "active": { "type": "boolean", "value": false - }, - "defaultRuntime": { - "type": "number", - "unit": "minutes", - "value": 30 - }, - "isActiveWritable": { - "type": "boolean", - "value": true } }, - "timestamp": "2024-12-17T13:24:04.515Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.filterChange" }, { "apiVersion": 1, "commands": { - "activate": { - "isExecutable": true, - "name": "activate", - "params": { - "timeout": { - "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 - }, - "required": false, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/activate" - }, - "deactivate": { - "isExecutable": true, - "name": "deactivate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/deactivate" - }, - "setDefaultRuntime": { - "isExecutable": true, - "name": "setDefaultRuntime", - "params": { - "defaultRuntime": { - "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/setDefaultRuntime" - }, - "setTimeout": { + "setLevel": { "isExecutable": true, - "name": "setTimeout", + "name": "setLevel", "params": { - "timeout": { + "level": { "constraints": { - "max": 1440, - "min": 1, - "stepping": 1 + "enum": ["levelOne", "levelTwo", "levelThree", "levelFour"] }, "required": true, - "type": "number" + "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/setTimeout" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.permanent/commands/setLevel" } }, "deviceId": "0", - "feature": "ventilation.quickmodes.silent", + "feature": "ventilation.operating.modes.permanent", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -313,88 +501,86 @@ "active": { "type": "boolean", "value": false - }, - "defaultRuntime": { - "type": "number", - "unit": "minutes", - "value": 30 - }, - "isActiveWritable": { - "type": "boolean", - "value": true } }, - "timestamp": "2024-12-17T13:24:04.515Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent" + "timestamp": "2025-11-04T22:45:12.663Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.permanent" }, { "apiVersion": 1, - "commands": { - "activate": { - "isExecutable": true, - "name": "activate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby/commands/activate" - }, - "deactivate": { - "isExecutable": true, - "name": "deactivate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby/commands/deactivate" + "commands": {}, + "deviceId": "0", + "feature": "ventilation.operating.modes.sensorDriven", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true } }, + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.sensorDriven" + }, + { + "apiVersion": 1, + "commands": {}, "deviceId": "0", - "feature": "ventilation.quickmodes.standby", + "feature": "ventilation.operating.modes.ventilation", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { "active": { "type": "boolean", - "value": true + "value": false } }, - "timestamp": "2024-12-17T13:24:04.515Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.ventilation" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "device.productIdentification", + "feature": "ventilation.operating.programs.active", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "product": { - "type": "object", - "value": { - "busAddress": 0, - "busType": "OwnBus", - "productFamily": "B_00059_VP300", - "viessmannIdentificationNumber": "################" - } + "value": { + "type": "string", + "value": "levelTwo" } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.productIdentification" + "timestamp": "2025-11-04T22:45:12.663Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.programs.active" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "device.messages.errors.raw", + "feature": "ventilation.operating.state", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "entries": { - "type": "array", - "value": [] + "demand": { + "type": "string", + "value": "unknown" + }, + "level": { + "type": "string", + "value": "unknown" + }, + "reason": { + "type": "string", + "value": "sensorDriven" } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.messages.errors.raw" + "timestamp": "2025-11-04T22:45:13.754Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.state" }, { "apiVersion": 1, @@ -403,149 +589,211 @@ "isExecutable": true, "name": "activate", "params": { - "begin": { - "constraints": { - "regEx": "^[\\d]{2}-[\\d]{2}$" - }, - "required": true, - "type": "string" - }, - "end": { + "timeout": { "constraints": { - "regEx": "^[\\d]{2}-[\\d]{2}$" + "max": 1440, + "min": 1, + "stepping": 1 }, - "required": true, - "type": "string" + "required": false, + "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving/commands/activate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/activate" }, "deactivate": { "isExecutable": true, "name": "deactivate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving/commands/deactivate" - } - }, - "deviceId": "0", - "feature": "device.time.daylightSaving", - "gatewayId": "################", - "isEnabled": true, - "isReady": true, - "properties": { - "active": { - "type": "boolean", - "value": false - } - }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.time.daylightSaving" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.boiler.serial", - "gatewayId": "################", - "isEnabled": true, - "isReady": true, - "properties": { - "value": { - "type": "string", - "value": "################" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/deactivate" + }, + "setDefaultRuntime": { + "isExecutable": true, + "name": "setDefaultRuntime", + "params": { + "defaultRuntime": { + "constraints": { + "max": 1440, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/setDefaultRuntime" + }, + "setTimeout": { + "isExecutable": true, + "name": "setTimeout", + "params": { + "timeout": { + "constraints": { + "max": 1440, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour/commands/setTimeout" } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.boiler.serial" - }, - { - "apiVersion": 1, - "commands": {}, "deviceId": "0", - "feature": "heating.device.variant", + "feature": "ventilation.quickmodes.forcedLevelFour", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "value": { - "type": "string", - "value": "Vitopure350" + "active": { + "type": "boolean", + "value": false + }, + "defaultRuntime": { + "type": "number", + "unit": "minutes", + "value": 30 + }, + "isCommandExecutable": { + "type": "boolean", + "value": true } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.device.variant" + "timestamp": "2025-11-04T22:45:12.663Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.forcedLevelFour" }, { "apiVersion": 1, "commands": { - "setMode": { + "activate": { "isExecutable": true, - "name": "setMode", + "name": "activate", "params": { - "mode": { + "timeout": { "constraints": { - "enum": ["permanent", "ventilation", "sensorDriven"] + "max": 1440, + "min": 1, + "stepping": 1 }, - "required": true, - "type": "string" + "required": false, + "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setMode" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/activate" }, - "setModeContinuousSensorOverride": { - "isExecutable": false, - "name": "setModeContinuousSensorOverride", + "deactivate": { + "isExecutable": true, + "name": "deactivate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setModeContinuousSensorOverride" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/deactivate" + }, + "setDefaultRuntime": { + "isExecutable": true, + "name": "setDefaultRuntime", + "params": { + "defaultRuntime": { + "constraints": { + "max": 1440, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/setDefaultRuntime" + }, + "setTimeout": { + "isExecutable": true, + "name": "setTimeout", + "params": { + "timeout": { + "constraints": { + "max": 1440, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent/commands/setTimeout" } }, "deviceId": "0", - "feature": "ventilation.operating.modes.active", + "feature": "ventilation.quickmodes.silent", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "value": { - "type": "string", - "value": "sensorDriven" + "active": { + "type": "boolean", + "value": false + }, + "defaultRuntime": { + "type": "number", + "unit": "minutes", + "value": 30 + }, + "isCommandExecutable": { + "type": "boolean", + "value": true } }, - "timestamp": "2024-12-17T08:16:15.525Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active" + "timestamp": "2025-11-04T22:45:12.663Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.silent" }, { "apiVersion": 1, - "commands": {}, + "commands": { + "activate": { + "isExecutable": true, + "name": "activate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby/commands/deactivate" + }, + "setActive": { + "isExecutable": true, + "name": "setActive", + "params": { + "active": { + "constraints": {}, + "required": true, + "type": "boolean" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby/commands/setActive" + } + }, "deviceId": "0", - "feature": "ventilation.operating.state", + "feature": "ventilation.quickmodes.standby", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "demand": { - "type": "string", - "value": "unknown" - }, - "level": { - "type": "string", - "value": "unknown" - }, - "reason": { - "type": "string", - "value": "standby" + "active": { + "type": "boolean", + "value": false } }, - "timestamp": "2024-12-17T13:24:04.515Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.state" + "timestamp": "2025-11-04T21:27:15.790Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.standby" }, { "apiVersion": 1, "commands": { "resetSchedule": { - "isExecutable": false, + "isExecutable": true, "name": "resetSchedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/resetSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/resetSchedule" }, "setSchedule": { "isExecutable": true, @@ -563,7 +811,7 @@ "type": "Schedule" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/setSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/setSchedule" } }, "deviceId": "0", @@ -638,8 +886,188 @@ } } }, - "timestamp": "2024-12-17T07:50:28.062Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule" + "timestamp": "2025-11-03T11:07:19.646Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.airBorneDust.pm1", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "microgrammsPerCubicMeter", + "value": 0.1 + } + }, + "timestamp": "2025-11-05T07:37:37.153Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.airBorneDust.pm1" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.airBorneDust.pm10", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "microgrammsPerCubicMeter", + "value": 0.5 + } + }, + "timestamp": "2025-11-05T07:51:36.966Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.airBorneDust.pm10" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.airBorneDust.pm2d5", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "microgrammsPerCubicMeter", + "value": 0.3 + } + }, + "timestamp": "2025-11-05T07:46:52.511Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.airBorneDust.pm2d5" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.airBorneDust.pm4", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "microgrammsPerCubicMeter", + "value": 0.4 + } + }, + "timestamp": "2025-11-05T07:47:07.330Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.airBorneDust.pm4" + }, + { + "apiVersion": 1, + "commands": {}, + "deprecated": { + "info": "replaced by ventilation.airQuality.abstract", + "removalDate": "2024-09-15" + }, + "deviceId": "0", + "feature": "ventilation.sensors.airQuality", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "", + "value": 49 + } + }, + "timestamp": "2025-11-05T07:34:04.585Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.airQuality" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.humidity.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "percent", + "value": 59 + } + }, + "timestamp": "2025-11-05T02:32:43.812Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.humidity.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.temperature.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 20.8 + } + }, + "timestamp": "2025-11-05T07:32:48.488Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.temperature.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.sensors.volatileOrganicCompounds", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "", + "value": 148 + } + }, + "timestamp": "2025-11-05T07:44:08.861Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.sensors.volatileOrganicCompounds" } ] } diff --git a/tests/components/vicare/fixtures/Vitocal222G_Vitovent300W.json b/tests/components/vicare/fixtures/Vitocal222G_Vitovent300W.json index a733d33a12aba..48d16093cf2cc 100644 --- a/tests/components/vicare/fixtures/Vitocal222G_Vitovent300W.json +++ b/tests/components/vicare/fixtures/Vitocal222G_Vitovent300W.json @@ -4,18 +4,77 @@ "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "device.messages.errors.raw", + "feature": "device.messages.errors.counter.d6", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 0 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.messages.errors.counter.d6" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "device.messages.logbook", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { "entries": { "type": "array", - "value": [] + "value": [ + { + "actor": "SEK_PUMPE1", + "additionalInfo": 120, + "event": "Inverter_CPU_error", + "stateMachine": "TPM_SC2", + "status": 0, + "timestamp": "2025-11-05T07:53:50.000Z" + }, + { + "actor": "SEK_PUMPE1", + "additionalInfo": 117, + "event": "Evap_SuctGas_T emp", + "stateMachine": "TPM_SC2", + "status": 100, + "timestamp": "2025-11-05T07:51:54.000Z" + }, + { + "actor": "VERDICHTER1", + "additionalInfo": 600, + "event": "Inverter_Under_voltage", + "stateMachine": "WAERMEPUMPE1", + "status": 0, + "timestamp": "2025-11-05T07:51:51.000Z" + }, + { + "actor": "E_HEIZ_ST1", + "additionalInfo": 0, + "circuit": "Heizkreis", + "event": "Liquid_Line_Temp", + "status": 0, + "timestamp": "2025-11-05T07:51:41.000Z" + }, + { + "actor": "E_HEIZ_ST1", + "additionalInfo": 100, + "circuit": "Heizkreis", + "event": "Liquid_Line_Temp", + "status": 3, + "timestamp": "2025-11-05T07:41:16.000Z" + } + ] } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.messages.errors.raw" + "timestamp": "2025-11-05T06:55:22.268Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.messages.logbook" }, { "apiVersion": 1, @@ -31,8 +90,8 @@ "value": "################" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/device.serial" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/device.serial" }, { "apiVersion": 1, @@ -48,8 +107,8 @@ "value": "notConnected" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.boiler.sensors.temperature.commonSupply" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.boiler.sensors.temperature.commonSupply" }, { "apiVersion": 1, @@ -65,8 +124,8 @@ "value": "################" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.boiler.serial" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.boiler.serial" }, { "apiVersion": 1, @@ -83,11 +142,16 @@ "properties": { "status": { "type": "string", - "value": "notConnected" + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 41.2 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.buffer.sensors.temperature.main" + "timestamp": "2025-11-05T07:15:16.532Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.buffer.sensors.temperature.main" }, { "apiVersion": 1, @@ -104,11 +168,16 @@ "properties": { "status": { "type": "string", - "value": "notConnected" + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 41.2 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.buffer.sensors.temperature.top" + "timestamp": "2025-11-05T07:15:16.532Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.buffer.sensors.temperature.top" }, { "apiVersion": 1, @@ -121,11 +190,16 @@ "properties": { "status": { "type": "string", - "value": "notConnected" + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 41.2 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.bufferCylinder.sensors.temperature.main" + "timestamp": "2025-11-05T07:15:16.532Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.bufferCylinder.sensors.temperature.main" }, { "apiVersion": 1, @@ -138,11 +212,16 @@ "properties": { "status": { "type": "string", - "value": "notConnected" + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 41.2 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.bufferCylinder.sensors.temperature.top" + "timestamp": "2025-11-05T07:15:16.532Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.bufferCylinder.sensors.temperature.top" }, { "apiVersion": 1, @@ -155,52 +234,69 @@ "properties": { "enabled": { "type": "array", - "value": ["0"] + "value": ["0", "1"] } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits" }, { "apiVersion": 1, - "commands": {}, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 20, + "minLength": 1 + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0/commands/setName" + } + }, "deviceId": "0", - "feature": "heating.circuits.0.circulation.pump", + "feature": "heating.circuits.0", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "status": { + "active": { + "type": "boolean", + "value": true + }, + "name": { "type": "string", - "value": "on" + "value": "Heizkreis Lüftung" + }, + "type": { + "type": "string", + "value": "heatingCircuit" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.circulation.pump" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.1.circulation.pump", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.circulation.pump" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.circulation.pump", + "feature": "heating.circuits.0.circulation.pump", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.circulation.pump" + "properties": { + "status": { + "type": "string", + "value": "off" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.circulation.pump" }, { "apiVersion": 1, @@ -213,35 +309,11 @@ "properties": { "status": { "type": "string", - "value": "on" + "value": "off" } }, - "timestamp": "2025-02-11T20:58:18.395Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.frostprotection" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.1.frostprotection", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.frostprotection" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.2.frostprotection", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.frostprotection" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.frostprotection" }, { "apiVersion": 1, @@ -269,7 +341,7 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.curve/commands/setCurve" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.curve/commands/setCurve" } }, "deviceId": "0", @@ -286,44 +358,20 @@ "slope": { "type": "number", "unit": "", - "value": 0.4 + "value": 0.2 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.curve" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.1.heating.curve", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.curve" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.2.heating.curve", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.heating.curve" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.curve" }, { "apiVersion": 1, "commands": { "resetSchedule": { - "isExecutable": false, + "isExecutable": true, "name": "resetSchedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule/commands/resetSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule/commands/resetSchedule" }, "setSchedule": { "isExecutable": true, @@ -341,7 +389,7 @@ "type": "Schedule" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule/commands/setSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule/commands/setSchedule" } }, "deviceId": "0", @@ -357,91 +405,52 @@ "entries": { "type": "Schedule", "value": { - "fri": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "mon": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "sat": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "sun": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "thu": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "tue": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ], - "wed": [ - { - "end": "24:00", - "mode": "normal", - "position": 0, - "start": "00:00" - } - ] + "fri": [], + "mon": [], + "sat": [], + "sun": [], + "thu": [], + "tue": [], + "wed": [] } } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule" - }, - { - "apiVersion": 1, - "commands": {}, - "deviceId": "0", - "feature": "heating.circuits.1.heating.schedule", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.schedule" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.heating.schedule" }, { "apiVersion": 1, - "commands": {}, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 20, + "minLength": 1 + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.name/commands/setName" + } + }, + "components": [], "deviceId": "0", - "feature": "heating.circuits.2.heating.schedule", + "feature": "heating.circuits.0.name", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.heating.schedule" + "properties": { + "name": { + "type": "string", + "value": "Heizkreis Lüftung" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.name" }, { "apiVersion": 1, @@ -458,7 +467,7 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.active/commands/setMode" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.active/commands/setMode" } }, "deviceId": "0", @@ -472,74 +481,89 @@ "value": "dhwAndHeating" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.active" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.active" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.active", + "feature": "heating.circuits.0.operating.modes.cooling", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.active" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.cooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.active", + "feature": "heating.circuits.0.operating.modes.dhw", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.active" + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhw" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.cooling", + "feature": "heating.circuits.0.operating.modes.dhwAndHeating", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.cooling" - }, - { - "apiVersion": 1, - "commands": {}, + "properties": { + "active": { + "type": "boolean", + "value": true + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhwAndHeating" + }, + { + "apiVersion": 1, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.cooling", + "feature": "heating.circuits.0.operating.modes.dhwAndHeatingCooling", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.cooling" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhwAndHeatingCooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.cooling", + "feature": "heating.circuits.0.operating.modes.forcedNormal", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.cooling" + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.forcedNormal" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.dhw", + "feature": "heating.circuits.0.operating.modes.forcedReduced", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -549,138 +573,1431 @@ "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhw" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.forcedReduced" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.dhw", + "feature": "heating.circuits.0.operating.modes.heating", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhw" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.heating" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.dhw", + "feature": "heating.circuits.0.operating.modes.heatingCooling", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhw" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.heatingCooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.dhwAndHeating", + "feature": "heating.circuits.0.operating.modes.normalStandby", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { "active": { "type": "boolean", - "value": true + "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhwAndHeating" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.normalStandby" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.dhwAndHeating", + "feature": "heating.circuits.0.operating.modes.standby", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhwAndHeating" + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.standby" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.dhwAndHeating", + "feature": "heating.circuits.0.operating.programs.active", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhwAndHeating" + "properties": { + "value": { + "type": "string", + "value": "standby" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.active" }, { "apiVersion": 1, - "commands": {}, + "commands": { + "activate": { + "isExecutable": true, + "name": "activate", + "params": { + "temperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": false, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/deactivate" + }, + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/setTemperature" + } + }, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.dhwAndHeatingCooling", + "feature": "heating.circuits.0.operating.programs.comfort", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.dhwAndHeatingCooling" + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 21 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort" + }, + { + "apiVersion": 1, + "commands": { + "activate": { + "isExecutable": false, + "name": "activate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco/commands/deactivate" + } + }, + "deviceId": "0", + "feature": "heating.circuits.0.operating.programs.eco", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 21 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.dhwAndHeatingCooling", + "feature": "heating.circuits.0.operating.programs.fixed", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhwAndHeatingCooling" + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.fixed" + }, + { + "apiVersion": 1, + "commands": { + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.normal/commands/setTemperature" + } + }, + "deviceId": "0", + "feature": "heating.circuits.0.operating.programs.normal", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 21 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.normal" + }, + { + "apiVersion": 1, + "commands": { + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.reduced/commands/setTemperature" + } + }, + "deviceId": "0", + "feature": "heating.circuits.0.operating.programs.reduced", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 18 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.reduced" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.dhwAndHeatingCooling", + "feature": "heating.circuits.0.operating.programs.screedDrying.heatpump", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhwAndHeatingCooling" + "properties": { + "useApproved": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.screedDrying.heatpump" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.forcedNormal", + "feature": "heating.circuits.0.operating.programs.standby", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { "active": { "type": "boolean", - "value": false + "value": true } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.forcedNormal" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.standby" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.0.sensors.temperature.room", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 21.4 + } + }, + "timestamp": "2025-11-05T05:23:20.999Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.sensors.temperature.room" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.0.sensors.temperature.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "notConnected" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.sensors.temperature.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.0.temperature", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 0 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature" + }, + { + "apiVersion": 1, + "commands": { + "setLevels": { + "isExecutable": true, + "name": "setLevels", + "params": { + "maxTemperature": { + "constraints": { + "max": 70, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + }, + "minTemperature": { + "constraints": { + "max": 30, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setLevels" + }, + "setMax": { + "isExecutable": true, + "name": "setMax", + "params": { + "temperature": { + "constraints": { + "max": 70, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setMax" + }, + "setMin": { + "isExecutable": true, + "name": "setMin", + "params": { + "temperature": { + "constraints": { + "max": 30, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setMin" + } + }, + "deviceId": "0", + "feature": "heating.circuits.0.temperature.levels", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "max": { + "type": "number", + "unit": "celsius", + "value": 40 + }, + "min": { + "type": "number", + "unit": "celsius", + "value": 10 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels" + }, + { + "apiVersion": 1, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 20, + "minLength": 1 + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1/commands/setName" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true + }, + "name": { + "type": "string", + "value": "Heizkreis Heizung" + }, + "type": { + "type": "string", + "value": "heatingCircuit" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.circulation.pump", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "off" + } + }, + "timestamp": "2025-11-05T07:37:30.635Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.circulation.pump" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.frostprotection", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "off" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.frostprotection" + }, + { + "apiVersion": 1, + "commands": { + "setCurve": { + "isExecutable": true, + "name": "setCurve", + "params": { + "shift": { + "constraints": { + "max": 40, + "min": -15, + "stepping": 1 + }, + "required": true, + "type": "number" + }, + "slope": { + "constraints": { + "max": 3.5, + "min": 0, + "stepping": 0.1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.curve/commands/setCurve" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.heating.curve", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "shift": { + "type": "number", + "unit": "", + "value": 0 + }, + "slope": { + "type": "number", + "unit": "", + "value": 0.2 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.curve" + }, + { + "apiVersion": 1, + "commands": { + "resetSchedule": { + "isExecutable": true, + "name": "resetSchedule", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.schedule/commands/resetSchedule" + }, + "setSchedule": { + "isExecutable": true, + "name": "setSchedule", + "params": { + "newSchedule": { + "constraints": { + "defaultMode": "standby", + "maxEntries": 8, + "modes": ["reduced", "normal", "fixed"], + "overlapAllowed": true, + "resolution": 10 + }, + "required": true, + "type": "Schedule" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.schedule/commands/setSchedule" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.heating.schedule", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true + }, + "entries": { + "type": "Schedule", + "value": { + "fri": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "mon": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "sat": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "sun": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "thu": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "tue": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ], + "wed": [ + { + "end": "24:00", + "mode": "normal", + "position": 0, + "start": "00:00" + } + ] + } + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.heating.schedule" + }, + { + "apiVersion": 1, + "commands": { + "setName": { + "isExecutable": true, + "name": "setName", + "params": { + "name": { + "constraints": { + "maxLength": 20, + "minLength": 1 + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.name/commands/setName" + } + }, + "components": [], + "deviceId": "0", + "feature": "heating.circuits.1.name", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "name": { + "type": "string", + "value": "Heizkreis Heizung" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.name" + }, + { + "apiVersion": 1, + "commands": { + "setMode": { + "isExecutable": true, + "name": "setMode", + "params": { + "mode": { + "constraints": { + "enum": ["dhw", "dhwAndHeating", "standby"] + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.active/commands/setMode" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.active", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "dhwAndHeating" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.active" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.cooling", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.cooling" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.dhw", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhw" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.dhwAndHeating", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhwAndHeating" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.dhwAndHeatingCooling", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.dhwAndHeatingCooling" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.forcedNormal", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.forcedNormal" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.forcedReduced", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.forcedReduced" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.heating", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.heating" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.heatingCooling", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.heatingCooling" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.normalStandby", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.normalStandby" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.modes.standby", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.standby" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.active", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "standby" + } + }, + "timestamp": "2025-11-05T07:37:30.635Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.active" + }, + { + "apiVersion": 1, + "commands": { + "activate": { + "isExecutable": true, + "name": "activate", + "params": { + "temperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": false, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.comfort/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.comfort/commands/deactivate" + }, + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.comfort/commands/setTemperature" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.comfort", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 21 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.comfort" + }, + { + "apiVersion": 1, + "commands": { + "activate": { + "isExecutable": false, + "name": "activate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.eco/commands/activate" + }, + "deactivate": { + "isExecutable": true, + "name": "deactivate", + "params": {}, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.eco/commands/deactivate" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.eco", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 22 + } + }, + "timestamp": "2025-11-05T07:37:30.635Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.eco" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.fixed", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.fixed" + }, + { + "apiVersion": 1, + "commands": { + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.normal/commands/setTemperature" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.normal", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 22 + } + }, + "timestamp": "2025-11-05T07:37:30.635Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.normal" + }, + { + "apiVersion": 1, + "commands": { + "setTemperature": { + "isExecutable": true, + "name": "setTemperature", + "params": { + "targetTemperature": { + "constraints": { + "max": 30, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.reduced/commands/setTemperature" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.reduced", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + }, + "demand": { + "type": "string", + "value": "unknown" + }, + "temperature": { + "type": "number", + "unit": "celsius", + "value": 22 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.reduced" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.screedDrying.heatpump", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "useApproved": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.screedDrying.heatpump" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.operating.programs.standby", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": true + } + }, + "timestamp": "2025-11-05T07:37:30.635Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.standby" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.sensors.temperature.room", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 21.4 + } + }, + "timestamp": "2025-11-05T05:23:20.999Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.sensors.temperature.room" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.sensors.temperature.supply", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 34.3 + } + }, + "timestamp": "2025-11-05T07:36:47.741Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.sensors.temperature.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.1.temperature", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 0 + } + }, + "timestamp": "2025-11-05T07:37:33.709Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature" + }, + { + "apiVersion": 1, + "commands": { + "setLevels": { + "isExecutable": true, + "name": "setLevels", + "params": { + "maxTemperature": { + "constraints": { + "max": 70, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + }, + "minTemperature": { + "constraints": { + "max": 30, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature.levels/commands/setLevels" + }, + "setMax": { + "isExecutable": true, + "name": "setMax", + "params": { + "temperature": { + "constraints": { + "max": 70, + "min": 10, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature.levels/commands/setMax" + }, + "setMin": { + "isExecutable": true, + "name": "setMin", + "params": { + "temperature": { + "constraints": { + "max": 30, + "min": 1, + "stepping": 1 + }, + "required": true, + "type": "number" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature.levels/commands/setMin" + } + }, + "deviceId": "0", + "feature": "heating.circuits.1.temperature.levels", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "max": { + "type": "number", + "unit": "celsius", + "value": 40 + }, + "min": { + "type": "number", + "unit": "celsius", + "value": 12 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature.levels" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.circulation.pump", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.circulation.pump" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.frostprotection", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.frostprotection" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.heating.curve", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.heating.curve" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.heating.schedule", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.heating.schedule" + }, + { + "apiVersion": 1, + "commands": {}, + "components": [], + "deviceId": "0", + "feature": "heating.circuits.2.name", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2024-01-18T15:47:01.598Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.name" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.operating.modes.active", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.active" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.operating.modes.cooling", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.cooling" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.operating.modes.dhw", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhw" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.circuits.2.operating.modes.dhwAndHeating", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhwAndHeating" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.forcedNormal", + "feature": "heating.circuits.2.operating.modes.dhwAndHeatingCooling", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.forcedNormal" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.dhwAndHeatingCooling" }, { "apiVersion": 1, @@ -691,289 +2008,271 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.forcedNormal" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.forcedNormal" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.forcedReduced", + "feature": "heating.circuits.2.operating.modes.forcedReduced", "gatewayId": "################", - "isEnabled": true, + "isEnabled": false, "isReady": true, - "properties": { - "active": { - "type": "boolean", - "value": false - } - }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.forcedReduced" + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.forcedReduced" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.forcedReduced", + "feature": "heating.circuits.2.operating.modes.heating", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.forcedReduced" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.heating" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.forcedReduced", + "feature": "heating.circuits.2.operating.modes.heatingCooling", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.forcedReduced" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.heatingCooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.heating", + "feature": "heating.circuits.2.operating.modes.normalStandby", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.heating" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.normalStandby" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.heating", + "feature": "heating.circuits.2.operating.modes.standby", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.heating" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.standby" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.heating", + "feature": "heating.circuits.2.operating.programs.active", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.heating" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.active" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.heatingCooling", + "feature": "heating.circuits.2.operating.programs.comfort", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.heatingCooling" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.comfort" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.heatingCooling", + "feature": "heating.circuits.2.operating.programs.eco", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.heatingCooling" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.eco" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.heatingCooling", + "feature": "heating.circuits.2.operating.programs.fixed", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.heatingCooling" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.fixed" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.normalStandby", + "feature": "heating.circuits.2.operating.programs.normal", "gatewayId": "################", - "isEnabled": true, + "isEnabled": false, "isReady": true, - "properties": { - "active": { - "type": "boolean", - "value": false - } - }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.normalStandby" + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.normal" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.normalStandby", + "feature": "heating.circuits.2.operating.programs.reduced", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.normalStandby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.reduced" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.normalStandby", + "feature": "heating.circuits.2.operating.programs.screedDrying.heatpump", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.normalStandby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.screedDrying.heatpump" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.modes.standby", + "feature": "heating.circuits.2.operating.programs.standby", "gatewayId": "################", - "isEnabled": true, + "isEnabled": false, "isReady": true, - "properties": { - "active": { - "type": "boolean", - "value": false - } - }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.modes.standby" + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.standby" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.modes.standby", + "feature": "heating.circuits.2.sensors.temperature.room", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.modes.standby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.sensors.temperature.room" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.modes.standby", + "feature": "heating.circuits.2.sensors.temperature.supply", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.modes.standby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.sensors.temperature.supply" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.active", + "feature": "heating.circuits.2.temperature", "gatewayId": "################", - "isEnabled": true, + "isEnabled": false, "isReady": true, - "properties": { - "value": { - "type": "string", - "value": "normal" - } - }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.active" + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.temperature" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.active", + "feature": "heating.circuits.2.temperature.levels", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.active" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.temperature.levels" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.active", + "feature": "heating.compressors", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.active" + "properties": { + "enabled": { + "type": "array", + "value": ["0"] + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors" }, { "apiVersion": 1, "commands": { - "activate": { - "isExecutable": true, - "name": "activate", + "setActive": { + "isExecutable": false, + "name": "setActive", "params": { - "temperature": { - "constraints": { - "max": 30, - "min": 10, - "stepping": 1 - }, - "required": false, - "type": "number" + "active": { + "constraints": {}, + "required": true, + "type": "boolean" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/activate" - }, - "deactivate": { - "isExecutable": true, - "name": "deactivate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/deactivate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0/commands/setActive" }, - "setTemperature": { - "isExecutable": true, - "name": "setTemperature", + "setPhase": { + "isExecutable": false, + "name": "setPhase", "params": { - "targetTemperature": { + "value": { "constraints": { - "max": 30, - "min": 10, - "stepping": 1 + "enum": [ + "preparing", + "heating", + "pause", + "cooling", + "preparing-defrost", + "defrost", + "passive-defrost", + "off" + ] }, "required": true, - "type": "number" + "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort/commands/setTemperature" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0/commands/setPhase" } }, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.comfort", + "feature": "heating.compressors.0", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -982,359 +2281,458 @@ "type": "boolean", "value": false }, - "demand": { + "phase": { "type": "string", - "value": "unknown" + "value": "off" + } + }, + "timestamp": "2025-11-05T07:02:08.142Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.compressors.0.heat.production.cooling.week", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" }, - "temperature": { + "value": { "type": "number", - "unit": "celsius", - "value": 20 + "unit": "kilowattHour", + "value": 0 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.comfort" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.heat.production.cooling.week" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.comfort", + "feature": "heating.compressors.0.heat.production.current", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.comfort" + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "watt", + "value": 0 + } + }, + "timestamp": "2025-11-05T06:52:28.461Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.heat.production.current" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.comfort", + "feature": "heating.compressors.0.heat.production.dhw.week", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.comfort" + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "kilowattHour", + "value": 10.7 + } + }, + "timestamp": "2025-11-05T04:34:50.188Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.heat.production.dhw.week" }, { "apiVersion": 1, - "commands": { - "activate": { - "isExecutable": true, - "name": "activate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco/commands/activate" + "commands": {}, + "deviceId": "0", + "feature": "heating.compressors.0.heat.production.heating.week", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" }, - "deactivate": { - "isExecutable": true, - "name": "deactivate", - "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco/commands/deactivate" + "value": { + "type": "number", + "unit": "kilowattHour", + "value": 36.2 } }, + "timestamp": "2025-11-05T06:56:17.630Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.heat.production.heating.week" + }, + { + "apiVersion": 1, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.eco", + "feature": "heating.compressors.0.heatTarget", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false - }, - "temperature": { + "value": { + "type": "string", + "value": "none" + } + }, + "timestamp": "2025-11-05T05:33:45.330Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.heatTarget" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.compressors.0.power", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { "type": "number", - "unit": "celsius", - "value": 22 + "unit": "kilowatt", + "value": 8 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.eco" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.power" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.eco", + "feature": "heating.compressors.0.power.consumption.cooling.week", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.eco" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.power.consumption.cooling.week" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.eco", + "feature": "heating.compressors.0.power.consumption.current", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.eco" + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "kilowatt", + "value": 0 + } + }, + "timestamp": "2025-11-05T06:52:37.965Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.power.consumption.current" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.fixed", + "feature": "heating.compressors.0.power.consumption.dhw.week", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "kilowattHour", + "value": 2.3 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.fixed" + "timestamp": "2025-11-05T04:34:50.188Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.power.consumption.dhw.week" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.fixed", + "feature": "heating.compressors.0.power.consumption.heating.week", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.fixed" + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "kilowattHour", + "value": 6.3 + } + }, + "timestamp": "2025-11-05T06:56:17.630Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.power.consumption.heating.week" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.fixed", + "feature": "heating.compressors.0.sensors.power", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.fixed" + "properties": { + "status": { + "type": "string", + "value": "notConnected" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.power" }, { "apiVersion": 1, - "commands": { - "setTemperature": { - "isExecutable": true, - "name": "setTemperature", - "params": { - "targetTemperature": { - "constraints": { - "max": 30, - "min": 10, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.normal/commands/setTemperature" - } - }, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.normal", + "feature": "heating.compressors.0.sensors.pressure.inlet", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": true - }, - "demand": { + "status": { "type": "string", - "value": "unknown" + "value": "connected" }, - "temperature": { + "value": { "type": "number", - "unit": "celsius", - "value": 22 + "unit": "bar", + "value": 12.9 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.normal" + "timestamp": "2025-11-05T07:30:59.743Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.pressure.inlet" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.normal", + "feature": "heating.compressors.0.sensors.temperature.ambient", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.normal" + "properties": { + "status": { + "type": "string", + "value": "notConnected" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.temperature.ambient" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.normal", + "feature": "heating.compressors.0.sensors.temperature.inlet", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.normal" + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 23.3 + } + }, + "timestamp": "2025-11-05T07:34:06.343Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.temperature.inlet" }, { "apiVersion": 1, - "commands": { - "setTemperature": { - "isExecutable": true, - "name": "setTemperature", - "params": { - "targetTemperature": { - "constraints": { - "max": 30, - "min": 10, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.reduced/commands/setTemperature" - } - }, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.reduced", + "feature": "heating.compressors.0.sensors.temperature.outlet", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false - }, - "demand": { + "status": { "type": "string", - "value": "unknown" + "value": "connected" }, - "temperature": { + "value": { "type": "number", "unit": "celsius", - "value": 20 + "value": 31.8 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.reduced" + "timestamp": "2025-11-05T07:34:06.343Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.temperature.outlet" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.reduced", + "feature": "heating.compressors.0.sensors.temperature.overheat", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.reduced" + "properties": { + "status": { + "type": "string", + "value": "notConnected" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.sensors.temperature.overheat" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.reduced", + "feature": "heating.compressors.0.statistics", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.reduced" + "properties": { + "hours": { + "type": "number", + "unit": "hour", + "value": 2394.4 + }, + "starts": { + "type": "number", + "unit": "", + "value": 5067 + } + }, + "timestamp": "2025-11-05T06:49:02.935Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.statistics" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.operating.programs.standby", + "feature": "heating.compressors.0.statistics.load", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false + "hoursLoadClassFive": { + "type": "number", + "unit": "hour", + "value": 43 + }, + "hoursLoadClassFour": { + "type": "number", + "unit": "hour", + "value": 408 + }, + "hoursLoadClassOne": { + "type": "number", + "unit": "hour", + "value": 105 + }, + "hoursLoadClassThree": { + "type": "number", + "unit": "hour", + "value": 1305 + }, + "hoursLoadClassTwo": { + "type": "number", + "unit": "hour", + "value": 455 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.operating.programs.standby" + "timestamp": "2025-11-05T03:02:24.731Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.statistics.load" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.operating.programs.standby", + "feature": "heating.compressors.1", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.operating.programs.standby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.operating.programs.standby", + "feature": "heating.compressors.1.heat.production.current", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.operating.programs.standby" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1.heat.production.current" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.sensors.temperature.room", + "feature": "heating.compressors.1.power.consumption.current", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.sensors.temperature.room" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1.power.consumption.current" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.sensors.temperature.room", + "feature": "heating.compressors.1.statistics", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.sensors.temperature.room" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1.statistics" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.sensors.temperature.room", + "feature": "heating.compressors.1.statistics.load", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.sensors.temperature.room" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1.statistics.load" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.sensors.temperature.supply", + "feature": "heating.condensors.0.sensors.temperature.subcooling", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -1346,41 +2744,79 @@ "value": { "type": "number", "unit": "celsius", - "value": 26.3 + "value": -2.8 } }, - "timestamp": "2025-02-11T20:49:01.456Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.sensors.temperature.supply" + "timestamp": "2025-11-05T07:34:54.257Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.condensors.0.sensors.temperature.subcooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.sensors.temperature.supply", + "feature": "heating.configuration.buffer.temperature.max", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 40 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.buffer.temperature.max" + }, + { + "apiVersion": 1, + "commands": {}, + "deprecated": { + "info": "replaced by heating.dhw.configuration.temperature.dhwCylinder.max", + "removalDate": "2024-09-15" + }, + "deviceId": "0", + "feature": "heating.configuration.dhw.temperature.dhwCylinder.max", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.sensors.temperature.supply" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.dhw.temperature.dhwCylinder.max" }, { "apiVersion": 1, "commands": {}, + "deprecated": { + "info": "replaced by heating.configuration.dhw.temperature.dhwCylinder.max", + "removalDate": "2024-09-15" + }, "deviceId": "0", - "feature": "heating.circuits.2.sensors.temperature.supply", + "feature": "heating.configuration.dhw.temperature.hotWaterStorage.max", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.sensors.temperature.supply" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.dhw.temperature.hotWaterStorage.max" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.temperature", + "feature": "heating.configuration.dhwHeater", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.dhwHeater" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.configuration.flow.temperature.max", "gatewayId": "################", "isEnabled": true, "isReady": true, @@ -1388,307 +2824,279 @@ "value": { "type": "number", "unit": "celsius", - "value": 33.2 + "value": 40 } }, - "timestamp": "2025-02-11T19:48:05.380Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.flow.temperature.max" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.temperature", + "feature": "heating.configuration.flow.temperature.min", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature" + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 12 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.flow.temperature.min" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.temperature", + "feature": "heating.configuration.heatingRod.dhw", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.temperature" + "properties": { + "useApproved": { + "type": "boolean", + "value": true + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.heatingRod.dhw" }, { "apiVersion": 1, - "commands": { - "setLevels": { - "isExecutable": true, - "name": "setLevels", - "params": { - "maxTemperature": { - "constraints": { - "max": 70, - "min": 10, - "stepping": 1 - }, - "required": true, - "type": "number" - }, - "minTemperature": { - "constraints": { - "max": 30, - "min": 1, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setLevels" - }, - "setMax": { - "isExecutable": true, - "name": "setMax", - "params": { - "temperature": { - "constraints": { - "max": 70, - "min": 10, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setMax" - }, - "setMin": { - "isExecutable": true, - "name": "setMin", - "params": { - "temperature": { - "constraints": { - "max": 30, - "min": 1, - "stepping": 1 - }, - "required": true, - "type": "number" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels/commands/setMin" - } - }, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.temperature.levels", + "feature": "heating.configuration.heatingRod.heating", "gatewayId": "################", - "isEnabled": true, - "isReady": true, - "properties": { - "max": { - "type": "number", - "unit": "celsius", - "value": 44 - }, - "min": { - "type": "number", - "unit": "celsius", - "value": 15 + "isEnabled": true, + "isReady": true, + "properties": { + "useApproved": { + "type": "boolean", + "value": true } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.temperature.levels" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.heatingRod.heating" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1.temperature.levels", + "feature": "heating.configuration.smartGrid.heatingRod", "gatewayId": "################", "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.temperature.levels" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.smartGrid.heatingRod" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2.temperature.levels", + "feature": "heating.configuration.temperature.outside.DampingFactor", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.temperature.levels" + "properties": { + "value": { + "type": "number", + "unit": "minute", + "value": 180 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.configuration.temperature.outside.DampingFactor" }, { "apiVersion": 1, - "commands": { - "setName": { - "isExecutable": true, - "name": "setName", - "params": { - "name": { - "constraints": { - "maxLength": 20, - "minLength": 1 - }, - "required": true, - "type": "string" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0/commands/setName" - } - }, + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0", + "feature": "heating.controller.serial", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": true - }, - "name": { - "type": "string", - "value": "" - }, - "type": { + "value": { "type": "string", - "value": "heatingCircuit" + "value": "################" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.controller.serial" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.1", + "feature": "heating.coolingCircuits.0.reverse", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1" + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.coolingCircuits.0.reverse" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.circuits.2", + "feature": "heating.coolingCircuits.0.type", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2" + "properties": { + "value": { + "type": "string", + "value": "3xx-G Release 2014" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.coolingCircuits.0.type" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.compressors", + "feature": "heating.cop.cooling", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "enabled": { - "type": "array", - "value": ["0"] + "value": { + "type": "number", + "unit": "", + "value": 0 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.compressors" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.cop.cooling" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.compressors.0.statistics", + "feature": "heating.cop.dhw", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "hours": { - "type": "number", - "unit": "hour", - "value": 4332.4 - }, - "starts": { + "value": { "type": "number", "unit": "", - "value": 21314 + "value": 4.8 } }, - "timestamp": "2025-02-11T20:34:55.482Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0.statistics" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.cop.dhw" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.compressors.1.statistics", + "feature": "heating.cop.green", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1.statistics" + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 0 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.cop.green" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.compressors.0", + "feature": "heating.cop.heating", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "active": { - "type": "boolean", - "value": false - }, - "phase": { - "type": "string", - "value": "off" + "value": { + "type": "number", + "unit": "", + "value": 5.4 } }, - "timestamp": "2025-02-11T20:45:56.068Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.compressors.0" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.cop.heating" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.compressors.1", + "feature": "heating.cop.total", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.compressors.1" + "properties": { + "value": { + "type": "number", + "unit": "", + "value": 5.3 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.cop.total" }, { "apiVersion": 1, "commands": {}, "deviceId": "0", - "feature": "heating.controller.serial", + "feature": "heating.device.mainECU", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "value": { - "type": "string", - "value": "################" + "runtime": { + "type": "number", + "unit": "seconds", + "value": 101261244 + } + }, + "timestamp": "2025-11-05T07:37:19.435Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.mainECU" + }, + { + "apiVersion": 1, + "commands": { + "setTime": { + "isExecutable": true, + "name": "setTime", + "params": { + "value": { + "constraints": { + "regEx": "^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])$" + }, + "required": true, + "type": "string" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.time/commands/setTime" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.controller.serial" + "deviceId": "0", + "feature": "heating.device.time", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T07:20:48.714Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.device.time" }, { "apiVersion": 1, @@ -1708,8 +3116,8 @@ "value": "on" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw" }, { "apiVersion": 1, @@ -1725,8 +3133,20 @@ "value": false } }, - "timestamp": "2025-02-11T19:42:36.300Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.charging" + "timestamp": "2025-11-05T04:30:06.991Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.charging" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.dhw.configuration.temperature.dhwCylinder.max", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.configuration.temperature.dhwCylinder.max" }, { "apiVersion": 1, @@ -1735,13 +3155,25 @@ "isExecutable": true, "name": "activate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge/commands/activate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge/commands/activate" }, "deactivate": { "isExecutable": true, "name": "deactivate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge/commands/deactivate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge/commands/deactivate" + }, + "setActive": { + "isExecutable": true, + "name": "setActive", + "params": { + "active": { + "constraints": {}, + "required": true, + "type": "boolean" + } + }, + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge/commands/setActive" } }, "deviceId": "0", @@ -1755,8 +3187,8 @@ "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.oneTimeCharge" }, { "apiVersion": 1, @@ -1769,20 +3201,20 @@ "properties": { "status": { "type": "string", - "value": "on" + "value": "off" } }, - "timestamp": "2025-02-11T19:42:36.300Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation" + "timestamp": "2025-11-05T07:00:06.069Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation" }, { "apiVersion": 1, "commands": { "resetSchedule": { - "isExecutable": false, + "isExecutable": true, "name": "resetSchedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule/commands/resetSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule/commands/resetSchedule" }, "setSchedule": { "isExecutable": true, @@ -1800,7 +3232,7 @@ "type": "Schedule" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule/commands/setSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule/commands/setSchedule" } }, "deviceId": "0", @@ -1818,65 +3250,107 @@ "value": { "fri": [ { - "end": "22:00", - "mode": "on", + "end": "08:00", + "mode": "5/25-cycles", "position": 0, - "start": "06:50" + "start": "06:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "mon": [ { - "end": "22:00", - "mode": "on", + "end": "08:00", + "mode": "5/25-cycles", "position": 0, - "start": "06:50" + "start": "06:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "sat": [ { - "end": "22:00", - "mode": "on", + "end": "09:00", + "mode": "5/25-cycles", "position": 0, - "start": "07:30" + "start": "07:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "sun": [ { - "end": "22:00", - "mode": "on", + "end": "09:00", + "mode": "5/25-cycles", "position": 0, - "start": "07:30" + "start": "07:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "thu": [ { - "end": "22:00", - "mode": "on", + "end": "08:00", + "mode": "5/25-cycles", "position": 0, - "start": "06:50" + "start": "06:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "tue": [ { - "end": "22:00", - "mode": "on", + "end": "08:00", + "mode": "5/25-cycles", "position": 0, - "start": "06:50" + "start": "06:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ], "wed": [ { - "end": "22:00", - "mode": "on", + "end": "08:00", + "mode": "5/25-cycles", "position": 0, - "start": "06:50" + "start": "06:00" + }, + { + "end": "20:00", + "mode": "5/25-cycles", + "position": 1, + "start": "18:00" } ] } } }, - "timestamp": "2025-02-11T17:50:12.565Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.circulation.schedule" }, { "apiVersion": 1, @@ -1887,17 +3361,17 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.primary" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.pumps.primary" }, { "apiVersion": 1, "commands": { "resetSchedule": { - "isExecutable": false, + "isExecutable": true, "name": "resetSchedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule/commands/resetSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule/commands/resetSchedule" }, "setSchedule": { "isExecutable": true, @@ -1915,7 +3389,7 @@ "type": "Schedule" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule/commands/setSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule/commands/setSchedule" } }, "deviceId": "0", @@ -1936,7 +3410,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "mon": [ @@ -1944,7 +3418,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "sat": [ @@ -1952,7 +3426,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "sun": [ @@ -1960,7 +3434,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "thu": [ @@ -1968,7 +3442,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "tue": [ @@ -1976,7 +3450,7 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ], "wed": [ @@ -1984,14 +3458,14 @@ "end": "22:00", "mode": "normal", "position": 0, - "start": "06:00" + "start": "05:30" } ] } } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.schedule" }, { "apiVersion": 1, @@ -2009,11 +3483,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 47.9 + "value": 45.1 } }, - "timestamp": "2025-02-11T20:39:18.305Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder" + "timestamp": "2025-11-05T07:24:39.759Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder" }, { "apiVersion": 1, @@ -2029,8 +3503,8 @@ "value": "notConnected" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder.bottom" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder.bottom" }, { "apiVersion": 1, @@ -2048,11 +3522,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 47.9 + "value": 45.1 } }, - "timestamp": "2025-02-11T20:39:18.305Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder.top" + "timestamp": "2025-11-05T07:24:39.759Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.dhwCylinder.top" }, { "apiVersion": 1, @@ -2074,11 +3548,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 47.9 + "value": 45.1 } }, - "timestamp": "2025-02-11T20:39:18.305Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage" + "timestamp": "2025-11-05T07:24:39.759Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage" }, { "apiVersion": 1, @@ -2098,8 +3572,8 @@ "value": "notConnected" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage.bottom" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage.bottom" }, { "apiVersion": 1, @@ -2121,11 +3595,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 47.9 + "value": 45.1 } }, - "timestamp": "2025-02-11T20:39:18.305Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage.top" + "timestamp": "2025-11-05T07:24:39.759Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.hotWaterStorage.top" }, { "apiVersion": 1, @@ -2141,8 +3615,8 @@ "value": "notConnected" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.outlet" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.sensors.temperature.outlet" }, { "apiVersion": 1, @@ -2161,7 +3635,7 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresis" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresis" }, "setHysteresisSwitchOffValue": { "isExecutable": false, @@ -2177,7 +3651,7 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresisSwitchOffValue" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresisSwitchOffValue" }, "setHysteresisSwitchOnValue": { "isExecutable": true, @@ -2193,7 +3667,7 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresisSwitchOnValue" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis/commands/setHysteresisSwitchOnValue" } }, "deviceId": "0", @@ -2218,8 +3692,8 @@ "value": 5 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.hysteresis" }, { "apiVersion": 1, @@ -2240,7 +3714,7 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.main/commands/setTargetTemperature" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.main/commands/setTargetTemperature" } }, "deviceId": "0", @@ -2252,11 +3726,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 50 + "value": 45 } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.main" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.main" }, { "apiVersion": 1, @@ -2275,23 +3749,136 @@ "type": "number" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.temp2/commands/setTargetTemperature" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.temp2/commands/setTargetTemperature" + } + }, + "deviceId": "0", + "feature": "heating.dhw.temperature.temp2", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "number", + "unit": "celsius", + "value": 45 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.temp2" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.evaporators.0.sensors.temperature.liquid", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 18.2 + } + }, + "timestamp": "2025-11-05T07:34:50.587Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.evaporators.0.sensors.temperature.liquid" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.evaporators.0.sensors.temperature.overheat", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 0 + } + }, + "timestamp": "2025-11-05T06:52:41.702Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.evaporators.0.sensors.temperature.overheat" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.heatingRod.heatTarget", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "value": { + "type": "string", + "value": "none" + } + }, + "timestamp": "2025-11-05T06:52:29.129Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.heatingRod.heatTarget" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.heatingRod.runtime", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "levelOne": { + "type": "number", + "unit": "seconds", + "value": 815476 + }, + "levelTwo": { + "type": "number", + "unit": "seconds", + "value": 614313 } }, + "timestamp": "2025-11-05T06:53:58.597Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.heatingRod.runtime" + }, + { + "apiVersion": 1, + "commands": {}, "deviceId": "0", - "feature": "heating.dhw.temperature.temp2", + "feature": "heating.heatingRod.status", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "value": { - "type": "number", - "unit": "celsius", - "value": 60 + "level1": { + "type": "boolean", + "value": false + }, + "level2": { + "type": "boolean", + "value": false + }, + "level3": { + "type": "boolean", + "value": false + }, + "overall": { + "type": "boolean", + "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.dhw.temperature.temp2" + "timestamp": "2025-11-05T06:52:21.601Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.heatingRod.status" }, { "apiVersion": 1, @@ -2309,7 +3896,7 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/changeEndDate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/changeEndDate" }, "schedule": { "isExecutable": true, @@ -2331,13 +3918,13 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/schedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/schedule" }, "unschedule": { "isExecutable": true, "name": "unschedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/unschedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday/commands/unschedule" } }, "deviceId": "0", @@ -2359,8 +3946,30 @@ "value": "" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.operating.programs.holiday" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.primaryCircuit.sensors.rotation", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "percent", + "value": 0 + } + }, + "timestamp": "2025-11-05T06:52:25.762Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.primaryCircuit.sensors.rotation" }, { "apiVersion": 1, @@ -2378,11 +3987,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 6.9 + "value": 15.5 } }, - "timestamp": "2025-02-11T20:58:31.054Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.primaryCircuit.sensors.temperature.return" + "timestamp": "2025-11-05T07:36:41.239Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.primaryCircuit.sensors.temperature.return" }, { "apiVersion": 1, @@ -2400,11 +4009,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 5.2 + "value": 14.6 } }, - "timestamp": "2025-02-11T20:48:38.307Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.primaryCircuit.sensors.temperature.supply" + "timestamp": "2025-11-05T07:32:35.014Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.primaryCircuit.sensors.temperature.supply" }, { "apiVersion": 1, @@ -2422,11 +4031,99 @@ "value": { "type": "number", "unit": "celsius", - "value": 26.9 + "value": 35.2 + } + }, + "timestamp": "2025-11-05T07:36:20.795Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.secondaryCircuit.sensors.temperature.supply" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.sensors.pressure.hotGas", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "bar", + "value": 12.7 + } + }, + "timestamp": "2025-11-05T07:29:36.342Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.pressure.hotGas" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.sensors.pressure.suctionGas", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "bar", + "value": 12.9 + } + }, + "timestamp": "2025-11-05T07:30:59.743Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.pressure.suctionGas" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.sensors.temperature.hotGas", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 31.8 + } + }, + "timestamp": "2025-11-05T07:34:06.343Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.hotGas" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.sensors.temperature.liquidGas", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 18.2 } }, - "timestamp": "2025-02-11T20:46:37.502Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.secondaryCircuit.sensors.temperature.supply" + "timestamp": "2025-11-05T07:34:50.587Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.liquidGas" }, { "apiVersion": 1, @@ -2444,11 +4141,11 @@ "value": { "type": "number", "unit": "celsius", - "value": 1.9 + "value": 6.1 } }, - "timestamp": "2025-02-11T21:00:13.154Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.outside" + "timestamp": "2025-11-05T06:23:43.575Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.outside" }, { "apiVersion": 1, @@ -2466,11 +4163,33 @@ "value": { "type": "number", "unit": "celsius", - "value": 26.5 + "value": 33.2 + } + }, + "timestamp": "2025-11-05T07:35:16.900Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.return" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.sensors.temperature.suctionGas", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "connected" + }, + "value": { + "type": "number", + "unit": "celsius", + "value": 23.3 } }, - "timestamp": "2025-02-11T20:48:00.474Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.return" + "timestamp": "2025-11-05T07:34:06.343Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.sensors.temperature.suctionGas" }, { "apiVersion": 1, @@ -2481,8 +4200,8 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar" }, { "apiVersion": 1, @@ -2493,8 +4212,8 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar.power.cumulativeProduced" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.power.cumulativeProduced" }, { "apiVersion": 1, @@ -2505,8 +4224,8 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar.power.production" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.power.production" }, { "apiVersion": 1, @@ -2517,8 +4236,20 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar.pumps.circuit" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.pumps.circuit" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.solar.rechargeSuppression", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.rechargeSuppression" }, { "apiVersion": 1, @@ -2529,8 +4260,8 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar.sensors.temperature.collector" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.sensors.temperature.collector" }, { "apiVersion": 1, @@ -2541,8 +4272,20 @@ "isEnabled": false, "isReady": true, "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.solar.sensors.temperature.dhw" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.sensors.temperature.dhw" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "heating.solar.statistics", + "gatewayId": "################", + "isEnabled": false, + "isReady": true, + "properties": {}, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/heating.solar.statistics" }, { "apiVersion": 1, @@ -2558,8 +4301,25 @@ "value": true } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.heatExchanger.frostprotection", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "status": { + "type": "string", + "value": "off" + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.heatExchanger.frostprotection" }, { "apiVersion": 1, @@ -2567,11 +4327,17 @@ "deviceId": "0", "feature": "ventilation.levels.levelFour", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelFour" + "properties": { + "volumeFlow": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 257 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelFour" }, { "apiVersion": 1, @@ -2579,11 +4345,17 @@ "deviceId": "0", "feature": "ventilation.levels.levelOne", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelOne" + "properties": { + "volumeFlow": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 85 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelOne" }, { "apiVersion": 1, @@ -2591,11 +4363,17 @@ "deviceId": "0", "feature": "ventilation.levels.levelThree", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelThree" + "properties": { + "volumeFlow": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 191 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelThree" }, { "apiVersion": 1, @@ -2603,11 +4381,17 @@ "deviceId": "0", "feature": "ventilation.levels.levelTwo", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelTwo" + "properties": { + "volumeFlow": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 125 + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.levels.levelTwo" }, { "apiVersion": 1, @@ -2624,13 +4408,13 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setMode" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setMode" }, "setModeContinuousSensorOverride": { "isExecutable": false, "name": "setModeContinuousSensorOverride", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setModeContinuousSensorOverride" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active/commands/setModeContinuousSensorOverride" } }, "deviceId": "0", @@ -2644,8 +4428,8 @@ "value": "ventilation" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.active" }, { "apiVersion": 1, @@ -2661,8 +4445,25 @@ "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.standard" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.standard" + }, + { + "apiVersion": 1, + "commands": {}, + "deviceId": "0", + "feature": "ventilation.operating.modes.standby", + "gatewayId": "################", + "isEnabled": true, + "isReady": true, + "properties": { + "active": { + "type": "boolean", + "value": false + } + }, + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.standby" }, { "apiVersion": 1, @@ -2678,8 +4479,8 @@ "value": true } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.ventilation" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.modes.ventilation" }, { "apiVersion": 1, @@ -2695,8 +4496,8 @@ "value": "levelThree" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.programs.active" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.programs.active" }, { "apiVersion": 1, @@ -2720,8 +4521,8 @@ "value": "schedule" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.state" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.operating.state" }, { "apiVersion": 1, @@ -2730,13 +4531,13 @@ "isExecutable": true, "name": "activate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort/commands/activate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort/commands/activate" }, "deactivate": { "isExecutable": true, "name": "deactivate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort/commands/deactivate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort/commands/deactivate" } }, "deviceId": "0", @@ -2750,8 +4551,8 @@ "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.comfort" }, { "apiVersion": 1, @@ -2760,13 +4561,13 @@ "isExecutable": true, "name": "activate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco/commands/activate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco/commands/activate" }, "deactivate": { "isExecutable": true, "name": "deactivate", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco/commands/deactivate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco/commands/deactivate" } }, "deviceId": "0", @@ -2780,8 +4581,8 @@ "value": false } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.eco" }, { "apiVersion": 1, @@ -2799,7 +4600,7 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/changeEndDate" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/changeEndDate" }, "schedule": { "isExecutable": true, @@ -2821,13 +4622,13 @@ "type": "string" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/schedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/schedule" }, "unschedule": { "isExecutable": true, "name": "unschedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/unschedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday/commands/unschedule" } }, "deviceId": "0", @@ -2849,17 +4650,17 @@ "value": "" } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.quickmodes.holiday" }, { "apiVersion": 1, "commands": { "resetSchedule": { - "isExecutable": false, + "isExecutable": true, "name": "resetSchedule", "params": {}, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/resetSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/resetSchedule" }, "setSchedule": { "isExecutable": true, @@ -2877,7 +4678,7 @@ "type": "Schedule" } }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/setSchedule" + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule/commands/setSchedule" } }, "deviceId": "0", @@ -2952,68 +4753,44 @@ } } }, - "timestamp": "2025-02-10T14:01:48.216Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule" + "timestamp": "2025-11-05T00:20:46.443Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.schedule" }, { "apiVersion": 1, - "commands": { - "setName": { - "isExecutable": true, - "name": "setName", - "params": { - "name": { - "constraints": { - "maxLength": 20, - "minLength": 1 - }, - "required": true, - "type": "string" - } - }, - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.name/commands/setName" - } - }, - "components": [], + "commands": {}, "deviceId": "0", - "feature": "heating.circuits.0.name", + "feature": "ventilation.volumeFlow.current.input", "gatewayId": "################", "isEnabled": true, "isReady": true, "properties": { - "name": { - "type": "string", - "value": "" + "value": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 257 } }, - "timestamp": "2025-01-12T22:36:28.706Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.0.name" - }, - { - "apiVersion": 1, - "commands": {}, - "components": [], - "deviceId": "0", - "feature": "heating.circuits.1.name", - "gatewayId": "################", - "isEnabled": false, - "isReady": true, - "properties": {}, - "timestamp": "2024-02-02T01:29:44.670Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.1.name" + "timestamp": "2025-11-05T07:15:46.689Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.volumeFlow.current.input" }, { "apiVersion": 1, "commands": {}, - "components": [], "deviceId": "0", - "feature": "heating.circuits.2.name", + "feature": "ventilation.volumeFlow.current.output", "gatewayId": "################", - "isEnabled": false, + "isEnabled": true, "isReady": true, - "properties": {}, - "timestamp": "2024-02-02T01:29:44.670Z", - "uri": "https://api.viessmann.com/iot/v1/features/installations/#######/gateways/################/devices/0/features/heating.circuits.2.name" + "properties": { + "value": { + "type": "number", + "unit": "cubicMeter/hour", + "value": 257 + } + }, + "timestamp": "2025-11-05T07:15:46.689Z", + "uri": "https://api.viessmann-climatesolutions.com/iot/v2/features/installations/#######/gateways/################/devices/0/features/ventilation.volumeFlow.current.output" } ] } diff --git a/tests/components/vicare/snapshots/test_sensor.ambr b/tests/components/vicare/snapshots/test_sensor.ambr index 1d2ff27bbe27c..8a1bc268fa9f7 100644 --- a/tests/components/vicare/snapshots/test_sensor.ambr +++ b/tests/components/vicare/snapshots/test_sensor.ambr @@ -1122,6 +1122,166 @@ 'state': '25.5', }) # --- +# name: test_all_entities[sensor.model10_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.model10_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': 'gateway10_zigbee_################-zigbee_signal_strength', + 'unit_of_measurement': '%', + }) +# --- +# name: test_all_entities[sensor.model10_signal_strength-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model10 Signal strength', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.model10_signal_strength', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '46', + }) +# --- +# name: test_all_entities[sensor.model11_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.model11_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': 'gateway11_zigbee_################-zigbee_signal_strength', + 'unit_of_measurement': '%', + }) +# --- +# name: test_all_entities[sensor.model11_signal_strength-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model11 Signal strength', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.model11_signal_strength', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '37', + }) +# --- +# name: test_all_entities[sensor.model11_supply_temperature-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': None, + 'entity_id': 'sensor.model11_supply_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Supply temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'supply_temperature', + 'unique_id': 'gateway11_zigbee_################-supply_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model11_supply_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model11 Supply temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model11_supply_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '31.0', + }) +# --- # name: test_all_entities[sensor.model1_boiler_supply_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -2701,19 +2861,13 @@ 'state': '1.015', }) # --- -# name: test_all_entities[sensor.model2_ventilation_level-entry] +# name: test_all_entities[sensor.model2_buffer_main_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'options': list([ - 'standby', - 'levelone', - 'leveltwo', - 'levelthree', - 'levelfour', - ]), + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -2722,7 +2876,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model2_ventilation_level', + 'entity_id': 'sensor.model2_buffer_main_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2731,54 +2885,45 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Ventilation level', + 'original_name': 'Buffer main temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'ventilation_level', - 'unique_id': 'gateway2_deviceSerialViAir300F-ventilation_level', - 'unit_of_measurement': None, + 'translation_key': 'buffer_main_temperature', + 'unique_id': 'gateway2_################-buffer main temperature', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model2_ventilation_level-state] +# name: test_all_entities[sensor.model2_buffer_main_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'model2 Ventilation level', - 'options': list([ - 'standby', - 'levelone', - 'leveltwo', - 'levelthree', - 'levelfour', - ]), + 'device_class': 'temperature', + 'friendly_name': 'model2 Buffer main temperature', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model2_ventilation_level', + 'entity_id': 'sensor.model2_buffer_main_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'levelone', + 'state': '41.2', }) # --- -# name: test_all_entities[sensor.model2_ventilation_reason-entry] +# name: test_all_entities[sensor.model2_buffer_top_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'options': list([ - 'standby', - 'permanent', - 'schedule', - 'sensordriven', - 'silent', - 'forcedlevelfour', - ]), + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -2786,8 +2931,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.model2_ventilation_reason', + 'entity_category': None, + 'entity_id': 'sensor.model2_buffer_top_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2796,42 +2941,39 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Ventilation reason', + 'original_name': 'Buffer top temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'ventilation_reason', - 'unique_id': 'gateway2_deviceSerialViAir300F-ventilation_reason', - 'unit_of_measurement': None, + 'translation_key': 'buffer_top_temperature', + 'unique_id': 'gateway2_################-buffer top temperature', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model2_ventilation_reason-state] +# name: test_all_entities[sensor.model2_buffer_top_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'enum', - 'friendly_name': 'model2 Ventilation reason', - 'options': list([ - 'standby', - 'permanent', - 'schedule', - 'sensordriven', - 'silent', - 'forcedlevelfour', - ]), + 'device_class': 'temperature', + 'friendly_name': 'model2 Buffer top temperature', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model2_ventilation_reason', + 'entity_id': 'sensor.model2_buffer_top_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'permanent', + 'state': '41.2', }) # --- -# name: test_all_entities[sensor.model3_battery_charge_total-entry] +# name: test_all_entities[sensor.model2_compressor_hours-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -2845,8 +2987,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.model3_battery_charge_total', + 'entity_category': , + 'entity_id': 'sensor.model2_compressor_hours', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2855,54 +2997,48 @@ }), 'name': None, 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), }), - 'original_device_class': , + 'original_device_class': None, 'original_icon': None, - 'original_name': 'Battery charge total', + 'original_name': 'Compressor hours', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'ess_charge_total', - 'unique_id': 'gateway3_deviceId3-ess_charge_total', - 'unit_of_measurement': , + 'translation_key': 'compressor_hours', + 'unique_id': 'gateway2_################-compressor_hours-0', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model3_battery_charge_total-state] +# name: test_all_entities[sensor.model2_compressor_hours-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'model3 Battery charge total', + 'friendly_name': 'model2 Compressor hours', 'state_class': , - 'unit_of_measurement': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model3_battery_charge_total', + 'entity_id': 'sensor.model2_compressor_hours', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '1879163', + 'state': '2394.4', }) # --- -# name: test_all_entities[sensor.model5_battery-entry] +# name: test_all_entities[sensor.model2_compressor_inlet_pressure-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), + 'capabilities': None, 'config_entry_id': , 'config_subentry_id': , 'device_class': None, 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.model5_battery', + 'entity_category': None, + 'entity_id': 'sensor.model2_compressor_inlet_pressure', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2911,42 +3047,1804 @@ }), 'name': None, 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Battery', + 'original_name': 'Compressor inlet pressure', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-battery_level', - 'unit_of_measurement': '%', + 'translation_key': 'compressor_inlet_pressure', + 'unique_id': 'gateway2_################-compressor_inlet_pressure-0', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model5_battery-state] +# name: test_all_entities[sensor.model2_compressor_inlet_pressure-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'battery', - 'friendly_name': 'model5 Battery', - 'state_class': , - 'unit_of_measurement': '%', + 'device_class': 'pressure', + 'friendly_name': 'model2 Compressor inlet pressure', + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model5_battery', + 'entity_id': 'sensor.model2_compressor_inlet_pressure', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '89', + 'state': '12.9', }) # --- -# name: test_all_entities[sensor.model5_humidity-entry] +# name: test_all_entities[sensor.model2_compressor_inlet_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, - 'capabilities': dict({ - 'state_class': , + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_compressor_inlet_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Compressor inlet temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'compressor_inlet_temperature', + 'unique_id': 'gateway2_################-compressor_inlet_temperature-0', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_compressor_inlet_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Compressor inlet temperature', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_compressor_inlet_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '23.3', + }) +# --- +# name: test_all_entities[sensor.model2_compressor_outlet_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': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_compressor_outlet_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Compressor outlet temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'compressor_outlet_temperature', + 'unique_id': 'gateway2_################-compressor_outlet_temperature-0', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_compressor_outlet_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Compressor outlet temperature', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_compressor_outlet_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '31.8', + }) +# --- +# name: test_all_entities[sensor.model2_compressor_phase-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.model2_compressor_phase', + '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': 'Compressor phase', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'compressor_phase', + 'unique_id': 'gateway2_################-compressor_phase-0', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model2_compressor_phase-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model2 Compressor phase', + }), + 'context': , + 'entity_id': 'sensor.model2_compressor_phase', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[sensor.model2_compressor_starts-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.model2_compressor_starts', + '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': 'Compressor starts', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'compressor_starts', + 'unique_id': 'gateway2_################-compressor_starts-0', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model2_compressor_starts-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model2 Compressor starts', + 'state_class': , + }), + 'context': , + 'entity_id': 'sensor.model2_compressor_starts', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5067', + }) +# --- +# name: test_all_entities[sensor.model2_condensor_subcooling_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': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_condensor_subcooling_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Condensor subcooling temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'condensor_subcooling_temperature', + 'unique_id': 'gateway2_################-condensor_subcooling_temperature-0', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_condensor_subcooling_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Condensor subcooling temperature', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_condensor_subcooling_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '-2.8', + }) +# --- +# name: test_all_entities[sensor.model2_dhw_max_temperature-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': None, + 'entity_id': 'sensor.model2_dhw_max_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW max temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'hotwater_max_temperature', + 'unique_id': 'gateway2_################-hotwater_max_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_dhw_max_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 DHW max temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_dhw_max_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '60', + }) +# --- +# name: test_all_entities[sensor.model2_dhw_min_temperature-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': None, + 'entity_id': 'sensor.model2_dhw_min_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW min temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'hotwater_min_temperature', + 'unique_id': 'gateway2_################-hotwater_min_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_dhw_min_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 DHW min temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_dhw_min_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- +# name: test_all_entities[sensor.model2_dhw_storage_temperature-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': None, + 'entity_id': 'sensor.model2_dhw_storage_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW storage temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dhw_storage_temperature', + 'unique_id': 'gateway2_################-dhw_storage_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_dhw_storage_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 DHW storage temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_dhw_storage_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '45.1', + }) +# --- +# name: test_all_entities[sensor.model2_dhw_storage_top_temperature-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': None, + 'entity_id': 'sensor.model2_dhw_storage_top_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'DHW storage top temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dhw_storage_top_temperature', + 'unique_id': 'gateway2_################-dhw_storage_top_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_dhw_storage_top_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 DHW storage top temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_dhw_storage_top_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '45.1', + }) +# --- +# name: test_all_entities[sensor.model2_evaporator_liquid_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': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_evaporator_liquid_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Evaporator liquid temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'evaporator_liquid_temperature', + 'unique_id': 'gateway2_################-evaporator_liquid_temperature-0', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_evaporator_liquid_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Evaporator liquid temperature', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_evaporator_liquid_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '18.2', + }) +# --- +# name: test_all_entities[sensor.model2_evaporator_overheat_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': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_evaporator_overheat_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Evaporator overheat temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'evaporator_overheat_temperature', + 'unique_id': 'gateway2_################-evaporator_overheat_temperature-0', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_evaporator_overheat_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Evaporator overheat temperature', + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_evaporator_overheat_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_all_entities[sensor.model2_outside_temperature-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': None, + 'entity_id': 'sensor.model2_outside_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Outside temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'outside_temperature', + 'unique_id': 'gateway2_################-outside_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_outside_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Outside temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_outside_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6.1', + }) +# --- +# name: test_all_entities[sensor.model2_primary_circuit_return_temperature-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': None, + 'entity_id': 'sensor.model2_primary_circuit_return_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Primary circuit return temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'primary_circuit_return_temperature', + 'unique_id': 'gateway2_################-primary_circuit_return_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_primary_circuit_return_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Primary circuit return temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_primary_circuit_return_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '15.5', + }) +# --- +# name: test_all_entities[sensor.model2_primary_circuit_supply_temperature-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': None, + 'entity_id': 'sensor.model2_primary_circuit_supply_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Primary circuit supply temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'primary_circuit_supply_temperature', + 'unique_id': 'gateway2_################-primary_circuit_supply_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_primary_circuit_supply_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Primary circuit supply temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_primary_circuit_supply_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '14.6', + }) +# --- +# name: test_all_entities[sensor.model2_return_temperature-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': None, + 'entity_id': 'sensor.model2_return_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Return temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'return_temperature', + 'unique_id': 'gateway2_################-return_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_return_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Return temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_return_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '33.2', + }) +# --- +# name: test_all_entities[sensor.model2_secondary_circuit_supply_temperature-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': None, + 'entity_id': 'sensor.model2_secondary_circuit_supply_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Secondary circuit supply temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'secondary_circuit_supply_temperature', + 'unique_id': 'gateway2_################-secondary_circuit_supply_temperature', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_secondary_circuit_supply_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Secondary circuit supply temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_secondary_circuit_supply_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '35.2', + }) +# --- +# name: test_all_entities[sensor.model2_supply_temperature-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': None, + 'entity_id': 'sensor.model2_supply_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Supply temperature', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'supply_temperature', + 'unique_id': 'gateway2_################-supply_temperature-1', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_supply_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model2 Supply temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_supply_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '34.3', + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_input_volume_flow-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': None, + 'entity_id': 'sensor.model2_ventilation_input_volume_flow', + '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': 'Ventilation input volume flow', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_input_volumeflow', + 'unique_id': 'gateway2_################-ventilation_input_volumeflow', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_input_volume_flow-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model2 Ventilation input volume flow', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_ventilation_input_volume_flow', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '257', + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model2_ventilation_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': 'Ventilation level', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_level', + 'unique_id': 'gateway2_################-ventilation_level', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'model2 Ventilation level', + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), + }), + 'context': , + 'entity_id': 'sensor.model2_ventilation_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'levelthree', + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_output_volume_flow-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': None, + 'entity_id': 'sensor.model2_ventilation_output_volume_flow', + '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': 'Ventilation output volume flow', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_output_volumeflow', + 'unique_id': 'gateway2_################-ventilation_output_volumeflow', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_output_volume_flow-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model2 Ventilation output volume flow', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model2_ventilation_output_volume_flow', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '257', + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_reason-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.model2_ventilation_reason', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ventilation reason', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_reason', + 'unique_id': 'gateway2_################-ventilation_reason', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model2_ventilation_reason-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'model2 Ventilation reason', + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'context': , + 'entity_id': 'sensor.model2_ventilation_reason', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'schedule', + }) +# --- +# name: test_all_entities[sensor.model3_ventilation_level-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.model3_ventilation_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': 'Ventilation level', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_level', + 'unique_id': 'gateway3_deviceSerialViAir300F-ventilation_level', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model3_ventilation_level-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'model3 Ventilation level', + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), + }), + 'context': , + 'entity_id': 'sensor.model3_ventilation_level', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'levelone', + }) +# --- +# name: test_all_entities[sensor.model3_ventilation_reason-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.model3_ventilation_reason', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ventilation reason', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_reason', + 'unique_id': 'gateway3_deviceSerialViAir300F-ventilation_reason', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model3_ventilation_reason-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'model3 Ventilation reason', + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'context': , + 'entity_id': 'sensor.model3_ventilation_reason', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'permanent', + }) +# --- +# name: test_all_entities[sensor.model4_filter_hours-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.model4_filter_hours', + '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': 'Filter hours', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filter_hours', + 'unique_id': 'gateway4_deviceId4-filter_hours', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model4_filter_hours-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model4 Filter hours', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model4_filter_hours', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '5795', + }) +# --- +# name: test_all_entities[sensor.model4_filter_overdue_hours-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.model4_filter_overdue_hours', + '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': 'Filter overdue hours', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filter_overdue_hours', + 'unique_id': 'gateway4_deviceId4-filter_overdue_hours', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model4_filter_overdue_hours-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model4 Filter overdue hours', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model4_filter_overdue_hours', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0', + }) +# --- +# name: test_all_entities[sensor.model4_filter_remaining_hours-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.model4_filter_remaining_hours', + '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': 'Filter remaining hours', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'filter_remaining_hours', + 'unique_id': 'gateway4_deviceId4-filter_remaining_hours', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model4_filter_remaining_hours-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model4 Filter remaining hours', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model4_filter_remaining_hours', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2965', + }) +# --- +# name: test_all_entities[sensor.model4_pm1-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': None, + 'entity_id': 'sensor.model4_pm1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PM1', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'gateway4_deviceId4-pm01', + 'unit_of_measurement': 'μg/m³', + }) +# --- +# name: test_all_entities[sensor.model4_pm1-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'pm1', + 'friendly_name': 'model4 PM1', + 'state_class': , + 'unit_of_measurement': 'μg/m³', + }), + 'context': , + 'entity_id': 'sensor.model4_pm1', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.1', + }) +# --- +# name: test_all_entities[sensor.model4_pm10-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': None, + 'entity_id': 'sensor.model4_pm10', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PM10', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'gateway4_deviceId4-pm10', + 'unit_of_measurement': 'μg/m³', + }) +# --- +# name: test_all_entities[sensor.model4_pm10-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'pm10', + 'friendly_name': 'model4 PM10', + 'state_class': , + 'unit_of_measurement': 'μg/m³', + }), + 'context': , + 'entity_id': 'sensor.model4_pm10', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.5', + }) +# --- +# name: test_all_entities[sensor.model4_pm2_5-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': None, + 'entity_id': 'sensor.model4_pm2_5', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PM2.5', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'gateway4_deviceId4-pm02', + 'unit_of_measurement': 'μg/m³', + }) +# --- +# name: test_all_entities[sensor.model4_pm2_5-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'pm25', + 'friendly_name': 'model4 PM2.5', + 'state_class': , + 'unit_of_measurement': 'μg/m³', + }), + 'context': , + 'entity_id': 'sensor.model4_pm2_5', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.3', + }) +# --- +# name: test_all_entities[sensor.model4_pm4-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': None, + 'entity_id': 'sensor.model4_pm4', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'PM4', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'gateway4_deviceId4-pm04', + 'unit_of_measurement': 'μg/m³', + }) +# --- +# name: test_all_entities[sensor.model4_pm4-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'pm4', + 'friendly_name': 'model4 PM4', + 'state_class': , + 'unit_of_measurement': 'μg/m³', + }), + 'context': , + 'entity_id': 'sensor.model4_pm4', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.4', + }) +# --- +# name: test_all_entities[sensor.model4_supply_fan_hours-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.model4_supply_fan_hours', + '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': 'Supply fan hours', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'supply_fan_hours', + 'unique_id': 'gateway4_deviceId4-supply_fan_hours', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[sensor.model4_supply_fan_hours-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model4 Supply fan hours', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model4_supply_fan_hours', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '819', + }) +# --- +# name: test_all_entities[sensor.model4_supply_fan_speed-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.model4_supply_fan_speed', + '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': 'Supply fan speed', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'supply_fan_speed', + 'unique_id': 'gateway4_deviceId4-supply_fan_speed', + 'unit_of_measurement': 'rpm', + }) +# --- +# name: test_all_entities[sensor.model4_supply_fan_speed-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model4 Supply fan speed', + 'state_class': , + 'unit_of_measurement': 'rpm', + }), + 'context': , + 'entity_id': 'sensor.model4_supply_fan_speed', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '555', + }) +# --- +# name: test_all_entities[sensor.model4_supply_humidity-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -2955,7 +4853,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model5_humidity', + 'entity_id': 'sensor.model4_supply_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -2967,33 +4865,33 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Humidity', + 'original_name': 'Supply humidity', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-room_humidity', + 'translation_key': 'supply_humidity', + 'unique_id': 'gateway4_deviceId4-supply_humidity', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model5_humidity-state] +# name: test_all_entities[sensor.model4_supply_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'humidity', - 'friendly_name': 'model5 Humidity', + 'friendly_name': 'model4 Supply humidity', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model5_humidity', + 'entity_id': 'sensor.model4_supply_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '53', + 'state': '59', }) # --- -# name: test_all_entities[sensor.model5_temperature-entry] +# name: test_all_entities[sensor.model4_supply_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3008,7 +4906,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model5_temperature', + 'entity_id': 'sensor.model4_supply_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3023,39 +4921,45 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Temperature', + 'original_name': 'Supply temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway5_zigbee_d87a3bfffe5d844a-room_temperature', + 'translation_key': 'supply_temperature', + 'unique_id': 'gateway4_deviceId4-supply_temperature', 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model5_temperature-state] +# name: test_all_entities[sensor.model4_supply_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'temperature', - 'friendly_name': 'model5 Temperature', + 'friendly_name': 'model4 Supply temperature', 'state_class': , 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model5_temperature', + 'entity_id': 'sensor.model4_supply_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '17.5', + 'state': '20.8', }) # --- -# name: test_all_entities[sensor.model6_humidity-entry] +# name: test_all_entities[sensor.model4_ventilation_level-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), }), 'config_entry_id': , 'config_subentry_id': , @@ -3064,7 +4968,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model6_humidity', + 'entity_id': 'sensor.model4_ventilation_level', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3074,41 +4978,112 @@ 'name': None, 'options': dict({ }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Humidity', + 'original_name': 'Ventilation level', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway6_zigbee_5cc7c1fffea33a3b-room_humidity', - 'unit_of_measurement': '%', + 'translation_key': 'ventilation_level', + 'unique_id': 'gateway4_deviceId4-ventilation_level', + 'unit_of_measurement': None, }) # --- -# name: test_all_entities[sensor.model6_humidity-state] +# name: test_all_entities[sensor.model4_ventilation_level-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'humidity', - 'friendly_name': 'model6 Humidity', - 'state_class': , - 'unit_of_measurement': '%', + 'device_class': 'enum', + 'friendly_name': 'model4 Ventilation level', + 'options': list([ + 'standby', + 'levelone', + 'leveltwo', + 'levelthree', + 'levelfour', + ]), }), 'context': , - 'entity_id': 'sensor.model6_humidity', + 'entity_id': 'sensor.model4_ventilation_level', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '52', + 'state': 'unavailable', }) # --- -# name: test_all_entities[sensor.model6_temperature-entry] +# name: test_all_entities[sensor.model4_ventilation_reason-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), 'area_id': None, 'capabilities': dict({ - 'state_class': , + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.model4_ventilation_reason', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Ventilation reason', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ventilation_reason', + 'unique_id': 'gateway4_deviceId4-ventilation_reason', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[sensor.model4_ventilation_reason-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'enum', + 'friendly_name': 'model4 Ventilation reason', + 'options': list([ + 'standby', + 'permanent', + 'schedule', + 'sensordriven', + 'silent', + 'forcedlevelfour', + ]), + }), + 'context': , + 'entity_id': 'sensor.model4_ventilation_reason', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'sensordriven', + }) +# --- +# name: test_all_entities[sensor.model5_battery_charge_total-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , }), 'config_entry_id': , 'config_subentry_id': , @@ -3117,7 +5092,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model6_temperature', + 'entity_id': 'sensor.model5_battery_charge_total', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3127,35 +5102,35 @@ 'name': None, 'options': dict({ 'sensor': dict({ - 'suggested_display_precision': 1, + 'suggested_display_precision': 0, }), }), - 'original_device_class': , + 'original_device_class': , 'original_icon': None, - 'original_name': 'Temperature', + 'original_name': 'Battery charge total', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': None, - 'unique_id': 'gateway6_zigbee_5cc7c1fffea33a3b-room_temperature', - 'unit_of_measurement': , + 'translation_key': 'ess_charge_total', + 'unique_id': 'gateway5_deviceId5-ess_charge_total', + 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model6_temperature-state] +# name: test_all_entities[sensor.model5_battery_charge_total-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'model6 Temperature', - 'state_class': , - 'unit_of_measurement': , + 'device_class': 'energy', + 'friendly_name': 'model5 Battery charge total', + 'state_class': , + 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model6_temperature', + 'entity_id': 'sensor.model5_battery_charge_total', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '16.9', + 'state': '1879163', }) # --- # name: test_all_entities[sensor.model7_battery-entry] @@ -3191,7 +5166,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway7_zigbee_################-battery_level', + 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-battery_level', 'unit_of_measurement': '%', }) # --- @@ -3208,10 +5183,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '36', + 'state': '89', }) # --- -# name: test_all_entities[sensor.model7_signal_strength-entry] +# name: test_all_entities[sensor.model7_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3225,8 +5200,8 @@ 'device_id': , 'disabled_by': None, 'domain': 'sensor', - 'entity_category': , - 'entity_id': 'sensor.model7_signal_strength', + 'entity_category': None, + 'entity_id': 'sensor.model7_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3236,31 +5211,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': 'gateway7_zigbee_################-zigbee_signal_strength', + 'translation_key': None, + 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-room_humidity', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model7_signal_strength-state] +# name: test_all_entities[sensor.model7_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model7 Signal strength', + 'device_class': 'humidity', + 'friendly_name': 'model7 Humidity', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model7_signal_strength', + 'entity_id': 'sensor.model7_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '90', + 'state': '53', }) # --- # name: test_all_entities[sensor.model7_temperature-entry] @@ -3299,7 +5275,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': 'gateway7_zigbee_################-room_temperature', + 'unique_id': 'gateway7_zigbee_d87a3bfffe5d844a-room_temperature', 'unit_of_measurement': , }) # --- @@ -3316,10 +5292,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '21.5', + 'state': '17.5', }) # --- -# name: test_all_entities[sensor.model7_valve_position-entry] +# name: test_all_entities[sensor.model8_humidity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3334,7 +5310,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model7_valve_position', + 'entity_id': 'sensor.model8_humidity', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3344,34 +5320,91 @@ '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': 'gateway7_zigbee_################-valve_position', + 'translation_key': None, + 'unique_id': 'gateway8_zigbee_5cc7c1fffea33a3b-room_humidity', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model7_valve_position-state] +# name: test_all_entities[sensor.model8_humidity-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model7 Valve position', + 'device_class': 'humidity', + 'friendly_name': 'model8 Humidity', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model7_valve_position', + 'entity_id': 'sensor.model8_humidity', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '2', + 'state': '52', + }) +# --- +# name: test_all_entities[sensor.model8_temperature-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': None, + 'entity_id': 'sensor.model8_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Temperature', + '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': , + }) +# --- +# name: test_all_entities[sensor.model8_temperature-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'temperature', + 'friendly_name': 'model8 Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.model8_temperature', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '16.9', }) # --- -# name: test_all_entities[sensor.model8_signal_strength-entry] +# name: test_all_entities[sensor.model9_battery-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3386,7 +5419,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': , - 'entity_id': 'sensor.model8_signal_strength', + 'entity_id': 'sensor.model9_battery', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3396,31 +5429,32 @@ 'name': None, 'options': dict({ }), - 'original_device_class': None, + 'original_device_class': , 'original_icon': None, - 'original_name': 'Signal strength', + 'original_name': 'Battery', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'zigbee_signal_strength', - 'unique_id': 'gateway8_zigbee_################-zigbee_signal_strength', + 'translation_key': None, + 'unique_id': 'gateway9_zigbee_################-battery_level', 'unit_of_measurement': '%', }) # --- -# name: test_all_entities[sensor.model8_signal_strength-state] +# name: test_all_entities[sensor.model9_battery-state] StateSnapshot({ 'attributes': ReadOnlyDict({ - 'friendly_name': 'model8 Signal strength', + 'device_class': 'battery', + 'friendly_name': 'model9 Battery', 'state_class': , 'unit_of_measurement': '%', }), 'context': , - 'entity_id': 'sensor.model8_signal_strength', + 'entity_id': 'sensor.model9_battery', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '46', + 'state': '36', }) # --- # name: test_all_entities[sensor.model9_signal_strength-entry] @@ -3472,10 +5506,10 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '37', + 'state': '90', }) # --- -# name: test_all_entities[sensor.model9_supply_temperature-entry] +# name: test_all_entities[sensor.model9_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3490,7 +5524,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model9_supply_temperature', + 'entity_id': 'sensor.model9_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3505,30 +5539,82 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Supply temperature', + 'original_name': 'Temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'supply_temperature', - 'unique_id': 'gateway9_zigbee_################-supply_temperature', + 'translation_key': None, + 'unique_id': 'gateway9_zigbee_################-room_temperature', 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model9_supply_temperature-state] +# name: test_all_entities[sensor.model9_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'temperature', - 'friendly_name': 'model9 Supply temperature', + 'friendly_name': 'model9 Temperature', 'state_class': , 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model9_supply_temperature', + 'entity_id': 'sensor.model9_temperature', 'last_changed': , 'last_reported': , 'last_updated': , - 'state': '31.0', + 'state': '21.5', + }) +# --- +# name: test_all_entities[sensor.model9_valve_position-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': None, + 'entity_id': 'sensor.model9_valve_position', + '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': 'Valve position', + 'platform': 'vicare', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'valve_position', + 'unique_id': 'gateway9_zigbee_################-valve_position', + 'unit_of_measurement': '%', + }) +# --- +# name: test_all_entities[sensor.model9_valve_position-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'model9 Valve position', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.model9_valve_position', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2', }) # --- # name: test_all_entities[sensor.vitovalor_hydraulic_separator_temperature-entry] @@ -3567,7 +5653,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'hydraulic_separator_temperature', - 'unique_id': 'gateway4_deviceId4-hydraulic_separator_temperature', + 'unique_id': 'gateway6_deviceId6-hydraulic_separator_temperature', 'unit_of_measurement': , }) # --- diff --git a/tests/components/vicare/test_sensor.py b/tests/components/vicare/test_sensor.py index fb6f8e207ed26..285bb91facead 100644 --- a/tests/components/vicare/test_sensor.py +++ b/tests/components/vicare/test_sensor.py @@ -26,7 +26,9 @@ async def test_all_entities( fixtures: list[Fixture] = [ Fixture({"type:boiler"}, "vicare/Vitodens300W.json"), Fixture({"type:heatpump"}, "vicare/Vitocal250A.json"), + Fixture({"type:heatpump"}, "vicare/Vitocal222G_Vitovent300W.json"), Fixture({"type:ventilation"}, "vicare/ViAir300F.json"), + Fixture({"type:ventilation"}, "vicare/VitoPure.json"), Fixture({"type:ess"}, "vicare/VitoChargeVX3.json"), Fixture({None}, "vicare/VitoValor.json"), Fixture({"type:climateSensor"}, "vicare/RoomSensor1.json"), diff --git a/tests/components/xbox/conftest.py b/tests/components/xbox/conftest.py index b4d210d2b2604..a9ad38a2d883d 100644 --- a/tests/components/xbox/conftest.py +++ b/tests/components/xbox/conftest.py @@ -34,22 +34,10 @@ async def setup_credentials(hass: HomeAssistant) -> None: """Fixture to setup credentials.""" assert await async_setup_component(hass, "application_credentials", {}) await async_import_client_credential( - hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred" + hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "cloud" ) -@pytest.fixture(autouse=True) -def mock_oauth2_implementation() -> Generator[AsyncMock]: - """Mock config entry oauth2 implementation.""" - with patch( - "homeassistant.components.xbox.coordinator.async_get_config_entry_implementation", - return_value=AsyncMock(), - ) as mock_client: - client = mock_client.return_value - - yield client - - @pytest.fixture(name="config_entry") def mock_config_entry() -> MockConfigEntry: """Mock Xbox configuration entry.""" diff --git a/tests/components/xbox/snapshots/test_sensor.ambr b/tests/components/xbox/snapshots/test_sensor.ambr index b8422dfdfa66f..0d68963b42f1d 100644 --- a/tests/components/xbox/snapshots/test_sensor.ambr +++ b/tests/components/xbox/snapshots/test_sensor.ambr @@ -1371,3 +1371,357 @@ 'state': 'Offline', }) # --- +# name: test_sensors[sensor.xone_free_space_external-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.xone_free_space_external', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Free space - External', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'HIJKLMN_3_free_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xone_free_space_external-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONE Free space - External', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xone_free_space_external', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2980.89726638794', + }) +# --- +# name: test_sensors[sensor.xone_free_space_internal-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.xone_free_space_internal', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Free space - Internal', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'HIJKLMN_2_free_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xone_free_space_internal-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONE Free space - Internal', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xone_free_space_internal', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '137.056728363037', + }) +# --- +# name: test_sensors[sensor.xone_total_space_external-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.xone_total_space_external', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total space - External', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'HIJKLMN_3_total_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xone_total_space_external-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONE Total space - External', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xone_total_space_external', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '3726.02327680588', + }) +# --- +# name: test_sensors[sensor.xone_total_space_internal-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.xone_total_space_internal', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total space - Internal', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'HIJKLMN_2_total_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xone_total_space_internal-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONE Total space - Internal', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xone_total_space_internal', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '364.999996185303', + }) +# --- +# name: test_sensors[sensor.xonex_free_space_internal-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.xonex_free_space_internal', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Free space - Internal', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'ABCDEFG_1_free_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xonex_free_space_internal-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONEX Free space - Internal', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xonex_free_space_internal', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '220.041568756104', + }) +# --- +# name: test_sensors[sensor.xonex_total_space_internal-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.xonex_total_space_internal', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Total space - Internal', + 'platform': 'xbox', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'ABCDEFG_1_total_storage', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[sensor.xonex_total_space_internal-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'XONEX Total space - Internal', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.xonex_total_space_internal', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '780.999996185303', + }) +# --- diff --git a/tests/helpers/template/extensions/test_base.py b/tests/helpers/template/extensions/test_base.py index ce2bf966e6b99..5c35998d7b3bc 100644 --- a/tests/helpers/template/extensions/test_base.py +++ b/tests/helpers/template/extensions/test_base.py @@ -4,6 +4,7 @@ import pytest +from homeassistant.exceptions import TemplateError from homeassistant.helpers.template import TemplateEnvironment from homeassistant.helpers.template.extensions.base import ( BaseTemplateExtension, @@ -86,7 +87,7 @@ def test_func() -> str: def test_limited_ok_functions_not_registered_in_limited_env() -> None: - """Test that functions with limited_ok=False are not registered in limited env.""" + """Test that functions with limited_ok=False raise error in limited env.""" # Create a limited environment without hass env = TemplateEnvironment(None, limited=True) @@ -116,9 +117,18 @@ def restricted_func() -> str: ], ) - # Only the allowed function should be registered + # The allowed function should be registered and work assert "allowed_func" in env.globals - assert "restricted_func" not in env.globals + assert env.globals["allowed_func"]() == "allowed" + + # The restricted function should be registered but raise TemplateError + assert "restricted_func" in env.globals + with pytest.raises( + TemplateError, + match="Use of 'restricted_func' is not supported in limited templates", + ): + env.globals["restricted_func"]() + assert extension is not None diff --git a/tests/test_data_entry_flow.py b/tests/test_data_entry_flow.py index 55ff79e253109..b6dc2b39c7c0f 100644 --- a/tests/test_data_entry_flow.py +++ b/tests/test_data_entry_flow.py @@ -1,9 +1,11 @@ """Test the flow classes.""" import asyncio +from collections.abc import Callable import dataclasses import logging -from unittest.mock import Mock, patch +from typing import Any +from unittest.mock import AsyncMock, Mock, patch import pytest import voluptuous as vol @@ -930,6 +932,261 @@ async def test_change( ) # change (description placeholder) +@pytest.mark.parametrize( + ("task_side_effect", "flow_result"), + [ + (None, data_entry_flow.FlowResultType.CREATE_ENTRY), + (data_entry_flow.AbortFlow("fail"), data_entry_flow.FlowResultType.ABORT), + ], +) +@pytest.mark.parametrize( + ("description", "expected_description"), + [ + (None, None), + ({"title": "World"}, {"title": "World"}), + (lambda x: {"title": "World"}, {"title": "World"}), + ], +) +async def test_progress_step( + hass: HomeAssistant, + manager: MockFlowManager, + description: Callable[[data_entry_flow.FlowHandler], dict[str, Any]] + | dict[str, Any] + | None, + expected_description: dict[str, Any] | None, + task_side_effect: Exception | None, + flow_result: data_entry_flow.FlowResultType, +) -> None: + """Test progress_step decorator.""" + manager.hass = hass + events = [] + task_init_evt = asyncio.Event() + event_received_evt = asyncio.Event() + task_result = Mock() + task_result.side_effect = task_side_effect + + @callback + def capture_events(event: Event) -> None: + events.append(event) + event_received_evt.set() + + @manager.mock_reg_handler("test") + class TestFlow(data_entry_flow.FlowHandler): + VERSION = 5 + + @data_entry_flow.progress_step(description_placeholders=description) + async def async_step_init(self, user_input=None): + await task_init_evt.wait() + task_result() + + return await self.async_step_finish() + + async def async_step_finish(self, user_input=None): + return self.async_create_entry(data={}) + + hass.bus.async_listen( + data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESSED, + capture_events, + ) + + result = await manager.async_init("test") + assert result["type"] == data_entry_flow.FlowResultType.SHOW_PROGRESS + assert result["progress_action"] == "init" + description_placeholders = result["description_placeholders"] + assert description_placeholders == expected_description + assert len(manager.async_progress()) == 1 + assert len(manager.async_progress_by_handler("test")) == 1 + assert manager.async_get(result["flow_id"])["handler"] == "test" + + # Set task one done and wait for event + task_init_evt.set() + await event_received_evt.wait() + event_received_evt.clear() + assert len(events) == 1 + assert events[0].data == { + "handler": "test", + "flow_id": result["flow_id"], + "refresh": True, + } + + # Frontend refreshes the flow + result = await manager.async_configure(result["flow_id"]) + assert result["type"] == flow_result + + +@pytest.mark.parametrize( + ( + "task_init_side_effect", # side effect for initial step task + "task_next_side_effect", # side effect for next step task + "flow_result_before_init", # result before init task is done + "flow_result_after_init", # result after init task is done + "flow_result_after_next", # result after next task is done + "flow_init_events", # number of events fired after init task is done + "flow_next_events", # number of events fired after next task is done + "manager_call_after_init", # lambda to continue the flow after init task + "manager_call_after_next", # lambda to continue the flow after next task + "before_init_task_side_effect", # function called before init event + "before_next_task_side_effect", # function called before next event + ), + [ + ( # both steps show progress and complete successfully + None, + None, + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.CREATE_ENTRY, + 1, + 2, + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda received_event, init_task_event, next_task_event: None, + lambda received_event, init_task_event, next_task_event: None, + ), + ( # first step aborts + data_entry_flow.AbortFlow("fail"), + None, + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.ABORT, + data_entry_flow.FlowResultType.ABORT, + 1, + 1, + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda manager, result: AsyncMock(return_value=result)(), + lambda received_event, init_task_event, next_task_event: None, + lambda received_event, init_task_event, next_task_event: None, + ), + ( # first step shows progress, second step aborts + None, + data_entry_flow.AbortFlow("fail"), + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.ABORT, + 1, + 2, + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda received_event, init_task_event, next_task_event: None, + lambda received_event, init_task_event, next_task_event: None, + ), + ( # first step task is already done, second step shows progress and completes + None, + None, + data_entry_flow.FlowResultType.SHOW_PROGRESS_DONE, + data_entry_flow.FlowResultType.SHOW_PROGRESS, + data_entry_flow.FlowResultType.CREATE_ENTRY, + 0, + 1, + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda manager, result: manager.async_configure(result["flow_id"]), + lambda received_event, + init_task_event, + next_task_event: received_event.set() or init_task_event.set(), + lambda received_event, init_task_event, next_task_event: None, + ), + ], +) +async def test_chaining_progress_steps( + hass: HomeAssistant, + manager: MockFlowManager, + task_init_side_effect: Exception | None, + task_next_side_effect: Exception | None, + flow_result_before_init: data_entry_flow.FlowResultType, + flow_result_after_init: data_entry_flow.FlowResultType, + flow_result_after_next: data_entry_flow.FlowResultType, + flow_init_events: int, + flow_next_events: int, + manager_call_after_init: Callable[ + [MockFlowManager, data_entry_flow.FlowResult], Any + ], + manager_call_after_next: Callable[ + [MockFlowManager, data_entry_flow.FlowResult], Any + ], + before_init_task_side_effect: Callable[ + [asyncio.Event, asyncio.Event, asyncio.Event], None + ], + before_next_task_side_effect: Callable[ + [asyncio.Event, asyncio.Event, asyncio.Event], None + ], +) -> None: + """Test chaining two steps with progress_step decorators.""" + manager.hass = hass + events = [] + event_received_evt = asyncio.Event() + task_init_evt = asyncio.Event() + task_next_evt = asyncio.Event() + task_init_result = Mock() + task_init_result.side_effect = task_init_side_effect + task_next_result = Mock() + task_next_result.side_effect = task_next_side_effect + + @callback + def capture_events(event: Event) -> None: + events.append(event) + event_received_evt.set() + + @manager.mock_reg_handler("test") + class TestFlow(data_entry_flow.FlowHandler): + VERSION = 5 + + def async_remove(self) -> None: + # Disable event received event to allow test to finish if flow is aborted. + event_received_evt.set() + + @data_entry_flow.progress_step() + async def async_step_init(self, user_input=None): + await task_init_evt.wait() + task_init_result() + + return await self.async_step_next() + + @data_entry_flow.progress_step() + async def async_step_next(self, user_input=None): + await task_next_evt.wait() + task_next_result() + + return await self.async_step_finish() + + async def async_step_finish(self, user_input=None): + return self.async_create_entry(data={}) + + hass.bus.async_listen( + data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESSED, + capture_events, + ) + + # Run side effect before first event is awaited + before_init_task_side_effect(event_received_evt, task_init_evt, task_next_evt) + + result = await manager.async_init("test") + assert result["type"] == flow_result_before_init + assert len(manager.async_progress()) == 1 + assert len(manager.async_progress_by_handler("test")) == 1 + assert manager.async_get(result["flow_id"])["handler"] == "test" + + # Set task init done and wait for event + task_init_evt.set() + await event_received_evt.wait() + event_received_evt.clear() + assert len(events) == flow_init_events + + # Run side effect before second event is awaited + before_next_task_side_effect(event_received_evt, task_init_evt, task_next_evt) + + # Continue the flow if needed. + result = await manager_call_after_init(manager, result) + assert result["type"] == flow_result_after_init + + # Set task next done and wait for event + task_next_evt.set() + await event_received_evt.wait() + event_received_evt.clear() + assert len(events) == flow_next_events + + # Continue the flow if needed. + result = await manager_call_after_next(manager, result) + assert result["type"] == flow_result_after_next + + async def test_abort_flow_exception_step(manager: MockFlowManager) -> None: """Test that the AbortFlow exception works in a step."""