Show default profiles in homematic cloud climate entity (#107348)

* Default names for visible profiles

* Increase number of devices in test

* remove unnecessary check

* Add testcase and split another into two

* Add type annotations and docstring

* Remove code which not belongs to the PR

* Add myself to codeowners
This commit is contained in:
hahn-th 2024-04-19 08:09:48 +02:00 committed by GitHub
parent 4cce75177a
commit ed4f00279e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 462 additions and 16 deletions

View File

@ -599,6 +599,8 @@ build.json @home-assistant/supervisor
/tests/components/homekit_controller/ @Jc2k @bdraco /tests/components/homekit_controller/ @Jc2k @bdraco
/homeassistant/components/homematic/ @pvizeli /homeassistant/components/homematic/ @pvizeli
/tests/components/homematic/ @pvizeli /tests/components/homematic/ @pvizeli
/homeassistant/components/homematicip_cloud/ @hahn-th
/tests/components/homematicip_cloud/ @hahn-th
/homeassistant/components/homewizard/ @DCSBL /homeassistant/components/homewizard/ @DCSBL
/tests/components/homewizard/ @DCSBL /tests/components/homewizard/ @DCSBL
/homeassistant/components/honeywell/ @rdfurman @mkmer /homeassistant/components/honeywell/ @rdfurman @mkmer

View File

@ -13,6 +13,7 @@ from homematicip.aio.group import AsyncHeatingGroup
from homematicip.base.enums import AbsenceType from homematicip.base.enums import AbsenceType
from homematicip.device import Switch from homematicip.device import Switch
from homematicip.functionalHomes import IndoorClimateHome from homematicip.functionalHomes import IndoorClimateHome
from homematicip.group import HeatingCoolingProfile
from homeassistant.components.climate import ( from homeassistant.components.climate import (
PRESET_AWAY, PRESET_AWAY,
@ -35,6 +36,14 @@ from .hap import HomematicipHAP
HEATING_PROFILES = {"PROFILE_1": 0, "PROFILE_2": 1, "PROFILE_3": 2} HEATING_PROFILES = {"PROFILE_1": 0, "PROFILE_2": 1, "PROFILE_3": 2}
COOLING_PROFILES = {"PROFILE_4": 3, "PROFILE_5": 4, "PROFILE_6": 5} COOLING_PROFILES = {"PROFILE_4": 3, "PROFILE_5": 4, "PROFILE_6": 5}
NICE_PROFILE_NAMES = {
"PROFILE_1": "Default",
"PROFILE_2": "Alternative 1",
"PROFILE_3": "Alternative 2",
"PROFILE_4": "Cooling 1",
"PROFILE_5": "Cooling 2",
"PROFILE_6": "Cooling 3",
}
ATTR_PRESET_END_TIME = "preset_end_time" ATTR_PRESET_END_TIME = "preset_end_time"
PERMANENT_END_TIME = "permanent" PERMANENT_END_TIME = "permanent"
@ -164,8 +173,9 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
return PRESET_ECO return PRESET_ECO
return ( return (
self._device.activeProfile.name self._get_qualified_profile_name(self._device.activeProfile)
if self._device.activeProfile.name in self._device_profile_names if self._get_qualified_profile_name(self._device.activeProfile)
in self._device_profile_names
else None else None
) )
@ -218,9 +228,6 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
async def async_set_preset_mode(self, preset_mode: str) -> None: async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode.""" """Set new preset mode."""
if preset_mode not in self.preset_modes:
return
if self._device.boostMode and preset_mode != PRESET_BOOST: if self._device.boostMode and preset_mode != PRESET_BOOST:
await self._device.set_boost(False) await self._device.set_boost(False)
if preset_mode == PRESET_BOOST: if preset_mode == PRESET_BOOST:
@ -256,20 +263,30 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
return self._home.get_functionalHome(IndoorClimateHome) return self._home.get_functionalHome(IndoorClimateHome)
@property @property
def _device_profiles(self) -> list[Any]: def _device_profiles(self) -> list[HeatingCoolingProfile]:
"""Return the relevant profiles.""" """Return the relevant profiles."""
return [ return [
profile profile
for profile in self._device.profiles for profile in self._device.profiles
if profile.visible if profile.visible and profile.index in self._relevant_profile_group
and profile.name != ""
and profile.index in self._relevant_profile_group
] ]
@property @property
def _device_profile_names(self) -> list[str]: def _device_profile_names(self) -> list[str]:
"""Return a collection of profile names.""" """Return a collection of profile names."""
return [profile.name for profile in self._device_profiles] return [
self._get_qualified_profile_name(profile)
for profile in self._device_profiles
]
def _get_qualified_profile_name(self, profile: HeatingCoolingProfile) -> str:
"""Get a name for the given profile. If exists, this is the name of the profile."""
if profile.name != "":
return profile.name
if profile.index in NICE_PROFILE_NAMES:
return NICE_PROFILE_NAMES[profile.index]
return profile.index
def _get_profile_idx_by_name(self, profile_name: str) -> int: def _get_profile_idx_by_name(self, profile_name: str) -> int:
"""Return a profile index by name.""" """Return a profile index by name."""
@ -277,7 +294,7 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity):
index_name = [ index_name = [
profile.index profile.index
for profile in self._device_profiles for profile in self._device_profiles
if profile.name == profile_name if self._get_qualified_profile_name(profile) == profile_name
] ]
return relevant_index[index_name[0]] return relevant_index[index_name[0]]

View File

@ -1,7 +1,7 @@
{ {
"domain": "homematicip_cloud", "domain": "homematicip_cloud",
"name": "HomematicIP Cloud", "name": "HomematicIP Cloud",
"codeowners": [], "codeowners": ["@hahn-th"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud",
"iot_class": "cloud_push", "iot_class": "cloud_push",

View File

@ -4791,6 +4791,59 @@
"type": "HEATING_THERMOSTAT", "type": "HEATING_THERMOSTAT",
"updateState": "UP_TO_DATE" "updateState": "UP_TO_DATE"
}, },
"3014F71100000000ETRV0013": {
"automaticValveAdaptionNeeded": false,
"availableFirmwareVersion": "2.0.2",
"connectionType": "HMIP_RF",
"firmwareVersion": "2.0.2",
"firmwareVersionInteger": 131074,
"functionalChannels": {
"0": {
"configPending": false,
"deviceId": "3014F71100000000ETRV0013",
"dutyCycle": false,
"functionalChannelType": "DEVICE_OPERATIONLOCK",
"groupIndex": 0,
"groups": ["00000000-0000-0000-0000-000000000014"],
"index": 0,
"label": "",
"lowBat": false,
"operationLockActive": false,
"routerModuleEnabled": false,
"routerModuleSupported": false,
"rssiDeviceValue": -58,
"rssiPeerValue": -58,
"unreach": false,
"supportedOptionalFeatures": {}
},
"1": {
"deviceId": "3014F71100000000ETRV0013",
"functionalChannelType": "HEATING_THERMOSTAT_CHANNEL",
"groupIndex": 1,
"groups": ["00000000-0000-0000-0005-000000000019"],
"index": 1,
"label": "",
"valveActualTemperature": 20.0,
"setPointTemperature": 5.0,
"temperatureOffset": 0.0,
"valvePosition": 0.0,
"valveState": "ADAPTION_DONE"
}
},
"homeId": "00000000-0000-0000-0000-000000000001",
"id": "3014F71100000000ETRV0013",
"label": "Heizkörperthermostat4",
"lastStatusUpdate": 1524514007132,
"liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED",
"manufacturerCode": 1,
"modelId": 269,
"modelType": "HMIP-eTRV",
"oem": "eQ-3",
"permanentlyReachable": true,
"serializedGlobalTradeItemNumber": "3014F71100000000ETRV0013",
"type": "HEATING_THERMOSTAT",
"updateState": "UP_TO_DATE"
},
"3014F7110000000000000014": { "3014F7110000000000000014": {
"automaticValveAdaptionNeeded": false, "automaticValveAdaptionNeeded": false,
"availableFirmwareVersion": "2.0.2", "availableFirmwareVersion": "2.0.2",
@ -8535,6 +8588,297 @@
"windowOpenTemperature": 5.0, "windowOpenTemperature": 5.0,
"windowState": null "windowState": null
}, },
"00000000-0000-0000-0005-000000000019": {
"activeProfile": "PROFILE_1",
"actualTemperature": null,
"boostDuration": 15,
"boostMode": false,
"channels": [
{
"channelIndex": 1,
"deviceId": "3014F71100000000ETRV0013"
}
],
"controlMode": "AUTOMATIC",
"controllable": true,
"cooling": null,
"coolingAllowed": false,
"coolingIgnored": false,
"dutyCycle": false,
"ecoAllowed": true,
"ecoIgnored": false,
"externalClockCoolingTemperature": 23.0,
"externalClockEnabled": false,
"externalClockHeatingTemperature": 19.0,
"floorHeatingMode": "FLOOR_HEATING_STANDARD",
"homeId": "00000000-0000-0000-0000-000000000001",
"humidity": null,
"humidityLimitEnabled": true,
"humidityLimitValue": 60,
"id": "00000000-0000-0000-0005-000000000019",
"label": "Vorzimmer3",
"lastSetPointReachedTimestamp": 1557767559939,
"lastSetPointUpdatedTimestamp": 1557767559939,
"lastStatusUpdate": 1524514007132,
"lowBat": false,
"maxTemperature": 30.0,
"metaGroupId": "00000000-0000-0000-0000-000000000014",
"minTemperature": 5.0,
"partyMode": false,
"profiles": {
"PROFILE_1": {
"enabled": true,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_1",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000058",
"visible": true
},
"PROFILE_2": {
"enabled": true,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_2",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000059",
"visible": true
},
"PROFILE_3": {
"enabled": true,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_3",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000060",
"visible": false
},
"PROFILE_4": {
"enabled": false,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_4",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000061",
"visible": true
},
"PROFILE_5": {
"enabled": false,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_5",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000062",
"visible": false
},
"PROFILE_6": {
"enabled": false,
"groupId": "00000000-0000-0000-0005-000000000019",
"index": "PROFILE_6",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000063",
"visible": false
}
},
"setPointTemperature": 5.0,
"type": "HEATING",
"unreach": false,
"valvePosition": 0.0,
"valveSilentModeEnabled": false,
"valveSilentModeSupported": false,
"heatingFailureSupported": true,
"windowOpenTemperature": 5.0,
"windowState": null
},
"00000000-0000-0000-0001-000000000019": {
"activeProfile": "PROFILE_1",
"actualTemperature": null,
"boostDuration": 15,
"boostMode": false,
"channels": [
{
"channelIndex": 1,
"deviceId": "3014F7110000000000000013"
}
],
"controlMode": "AUTOMATIC",
"controllable": true,
"cooling": null,
"coolingAllowed": false,
"coolingIgnored": false,
"dutyCycle": false,
"ecoAllowed": true,
"ecoIgnored": false,
"externalClockCoolingTemperature": 23.0,
"externalClockEnabled": false,
"externalClockHeatingTemperature": 19.0,
"floorHeatingMode": "FLOOR_HEATING_STANDARD",
"homeId": "00000000-0000-0000-0000-000000000001",
"humidity": null,
"humidityLimitEnabled": true,
"humidityLimitValue": 60,
"id": "00000000-0000-0000-0001-000000000019",
"label": "Vorzimmer",
"lastSetPointReachedTimestamp": 1557767559939,
"lastSetPointUpdatedTimestamp": 1557767559939,
"lastStatusUpdate": 1524514007132,
"lowBat": false,
"maxTemperature": 30.0,
"metaGroupId": "00000000-0000-0000-0000-000000000014",
"minTemperature": 5.0,
"partyMode": false,
"profiles": {
"PROFILE_1": {
"enabled": true,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_1",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000058",
"visible": true
},
"PROFILE_2": {
"enabled": true,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_2",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000059",
"visible": false
},
"PROFILE_3": {
"enabled": true,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_3",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000060",
"visible": false
},
"PROFILE_4": {
"enabled": false,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_4",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000061",
"visible": true
},
"PROFILE_5": {
"enabled": false,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_5",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000062",
"visible": false
},
"PROFILE_6": {
"enabled": false,
"groupId": "00000000-0000-0000-0001-000000000019",
"index": "PROFILE_6",
"name": "",
"profileId": "00000000-0000-0000-0000-000000000063",
"visible": false
}
},
"setPointTemperature": 5.0,
"type": "HEATING",
"unreach": false,
"valvePosition": 0.0,
"valveSilentModeEnabled": false,
"valveSilentModeSupported": false,
"heatingFailureSupported": true,
"windowOpenTemperature": 5.0,
"windowState": null
},
"00000000-0000-0001-0001-000000000019": {
"activeProfile": "PROFILE_1",
"actualTemperature": null,
"boostDuration": 15,
"boostMode": false,
"channels": [
{
"channelIndex": 1,
"deviceId": "3014F7110000000000000013"
}
],
"controlMode": "AUTOMATIC",
"controllable": true,
"cooling": null,
"coolingAllowed": false,
"coolingIgnored": false,
"dutyCycle": false,
"ecoAllowed": true,
"ecoIgnored": false,
"externalClockCoolingTemperature": 23.0,
"externalClockEnabled": false,
"externalClockHeatingTemperature": 19.0,
"floorHeatingMode": "FLOOR_HEATING_STANDARD",
"homeId": "00000000-0000-0000-0000-000000000001",
"humidity": null,
"humidityLimitEnabled": true,
"humidityLimitValue": 60,
"id": "00000000-0000-0001-0001-000000000019",
"label": "Vorzimmer2",
"lastSetPointReachedTimestamp": 1557767559939,
"lastSetPointUpdatedTimestamp": 1557767559939,
"lastStatusUpdate": 1524514007132,
"lowBat": false,
"maxTemperature": 30.0,
"metaGroupId": "00000000-0000-0000-0000-000000000014",
"minTemperature": 5.0,
"partyMode": false,
"profiles": {
"PROFILE_1": {
"enabled": true,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_1",
"name": "Testprofile",
"profileId": "00000000-0000-0000-0001-000000000058",
"visible": true
},
"PROFILE_2": {
"enabled": true,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_2",
"name": "",
"profileId": "00000000-0000-0000-0001-000000000059",
"visible": true
},
"PROFILE_3": {
"enabled": true,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_3",
"name": "",
"profileId": "00000000-0000-0000-0001-000000000060",
"visible": false
},
"PROFILE_4": {
"enabled": false,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_4",
"name": "",
"profileId": "00000000-0000-0000-0001-000000000061",
"visible": true
},
"PROFILE_5": {
"enabled": false,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_5",
"name": "",
"profileId": "00000000-0000-0000-0001-000000000062",
"visible": false
},
"PROFILE_6": {
"enabled": false,
"groupId": "00000000-0000-0001-0001-000000000019",
"index": "PROFILE_6",
"name": "",
"profileId": "00000000-0000-0000-0001-000000000063",
"visible": false
}
},
"setPointTemperature": 5.0,
"type": "HEATING",
"unreach": false,
"valvePosition": 0.0,
"valveSilentModeEnabled": false,
"valveSilentModeSupported": false,
"heatingFailureSupported": true,
"windowOpenTemperature": 5.0,
"windowState": null
},
"00000000-AAAA-0000-0000-000000000001": { "00000000-AAAA-0000-0000-000000000001": {
"actualTemperature": 15.4, "actualTemperature": 15.4,
"channels": [ "channels": [

View File

@ -1,6 +1,7 @@
"""Tests for HomematicIP Cloud climate.""" """Tests for HomematicIP Cloud climate."""
import datetime import datetime
from unittest.mock import patch
from homematicip.base.enums import AbsenceType from homematicip.base.enums import AbsenceType
from homematicip.functionalHomes import IndoorClimateHome from homematicip.functionalHomes import IndoorClimateHome
@ -15,7 +16,6 @@ from homeassistant.components.climate import (
PRESET_AWAY, PRESET_AWAY,
PRESET_BOOST, PRESET_BOOST,
PRESET_ECO, PRESET_ECO,
PRESET_NONE,
HVACAction, HVACAction,
HVACMode, HVACMode,
) )
@ -217,12 +217,14 @@ async def test_hmip_heating_group_heat(
ha_state = hass.states.get(entity_id) ha_state = hass.states.get(entity_id)
assert ha_state.state == HVACMode.AUTO assert ha_state.state == HVACMode.AUTO
# hvac mode "dry" is not available. expect a valueerror.
await hass.services.async_call( await hass.services.async_call(
"climate", "climate",
"set_hvac_mode", "set_hvac_mode",
{"entity_id": entity_id, "hvac_mode": "dry"}, {"entity_id": entity_id, "hvac_mode": "dry"},
blocking=True, blocking=True,
) )
assert len(hmip_device.mock_calls) == service_call_counter + 24 assert len(hmip_device.mock_calls) == service_call_counter + 24
# Only fire event from last async_manipulate_test_data available. # Only fire event from last async_manipulate_test_data available.
assert hmip_device.mock_calls[-1][0] == "fire_update_event" assert hmip_device.mock_calls[-1][0] == "fire_update_event"
@ -429,14 +431,95 @@ async def test_hmip_heating_group_heat_with_radiator(
assert ha_state.attributes["min_temp"] == 5.0 assert ha_state.attributes["min_temp"] == 5.0
assert ha_state.attributes["max_temp"] == 30.0 assert ha_state.attributes["max_temp"] == 30.0
assert ha_state.attributes["temperature"] == 5.0 assert ha_state.attributes["temperature"] == 5.0
assert ha_state.attributes[ATTR_PRESET_MODE] is None assert ha_state.attributes[ATTR_PRESET_MODE] == "Default"
assert ha_state.attributes[ATTR_PRESET_MODES] == [ assert ha_state.attributes[ATTR_PRESET_MODES] == [
PRESET_NONE,
PRESET_BOOST, PRESET_BOOST,
PRESET_ECO, PRESET_ECO,
"Default",
] ]
async def test_hmip_heating_profile_default_name(
hass: HomeAssistant, default_mock_hap_factory
) -> None:
"""Test visible profile 1 without a name should be displayed as 'Default'."""
entity_id = "climate.vorzimmer3"
entity_name = "Vorzimmer3"
device_model = None
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
test_devices=["Heizkörperthermostat4"],
test_groups=[entity_name],
)
ha_state, hmip_device = get_and_check_entity_basics(
hass, mock_hap, entity_id, entity_name, device_model
)
assert hmip_device
assert ha_state.state == HVACMode.AUTO
assert ha_state.attributes[ATTR_PRESET_MODES] == [
PRESET_BOOST,
PRESET_ECO,
"Default",
"Alternative 1",
]
async def test_hmip_heating_profile_naming(
hass: HomeAssistant, default_mock_hap_factory
) -> None:
"""Test Heating Profile Naming."""
entity_id = "climate.vorzimmer2"
entity_name = "Vorzimmer2"
device_model = None
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
test_devices=["Heizkörperthermostat2"],
test_groups=[entity_name],
)
ha_state, hmip_device = get_and_check_entity_basics(
hass, mock_hap, entity_id, entity_name, device_model
)
assert hmip_device
assert ha_state.state == HVACMode.AUTO
assert ha_state.attributes[ATTR_PRESET_MODES] == [
PRESET_BOOST,
PRESET_ECO,
"Testprofile",
"Alternative 1",
]
async def test_hmip_heating_profile_name_not_in_list(
hass: HomeAssistant, default_mock_hap_factory
) -> None:
"""Test set profile when profile is not in available profiles."""
expected_profile = "Testprofile"
entity_id = "climate.vorzimmer2"
entity_name = "Vorzimmer2"
device_model = None
mock_hap = await default_mock_hap_factory.async_get_mock_hap(
test_devices=["Heizkörperthermostat2"],
test_groups=[entity_name],
)
ha_state, hmip_device = get_and_check_entity_basics(
hass, mock_hap, entity_id, entity_name, device_model
)
with patch(
"homeassistant.components.homematicip_cloud.climate.NICE_PROFILE_NAMES",
return_value={},
):
await hass.services.async_call(
"climate",
"set_preset_mode",
{"entity_id": entity_id, "preset_mode": expected_profile},
blocking=True,
)
ha_state = hass.states.get(entity_id)
assert ha_state.attributes[ATTR_PRESET_MODE] == expected_profile
async def test_hmip_climate_services( async def test_hmip_climate_services(
hass: HomeAssistant, mock_hap_with_service hass: HomeAssistant, mock_hap_with_service
) -> None: ) -> None:

View File

@ -26,7 +26,7 @@ async def test_hmip_load_all_supported_devices(
test_devices=None, test_groups=None test_devices=None, test_groups=None
) )
assert len(mock_hap.hmip_device_by_entity_id) == 272 assert len(mock_hap.hmip_device_by_entity_id) == 278
async def test_hmip_remove_device( async def test_hmip_remove_device(