diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index a9c046e25bfb6632a0caa0c9faf779c5a275182e..eab7ba4f09e30294a377b6d62ca38236fd39f0be 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -8,6 +8,9 @@ from typing import Any from homematicip.aio.device import ( AsyncBrandSwitchMeasuring, AsyncEnergySensorsInterface, + AsyncFloorTerminalBlock6, + AsyncFloorTerminalBlock10, + AsyncFloorTerminalBlock12, AsyncFullFlushSwitchMeasuring, AsyncHeatingThermostat, AsyncHeatingThermostatCompact, @@ -28,9 +31,13 @@ from homematicip.aio.device import ( AsyncWeatherSensor, AsyncWeatherSensorPlus, AsyncWeatherSensorPro, + AsyncWiredFloorTerminalBlock12, ) from homematicip.base.enums import FunctionalChannelType, ValveState -from homematicip.base.functionalChannels import FunctionalChannel +from homematicip.base.functionalChannels import ( + FloorTerminalBlockMechanicChannel, + FunctionalChannel, +) from homeassistant.components.sensor import ( SensorDeviceClass, @@ -86,7 +93,7 @@ ILLUMINATION_DEVICE_ATTRIBUTES = { } -async def async_setup_entry( +async def async_setup_entry( # noqa: C901 hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -184,10 +191,74 @@ async def async_setup_entry( if ch.currentPowerConsumption is not None: entities.append(HmipEsiLedCurrentPowerConsumption(hap, device)) entities.append(HmipEsiLedEnergyCounterHighTariff(hap, device)) + if isinstance( + device, + ( + AsyncFloorTerminalBlock6, + AsyncFloorTerminalBlock10, + AsyncFloorTerminalBlock12, + AsyncWiredFloorTerminalBlock12, + ), + ): + entities.extend( + HomematicipFloorTerminalBlockMechanicChannelValve( + hap, device, channel=channel.index + ) + for channel in device.functionalChannels + if isinstance(channel, FloorTerminalBlockMechanicChannel) + and getattr(channel, "valvePosition", None) is not None + ) async_add_entities(entities) +class HomematicipFloorTerminalBlockMechanicChannelValve( + HomematicipGenericEntity, SensorEntity +): + """Representation of the HomematicIP floor terminal block.""" + + _attr_native_unit_of_measurement = PERCENTAGE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__( + self, hap: HomematicipHAP, device, channel, is_multi_channel=True + ) -> None: + """Initialize floor terminal block 12 device.""" + super().__init__( + hap, + device, + channel=channel, + is_multi_channel=is_multi_channel, + post="Valve Position", + ) + + @property + def icon(self) -> str | None: + """Return the icon.""" + if super().icon: + return super().icon + channel = next( + channel + for channel in self._device.functionalChannels + if channel.index == self._channel + ) + if channel.valveState != ValveState.ADAPTION_DONE: + return "mdi:alert" + return "mdi:heating-coil" + + @property + def native_value(self) -> int | None: + """Return the state of the floor terminal block mechanical channel valve position.""" + channel = next( + channel + for channel in self._device.functionalChannels + if channel.index == self._channel + ) + if channel.valveState != ValveState.ADAPTION_DONE: + return None + return round(channel.valvePosition * 100) + + class HomematicipAccesspointDutyCycle(HomematicipGenericEntity, SensorEntity): """Representation of then HomeMaticIP access point.""" diff --git a/tests/components/homematicip_cloud/fixtures/homematicip_cloud.json b/tests/components/homematicip_cloud/fixtures/homematicip_cloud.json index 442fd16d2c7f65f50baab2ff51bb2f458e412df0..7a3d3f06b0956bdd468425c6415c7ba093b81b36 100644 --- a/tests/components/homematicip_cloud/fixtures/homematicip_cloud.json +++ b/tests/components/homematicip_cloud/fixtures/homematicip_cloud.json @@ -1805,93 +1805,164 @@ "updateState": "UP_TO_DATE" }, "3014F7110000000000000049": { - "availableFirmwareVersion": "1.0.8", + "availableFirmwareVersion": "1.4.8", "connectionType": "HMIP_RF", - "firmwareVersion": "1.0.8", - "firmwareVersionInteger": 65544, + "deviceArchetype": "HMIP", + "firmwareVersion": "1.4.8", + "firmwareVersionInteger": 66568, "functionalChannels": { "0": { + "busConfigMismatch": null, "coProFaulty": false, "coProRestartNeeded": false, "coProUpdateFailure": false, - "configPending": false, + "configPending": true, + "controlsMountingOrientation": null, "coolingEmergencyValue": 0.0, + "daliBusState": null, + "defaultLinkedGroup": [], + "deviceCommunicationError": null, + "deviceDriveError": null, + "deviceDriveModeError": null, "deviceId": "3014F7110000000000000049", + "deviceOperationMode": null, "deviceOverheated": false, "deviceOverloaded": false, + "devicePowerFailureDetected": false, "deviceUndervoltage": false, + "displayContrast": null, "dutyCycle": false, "frostProtectionTemperature": 8.0, "functionalChannelType": "DEVICE_BASE_FLOOR_HEATING", "groupIndex": 0, - "groups": [], - "heatingEmergencyValue": 0.25, + "groups": ["00000000-0000-0000-0000-000000000005"], + "heatingEmergencyValue": 0.05, "index": 0, "label": "", + "lockJammed": null, "lowBat": null, "minimumFloorHeatingValvePosition": 0.0, - "pulseWidthModulationAtLowFloorHeatingValvePositionEnabled": true, + "mountingOrientation": null, + "multicastRoutingEnabled": false, + "particulateMatterSensorCommunicationError": null, + "particulateMatterSensorError": null, + "powerShortCircuit": null, + "profilePeriodLimitReached": null, + "pulseWidthModulationAtLowFloorHeatingValvePositionEnabled": false, "routerModuleEnabled": false, "routerModuleSupported": false, - "rssiDeviceValue": -55, + "rssiDeviceValue": -83, "rssiPeerValue": null, + "sensorCommunicationError": null, + "sensorError": null, + "shortCircuitDataLine": null, "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, "IFeatureDeviceCoProError": false, "IFeatureDeviceCoProRestart": false, "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceCommunicationError": false, + "IFeatureDeviceDaliBusError": false, + "IFeatureDeviceDriveError": false, + "IFeatureDeviceDriveModeError": false, + "IFeatureDeviceIdentify": false, "IFeatureDeviceOverheated": false, "IFeatureDeviceOverloaded": false, + "IFeatureDeviceParticulateMatterSensorCommunicationError": false, + "IFeatureDeviceParticulateMatterSensorError": false, + "IFeatureDevicePowerFailure": false, + "IFeatureDeviceSensorCommunicationError": false, + "IFeatureDeviceSensorError": false, + "IFeatureDeviceTemperatureHumiditySensorCommunicationError": false, + "IFeatureDeviceTemperatureHumiditySensorError": false, "IFeatureDeviceTemperatureOutOfRange": false, "IFeatureDeviceUndervoltage": false, "IFeatureMinimumFloorHeatingValvePosition": true, - "IFeaturePulseWidthModulationAtLowFloorHeatingValvePosition": true + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureProfilePeriodLimit": false, + "IFeaturePulseWidthModulationAtLowFloorHeatingValvePosition": true, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDefaultLinkedGroup": false, + "IOptionalFeatureDeviceErrorLockJammed": false, + "IOptionalFeatureDeviceOperationMode": false, + "IOptionalFeatureDisplayContrast": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": false, + "IOptionalFeatureMountingOrientation": false }, + "temperatureHumiditySensorCommunicationError": null, + "temperatureHumiditySensorError": null, "temperatureOutOfRange": false, "unreach": false, "valveProtectionDuration": 5, "valveProtectionSwitchingInterval": 14 }, "1": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 1, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000022", + "00000000-0000-0000-0000-000000000023" + ], "index": 1, - "label": "", + "label": "Heizkreislauf (1) OG Bad r", + "valvePosition": 0.475, "valveState": "ADAPTION_DONE" }, "10": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 10, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000030", + "00000000-0000-0000-0000-000000000031" + ], "index": 10, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (10) OG AZ rechts", + "valvePosition": 0.385, + "valveState": "ADAPTION_DONE" }, "11": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 11, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000030", + "00000000-0000-0000-0000-000000000031" + ], "index": 11, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (11) OG AZ links", + "valvePosition": 0.385, + "valveState": "ADAPTION_DONE" }, "12": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 12, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000022", + "00000000-0000-0000-0000-000000000023" + ], "index": 12, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (12) OG Bad Heizk\u00f6rper", + "valvePosition": 0.385, + "valveState": "ADAPTION_DONE" }, "13": { "deviceId": "3014F7110000000000000049", "functionalChannelType": "HEAT_DEMAND_CHANNEL", "groupIndex": 0, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000058", + "00000000-0000-0000-0000-000000000059" + ], "index": 13, "label": "" }, @@ -1899,7 +1970,7 @@ "deviceId": "3014F7110000000000000049", "functionalChannelType": "DEHUMIDIFIER_DEMAND_CHANNEL", "groupIndex": 0, - "groups": [], + "groups": ["00000000-0000-0000-0000-000000000060"], "index": 14, "label": "" }, @@ -1907,89 +1978,136 @@ "deviceId": "3014F7110000000000000049", "functionalChannelType": "CHANGE_OVER_CHANNEL", "groupIndex": 0, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000061", + "00000000-0000-0000-0000-000000000062", + "00000000-0000-0000-0000-000000000063", + "00000000-0000-0000-0000-000000000064" + ], "index": 15, "label": "" }, "2": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 2, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000022", + "00000000-0000-0000-0000-000000000023" + ], "index": 2, - "label": "", + "label": "Heizkreislauf (2) OG Bad l", + "valvePosition": 0.385, "valveState": "ADAPTION_DONE" }, "3": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 3, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000017", + "00000000-0000-0000-0000-000000000018" + ], "index": 3, - "label": "", + "label": "Heizkreislauf (3) OG WZ rechts", + "valvePosition": 0.0, "valveState": "ADAPTION_DONE" }, "4": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 4, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000017", + "00000000-0000-0000-0000-000000000018" + ], "index": 4, - "label": "", + "label": "Heizkreislauf (4) OG WZ Mitte rechts", + "valvePosition": 0.0, "valveState": "ADAPTION_DONE" }, "5": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 5, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000017", + "00000000-0000-0000-0000-000000000018" + ], "index": 5, - "label": "", + "label": "Heizkreislauf (5) OG WZ Mitte links", + "valvePosition": 0.0, "valveState": "ADAPTION_DONE" }, "6": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 6, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000017", + "00000000-0000-0000-0000-000000000018" + ], "index": 6, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (6) OG WZ links", + "valvePosition": 0.0, + "valveState": "ADAPTION_DONE" }, "7": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 7, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000017", + "00000000-0000-0000-0000-000000000018" + ], "index": 7, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (7) OG K\u00fcche", + "valvePosition": 0.0, + "valveState": "ADAPTION_DONE" }, "8": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 8, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000026", + "00000000-0000-0000-0000-000000000027" + ], "index": 8, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (8) OG SZ rechts", + "valvePosition": 0.0, + "valveState": "ADAPTION_DONE" }, "9": { + "channelRole": "FLOOR_HEATING_COOLING_CONTROLLER", "deviceId": "3014F7110000000000000049", "functionalChannelType": "FLOOR_TERMINAL_BLOCK_MECHANIC_CHANNEL", "groupIndex": 9, - "groups": [], + "groups": [ + "00000000-0000-0000-0000-000000000026", + "00000000-0000-0000-0000-000000000027" + ], "index": 9, - "label": "", - "valveState": "ADJUSTMENT_TOO_SMALL" + "label": "Heizkreislauf (9) OG SZ links", + "valvePosition": 0.0, + "valveState": "ADAPTION_DONE" } }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000049", - "label": "Fu\u00dfbodenheizungsaktor OG motorisch", - "lastStatusUpdate": 1577486092047, + "label": "Fu\u00dfbodenheizungsaktor", + "lastStatusUpdate": 1704379652281, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manuallyUpdateForced": false, "manufacturerCode": 1, + "measuredAttributes": {}, "modelId": 365, "modelType": "HmIP-FALMOT-C12", "oem": "eQ-3", diff --git a/tests/components/homematicip_cloud/test_device.py b/tests/components/homematicip_cloud/test_device.py index d5f8d0f25c46d2970b94bf1c9b528de10e7a2504..5b4993f7314ac75602f1480024bb46dc5a3755d4 100644 --- a/tests/components/homematicip_cloud/test_device.py +++ b/tests/components/homematicip_cloud/test_device.py @@ -28,7 +28,7 @@ async def test_hmip_load_all_supported_devices( test_devices=None, test_groups=None ) - assert len(mock_hap.hmip_device_by_entity_id) == 296 + assert len(mock_hap.hmip_device_by_entity_id) == 308 async def test_hmip_remove_device( diff --git a/tests/components/homematicip_cloud/test_sensor.py b/tests/components/homematicip_cloud/test_sensor.py index 07cf5ea0ae5b7cd88cdda758a49d104d16689d54..bdd0b6194ed7bfcbaa8f0214e9de1e91b3a93a59 100644 --- a/tests/components/homematicip_cloud/test_sensor.py +++ b/tests/components/homematicip_cloud/test_sensor.py @@ -12,6 +12,7 @@ from homeassistant.components.homematicip_cloud.entity import ( ATTR_RSSI_DEVICE, ATTR_RSSI_PEER, ) +from homeassistant.components.homematicip_cloud.hap import HomematicipHAP from homeassistant.components.homematicip_cloud.sensor import ( ATTR_CURRENT_ILLUMINATION, ATTR_HIGHEST_ILLUMINATION, @@ -515,6 +516,47 @@ async def test_hmip_passage_detector_delta_counter( assert ha_state.state == "190" +async def test_hmip_floor_terminal_block_mechanic_channel_1_valve_position( + hass: HomeAssistant, default_mock_hap_factory: HomematicipHAP +) -> None: + """Test HomematicipFloorTerminalBlockMechanicChannelValve Channel 1 HmIP-FALMOT-C12.""" + entity_id = "sensor.heizkreislauf_1_og_bad_r" + entity_name = "Heizkreislauf (1) OG Bad r" + device_model = "HmIP-FALMOT-C12" + + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["Fu\u00dfbodenheizungsaktor"] + ) + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + hmip_device = mock_hap.hmip_device_by_entity_id.get(entity_id) + + assert ha_state.state == "48" + assert ha_state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + await async_manipulate_test_data(hass, hmip_device, "valvePosition", 0.36) + ha_state = hass.states.get(entity_id) + assert ha_state.state == "36" + + await async_manipulate_test_data(hass, hmip_device, "configPending", True) + ha_state = hass.states.get(entity_id) + assert ha_state.attributes["icon"] == "mdi:alert-circle" + + await async_manipulate_test_data(hass, hmip_device, "configPending", False) + await async_manipulate_test_data( + hass, hmip_device, "valveState", ValveState.ADAPTION_IN_PROGRESS + ) + ha_state = hass.states.get(entity_id) + assert ha_state.attributes["icon"] == "mdi:alert" + + await async_manipulate_test_data( + hass, hmip_device, "valveState", ValveState.ADAPTION_DONE + ) + ha_state = hass.states.get(entity_id) + assert ha_state.attributes["icon"] == "mdi:heating-coil" + + async def test_hmip_esi_iec_current_power_consumption( hass: HomeAssistant, default_mock_hap_factory: HomeFactory ) -> None: