Add homematicip_cloud service set cooling home (#121943)
* [homematicip_cloud] Add service to set cooling mode * Create seperate test for cooling * Rename service to set_home_cooling_mode * Raise exception when accesspoint not found
This commit is contained in:
parent
ecf22e4c4f
commit
81faf1b582
9 changed files with 133 additions and 17 deletions
|
@ -7,6 +7,7 @@
|
||||||
"deactivate_vacation": "mdi:compass-off",
|
"deactivate_vacation": "mdi:compass-off",
|
||||||
"set_active_climate_profile": "mdi:home-thermometer",
|
"set_active_climate_profile": "mdi:home-thermometer",
|
||||||
"dump_hap_config": "mdi:database-export",
|
"dump_hap_config": "mdi:database-export",
|
||||||
"reset_energy_counter": "mdi:reload"
|
"reset_energy_counter": "mdi:reload",
|
||||||
|
"set_home_cooling_mode": "mdi:snowflake"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["homematicip"],
|
"loggers": ["homematicip"],
|
||||||
"quality_scale": "silver",
|
"quality_scale": "silver",
|
||||||
"requirements": ["homematicip==1.1.1"]
|
"requirements": ["homematicip==1.1.2"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import comp_entity_ids
|
from homeassistant.helpers.config_validation import comp_entity_ids
|
||||||
from homeassistant.helpers.service import (
|
from homeassistant.helpers.service import (
|
||||||
|
@ -31,6 +32,7 @@ ATTR_CONFIG_OUTPUT_FILE_PREFIX = "config_output_file_prefix"
|
||||||
ATTR_CONFIG_OUTPUT_PATH = "config_output_path"
|
ATTR_CONFIG_OUTPUT_PATH = "config_output_path"
|
||||||
ATTR_DURATION = "duration"
|
ATTR_DURATION = "duration"
|
||||||
ATTR_ENDTIME = "endtime"
|
ATTR_ENDTIME = "endtime"
|
||||||
|
ATTR_COOLING = "cooling"
|
||||||
|
|
||||||
DEFAULT_CONFIG_FILE_PREFIX = "hmip-config"
|
DEFAULT_CONFIG_FILE_PREFIX = "hmip-config"
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ SERVICE_DEACTIVATE_VACATION = "deactivate_vacation"
|
||||||
SERVICE_DUMP_HAP_CONFIG = "dump_hap_config"
|
SERVICE_DUMP_HAP_CONFIG = "dump_hap_config"
|
||||||
SERVICE_RESET_ENERGY_COUNTER = "reset_energy_counter"
|
SERVICE_RESET_ENERGY_COUNTER = "reset_energy_counter"
|
||||||
SERVICE_SET_ACTIVE_CLIMATE_PROFILE = "set_active_climate_profile"
|
SERVICE_SET_ACTIVE_CLIMATE_PROFILE = "set_active_climate_profile"
|
||||||
|
SERVICE_SET_HOME_COOLING_MODE = "set_home_cooling_mode"
|
||||||
|
|
||||||
HMIPC_SERVICES = [
|
HMIPC_SERVICES = [
|
||||||
SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION,
|
SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION,
|
||||||
|
@ -52,6 +55,7 @@ HMIPC_SERVICES = [
|
||||||
SERVICE_DUMP_HAP_CONFIG,
|
SERVICE_DUMP_HAP_CONFIG,
|
||||||
SERVICE_RESET_ENERGY_COUNTER,
|
SERVICE_RESET_ENERGY_COUNTER,
|
||||||
SERVICE_SET_ACTIVE_CLIMATE_PROFILE,
|
SERVICE_SET_ACTIVE_CLIMATE_PROFILE,
|
||||||
|
SERVICE_SET_HOME_COOLING_MODE,
|
||||||
]
|
]
|
||||||
|
|
||||||
SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION = vol.Schema(
|
SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION = vol.Schema(
|
||||||
|
@ -107,6 +111,13 @@ SCHEMA_RESET_ENERGY_COUNTER = vol.Schema(
|
||||||
{vol.Required(ATTR_ENTITY_ID): comp_entity_ids}
|
{vol.Required(ATTR_ENTITY_ID): comp_entity_ids}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SCHEMA_SET_HOME_COOLING_MODE = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(ATTR_COOLING, default=True): cv.boolean,
|
||||||
|
vol.Optional(ATTR_ACCESSPOINT_ID): vol.All(str, vol.Length(min=24, max=24)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_services(hass: HomeAssistant) -> None:
|
async def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
"""Set up the HomematicIP Cloud services."""
|
"""Set up the HomematicIP Cloud services."""
|
||||||
|
@ -135,6 +146,8 @@ async def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
await _async_reset_energy_counter(hass, service)
|
await _async_reset_energy_counter(hass, service)
|
||||||
elif service_name == SERVICE_SET_ACTIVE_CLIMATE_PROFILE:
|
elif service_name == SERVICE_SET_ACTIVE_CLIMATE_PROFILE:
|
||||||
await _set_active_climate_profile(hass, service)
|
await _set_active_climate_profile(hass, service)
|
||||||
|
elif service_name == SERVICE_SET_HOME_COOLING_MODE:
|
||||||
|
await _async_set_home_cooling_mode(hass, service)
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
domain=HMIPC_DOMAIN,
|
domain=HMIPC_DOMAIN,
|
||||||
|
@ -194,6 +207,14 @@ async def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
schema=SCHEMA_RESET_ENERGY_COUNTER,
|
schema=SCHEMA_RESET_ENERGY_COUNTER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async_register_admin_service(
|
||||||
|
hass=hass,
|
||||||
|
domain=HMIPC_DOMAIN,
|
||||||
|
service=SERVICE_SET_HOME_COOLING_MODE,
|
||||||
|
service_func=async_call_hmipc_service,
|
||||||
|
schema=SCHEMA_SET_HOME_COOLING_MODE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_services(hass: HomeAssistant):
|
async def async_unload_services(hass: HomeAssistant):
|
||||||
"""Unload HomematicIP Cloud services."""
|
"""Unload HomematicIP Cloud services."""
|
||||||
|
@ -324,10 +345,25 @@ async def _async_reset_energy_counter(hass: HomeAssistant, service: ServiceCall)
|
||||||
await device.reset_energy_counter()
|
await device.reset_energy_counter()
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_set_home_cooling_mode(hass: HomeAssistant, service: ServiceCall):
|
||||||
|
"""Service to set the cooling mode."""
|
||||||
|
cooling = service.data[ATTR_COOLING]
|
||||||
|
|
||||||
|
if hapid := service.data.get(ATTR_ACCESSPOINT_ID):
|
||||||
|
if home := _get_home(hass, hapid):
|
||||||
|
await home.set_cooling(cooling)
|
||||||
|
else:
|
||||||
|
for hap in hass.data[HMIPC_DOMAIN].values():
|
||||||
|
await hap.home.set_cooling(cooling)
|
||||||
|
|
||||||
|
|
||||||
def _get_home(hass: HomeAssistant, hapid: str) -> AsyncHome | None:
|
def _get_home(hass: HomeAssistant, hapid: str) -> AsyncHome | None:
|
||||||
"""Return a HmIP home."""
|
"""Return a HmIP home."""
|
||||||
if hap := hass.data[HMIPC_DOMAIN].get(hapid):
|
if hap := hass.data[HMIPC_DOMAIN].get(hapid):
|
||||||
return hap.home
|
return hap.home
|
||||||
|
|
||||||
_LOGGER.info("No matching access point found for access point id %s", hapid)
|
raise ServiceValidationError(
|
||||||
return None
|
translation_domain=HMIPC_DOMAIN,
|
||||||
|
translation_key="access_point_not_found",
|
||||||
|
translation_placeholders={"id": hapid},
|
||||||
|
)
|
||||||
|
|
|
@ -98,3 +98,14 @@ reset_energy_counter:
|
||||||
example: switch.livingroom
|
example: switch.livingroom
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
|
||||||
|
set_home_cooling_mode:
|
||||||
|
fields:
|
||||||
|
cooling:
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
accesspoint_id:
|
||||||
|
example: 3014xxxxxxxxxxxxxxxxxxxx
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
|
@ -26,6 +26,11 @@
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"access_point_not_found": {
|
||||||
|
"message": "No matching access point found for access point id {id}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"services": {
|
"services": {
|
||||||
"activate_eco_mode_with_duration": {
|
"activate_eco_mode_with_duration": {
|
||||||
"name": "Activate eco mode with duration",
|
"name": "Activate eco mode with duration",
|
||||||
|
@ -134,6 +139,20 @@
|
||||||
"description": "The ID of the measuring entity. Use 'all' keyword to reset all energy counters."
|
"description": "The ID of the measuring entity. Use 'all' keyword to reset all energy counters."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"set_home_cooling_mode": {
|
||||||
|
"name": "Set home cooling mode",
|
||||||
|
"description": "Set the heating/cooling mode for the entire home",
|
||||||
|
"fields": {
|
||||||
|
"accesspoint_id": {
|
||||||
|
"name": "[%key:component::homematicip_cloud::services::activate_eco_mode_with_duration::fields::accesspoint_id::name%]",
|
||||||
|
"description": "[%key:component::homematicip_cloud::services::activate_eco_mode_with_duration::fields::accesspoint_id::description%]"
|
||||||
|
},
|
||||||
|
"cooling": {
|
||||||
|
"name": "Cooling",
|
||||||
|
"description": "Enable for cooling mode, disable for heating mode"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1105,7 +1105,7 @@ home-assistant-intents==2024.8.7
|
||||||
homeconnect==0.8.0
|
homeconnect==0.8.0
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==1.1.1
|
homematicip==1.1.2
|
||||||
|
|
||||||
# homeassistant.components.horizon
|
# homeassistant.components.horizon
|
||||||
horimote==0.4.1
|
horimote==0.4.1
|
||||||
|
|
|
@ -931,7 +931,7 @@ home-assistant-intents==2024.8.7
|
||||||
homeconnect==0.8.0
|
homeconnect==0.8.0
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==1.1.1
|
homematicip==1.1.2
|
||||||
|
|
||||||
# homeassistant.components.remember_the_milk
|
# homeassistant.components.remember_the_milk
|
||||||
httplib2==0.20.4
|
httplib2==0.20.4
|
||||||
|
|
|
@ -622,18 +622,67 @@ async def test_hmip_climate_services(
|
||||||
assert len(home._connection.mock_calls) == 10
|
assert len(home._connection.mock_calls) == 10
|
||||||
|
|
||||||
not_existing_hap_id = "5555F7110000000000000001"
|
not_existing_hap_id = "5555F7110000000000000001"
|
||||||
|
with pytest.raises(ServiceValidationError) as excinfo:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"homematicip_cloud",
|
"homematicip_cloud",
|
||||||
"deactivate_vacation",
|
"deactivate_vacation",
|
||||||
{"accesspoint_id": not_existing_hap_id},
|
{"accesspoint_id": not_existing_hap_id},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert home.mock_calls[-1][0] == "deactivate_vacation"
|
assert excinfo.value.translation_domain == HMIPC_DOMAIN
|
||||||
assert home.mock_calls[-1][1] == ()
|
assert excinfo.value.translation_key == "access_point_not_found"
|
||||||
# There is no further call on connection.
|
# There is no further call on connection.
|
||||||
assert len(home._connection.mock_calls) == 10
|
assert len(home._connection.mock_calls) == 10
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hmip_set_home_cooling_mode(
|
||||||
|
hass: HomeAssistant, mock_hap_with_service
|
||||||
|
) -> None:
|
||||||
|
"""Test HomematicipSetHomeCoolingMode."""
|
||||||
|
|
||||||
|
home = mock_hap_with_service.home
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"homematicip_cloud",
|
||||||
|
"set_home_cooling_mode",
|
||||||
|
{"accesspoint_id": HAPID, "cooling": False},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert home.mock_calls[-1][0] == "set_cooling"
|
||||||
|
assert home.mock_calls[-1][1] == (False,)
|
||||||
|
assert len(home._connection.mock_calls) == 1
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"homematicip_cloud",
|
||||||
|
"set_home_cooling_mode",
|
||||||
|
{"accesspoint_id": HAPID, "cooling": True},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert home.mock_calls[-1][0] == "set_cooling"
|
||||||
|
assert home.mock_calls[-1][1]
|
||||||
|
assert len(home._connection.mock_calls) == 2
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
"homematicip_cloud", "set_home_cooling_mode", blocking=True
|
||||||
|
)
|
||||||
|
assert home.mock_calls[-1][0] == "set_cooling"
|
||||||
|
assert home.mock_calls[-1][1]
|
||||||
|
assert len(home._connection.mock_calls) == 3
|
||||||
|
|
||||||
|
not_existing_hap_id = "5555F7110000000000000001"
|
||||||
|
with pytest.raises(ServiceValidationError) as excinfo:
|
||||||
|
await hass.services.async_call(
|
||||||
|
"homematicip_cloud",
|
||||||
|
"set_home_cooling_mode",
|
||||||
|
{"accesspoint_id": not_existing_hap_id, "cooling": True},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert excinfo.value.translation_domain == HMIPC_DOMAIN
|
||||||
|
assert excinfo.value.translation_key == "access_point_not_found"
|
||||||
|
# There is no further call on connection.
|
||||||
|
assert len(home._connection.mock_calls) == 3
|
||||||
|
|
||||||
|
|
||||||
async def test_hmip_heating_group_services(
|
async def test_hmip_heating_group_services(
|
||||||
hass: HomeAssistant, default_mock_hap_factory
|
hass: HomeAssistant, default_mock_hap_factory
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -199,7 +199,7 @@ async def test_setup_services_and_unload_services(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
# Check services are created
|
# Check services are created
|
||||||
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
||||||
assert len(hmipc_services) == 8
|
assert len(hmipc_services) == 9
|
||||||
|
|
||||||
config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN)
|
config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN)
|
||||||
assert len(config_entries) == 1
|
assert len(config_entries) == 1
|
||||||
|
@ -232,7 +232,7 @@ async def test_setup_two_haps_unload_one_by_one(hass: HomeAssistant) -> None:
|
||||||
assert await async_setup_component(hass, HMIPC_DOMAIN, {})
|
assert await async_setup_component(hass, HMIPC_DOMAIN, {})
|
||||||
|
|
||||||
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
||||||
assert len(hmipc_services) == 8
|
assert len(hmipc_services) == 9
|
||||||
|
|
||||||
config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN)
|
config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN)
|
||||||
assert len(config_entries) == 2
|
assert len(config_entries) == 2
|
||||||
|
@ -241,7 +241,7 @@ async def test_setup_two_haps_unload_one_by_one(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
# services still exists
|
# services still exists
|
||||||
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
hmipc_services = hass.services.async_services()[HMIPC_DOMAIN]
|
||||||
assert len(hmipc_services) == 8
|
assert len(hmipc_services) == 9
|
||||||
|
|
||||||
# unload the second AP
|
# unload the second AP
|
||||||
await hass.config_entries.async_unload(config_entries[1].entry_id)
|
await hass.config_entries.async_unload(config_entries[1].entry_id)
|
||||||
|
|
Loading…
Add table
Reference in a new issue