diff --git a/README.md b/README.md index 1d3d7fa8..2920699b 100644 --- a/README.md +++ b/README.md @@ -358,6 +358,11 @@ Use this only to reactivate devices with flaky communication to gain control aga Get a device parameter via the XML-RPC interface. +### `homematicip_local.get_link_peers` + +Call to `getLinkPeers` on the XML-RPC interface. +Returns a dict of link partners + ### `homematicip_local.get_paramset` Call to `getParamset` on the XML-RPC interface. diff --git a/changelog.md b/changelog.md index d0375dfe..1e73538b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,22 @@ -# Version 1.65.1 (2024-08-30) +# Version 1.66.0 (2024-09-01) -- Bump hahomematic to 2024.8.15 +- Bump hahomematic to 2024.9.0 + - Add check for link paramsets + - Add getLinkPeers XmlRPC method + - Add paramset_key to entity_key - Avoid permanent cache save on remove device - Check rx_mode + - Do not create update entities that are not updatable (manually remove obsolete update entities) - Ensure only one load/save of cache file at time + - Load all paramsets + - Mark only level as relevant entity for DALI + - Only try device update refresh if device is updatable + - Refactor update entity - Small definition fix for DALI + - Switch typing of paramset_key from str to ParamsetKey - Use TypedDict for device_description - Use TypedDict for parameter_data +- Add service get_link_peers - Use select for paramset_key with actions calls - Use selector for rx_mode in service description diff --git a/custom_components/homematicip_local/const.py b/custom_components/homematicip_local/const.py index 3779fa25..531e181f 100644 --- a/custom_components/homematicip_local/const.py +++ b/custom_components/homematicip_local/const.py @@ -60,6 +60,7 @@ SERVICE_FETCH_SYSTEM_VARIABLES: Final = "fetch_system_variables" SERVICE_FORCE_DEVICE_AVAILABILITY: Final = "force_device_availability" SERVICE_GET_DEVICE_VALUE: Final = "get_device_value" +SERVICE_GET_LINK_PEERS: Final = "get_link_peers" SERVICE_GET_PARAMSET: Final = "get_paramset" SERVICE_LIGHT_SET_ON_TIME: Final = "light_set_on_time" SERVICE_PUT_PARAMSET: Final = "put_paramset" @@ -82,6 +83,7 @@ SERVICE_GET_DEVICE_VALUE, SERVICE_GET_PARAMSET, SERVICE_LIGHT_SET_ON_TIME, + SERVICE_GET_LINK_PEERS, SERVICE_PUT_PARAMSET, SERVICE_SET_COVER_COMBINED_POSITION, SERVICE_SET_DEVICE_VALUE, diff --git a/custom_components/homematicip_local/manifest.json b/custom_components/homematicip_local/manifest.json index 8c1d6a43..2ba949e4 100644 --- a/custom_components/homematicip_local/manifest.json +++ b/custom_components/homematicip_local/manifest.json @@ -10,7 +10,7 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/danielperna84/hahomematic/issues", "loggers": ["hahomematic"], - "requirements": ["hahomematic==2024.8.15"], + "requirements": ["hahomematic==2024.9.0"], "ssdp": [ { "manufacturer": "EQ3", diff --git a/custom_components/homematicip_local/services.py b/custom_components/homematicip_local/services.py index 0d40ce0f..580f384e 100644 --- a/custom_components/homematicip_local/services.py +++ b/custom_components/homematicip_local/services.py @@ -4,7 +4,7 @@ from datetime import datetime import logging -from typing import TYPE_CHECKING, Final +from typing import TYPE_CHECKING, Final, cast from hahomematic.const import ForcedDeviceAvailability, ParamsetKey from hahomematic.exceptions import ClientException @@ -35,6 +35,7 @@ SERVICE_FETCH_SYSTEM_VARIABLES, SERVICE_FORCE_DEVICE_AVAILABILITY, SERVICE_GET_DEVICE_VALUE, + SERVICE_GET_LINK_PEERS, SERVICE_GET_PARAMSET, SERVICE_PUT_PARAMSET, SERVICE_SET_DEVICE_VALUE, @@ -110,6 +111,16 @@ ), ) +SCHEMA_SERVICE_GET_LINK_PEERS = vol.All( + cv.has_at_least_one_key(CONF_DEVICE_ID, CONF_DEVICE_ADDRESS), + cv.has_at_most_one_key(CONF_DEVICE_ID, CONF_DEVICE_ADDRESS), + BASE_SCHEMA_DEVICE.extend( + { + vol.Optional(CONF_CHANNEL): vol.Coerce(int), + } + ), +) + SCHEMA_SERVICE_GET_PARAMSET = vol.All( cv.has_at_least_one_key(CONF_DEVICE_ID, CONF_DEVICE_ADDRESS), cv.has_at_most_one_key(CONF_DEVICE_ID, CONF_DEVICE_ADDRESS), @@ -194,6 +205,8 @@ async def async_call_hmip_local_service(service: ServiceCall) -> ServiceResponse await _async_service_force_device_availability(hass=hass, service=service) elif service_name == SERVICE_GET_DEVICE_VALUE: return await _async_service_get_device_value(hass=hass, service=service) + elif service_name == SERVICE_GET_LINK_PEERS: + return await _async_service_get_link_peers(hass=hass, service=service) elif service_name == SERVICE_GET_PARAMSET: return await _async_service_get_paramset(hass=hass, service=service) elif service_name == SERVICE_PUT_PARAMSET: @@ -248,6 +261,14 @@ async def async_call_hmip_local_service(service: ServiceCall) -> ServiceResponse supports_response=SupportsResponse.OPTIONAL, ) + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_GET_LINK_PEERS, + service_func=async_call_hmip_local_service, + schema=SCHEMA_SERVICE_GET_LINK_PEERS, + supports_response=SupportsResponse.OPTIONAL, + ) + hass.services.async_register( domain=DOMAIN, service=SERVICE_GET_PARAMSET, @@ -352,6 +373,28 @@ async def _async_service_get_device_value( return None +async def _async_service_get_link_peers( + hass: HomeAssistant, service: ServiceCall +) -> ServiceResponse: + """Service to call the getLinkPeers method on a Homematic(IP) Local connection.""" + channel_no = service.data.get(CONF_CHANNEL) + + if hm_device := _async_get_hm_device_by_service_data(hass=hass, service=service): + address = ( + f"{hm_device.device_address}:{channel_no}" + if channel_no is not None + else hm_device.device_address + ) + try: + return cast( + ServiceResponse, {address: await hm_device.client.get_link_peers(address=address)} + ) + except ClientException as cex: + raise HomeAssistantError(cex) from cex + + return None + + async def _async_service_get_paramset( hass: HomeAssistant, service: ServiceCall ) -> ServiceResponse: diff --git a/custom_components/homematicip_local/services.yaml b/custom_components/homematicip_local/services.yaml index 4789e134..67ef2e1a 100644 --- a/custom_components/homematicip_local/services.yaml +++ b/custom_components/homematicip_local/services.yaml @@ -61,6 +61,25 @@ get_device_value: selector: text: +get_link_peers: + fields: + device_id: + required: false + selector: + device: + integration: homematicip_local + device_address: + example: "0008789453:3" + required: false + selector: + text: + channel: + required: false + selector: + number: + min: 0 + max: 99 + get_paramset: fields: device_id: diff --git a/custom_components/homematicip_local/strings.json b/custom_components/homematicip_local/strings.json index efb060d0..6e140155 100644 --- a/custom_components/homematicip_local/strings.json +++ b/custom_components/homematicip_local/strings.json @@ -721,6 +721,24 @@ } }, "name": "Get device value" + }, + "get_link_peers": { + "description": "Call to getLinkPeers in the RPC XML interface.", + "fields": { + "channel": { + "description": "Channel for calling a paramset.", + "name": "Channel" + }, + "device_address": { + "description": "Enter a device address.", + "name": "Device address" + }, + "device_id": { + "description": "Select a device.", + "name": "Device" + } + }, + "name": "Get link peers" }, "get_paramset": { "description": "Call to getParamset in the RPC XML interface.", diff --git a/custom_components/homematicip_local/translations/de.json b/custom_components/homematicip_local/translations/de.json index 5a557822..b88e5ede 100644 --- a/custom_components/homematicip_local/translations/de.json +++ b/custom_components/homematicip_local/translations/de.json @@ -725,6 +725,24 @@ }, "name": "Gerätewert abrufen" }, + "get_link_peers": { + "description": "Abruf von getLinkPeers in der RPC-XML-Schnittstelle.", + "fields": { + "channel": { + "description": "Kanal zum Abruf eines Parametersatzes.", + "name": "Kanal" + }, + "device_address": { + "description": "Geben Sie eine Geräteadresse ein.", + "name": "Geräteadresse" + }, + "device_id": { + "description": "Wähle ein Gerät.", + "name": "Gerät" + } + }, + "name": "Verbindungsgegenstellen lesen" + }, "get_paramset": { "description": "Abruf von getParamset in der RPC-XML-Schnittstelle.", "fields": { diff --git a/custom_components/homematicip_local/translations/en.json b/custom_components/homematicip_local/translations/en.json index efb060d0..6e140155 100644 --- a/custom_components/homematicip_local/translations/en.json +++ b/custom_components/homematicip_local/translations/en.json @@ -721,6 +721,24 @@ } }, "name": "Get device value" + }, + "get_link_peers": { + "description": "Call to getLinkPeers in the RPC XML interface.", + "fields": { + "channel": { + "description": "Channel for calling a paramset.", + "name": "Channel" + }, + "device_address": { + "description": "Enter a device address.", + "name": "Device address" + }, + "device_id": { + "description": "Select a device.", + "name": "Device" + } + }, + "name": "Get link peers" }, "get_paramset": { "description": "Call to getParamset in the RPC XML interface.", diff --git a/custom_components/homematicip_local/update.py b/custom_components/homematicip_local/update.py index 974b65e2..861e7eec 100644 --- a/custom_components/homematicip_local/update.py +++ b/custom_components/homematicip_local/update.py @@ -5,7 +5,7 @@ import logging from typing import Any, Final -from hahomematic.const import CALLBACK_TYPE, DeviceFirmwareState, HmPlatform +from hahomematic.const import CALLBACK_TYPE, HmPlatform from hahomematic.platforms.update import HmUpdate from homeassistant.components.update import UpdateEntity, UpdateEntityFeature @@ -80,7 +80,7 @@ def __init__( identifiers={(DOMAIN, hm_entity.device.identifier)}, ) self._attr_extra_state_attributes = { - ATTR_FIRMWARE_UPDATE_STATE: hm_entity.firmware_update_state + ATTR_FIRMWARE_UPDATE_STATE: hm_entity.device.firmware_update_state } self._unregister_callbacks: list[CALLBACK_TYPE] = [] _LOGGER.debug("init: Setting up %s", hm_entity.full_name) @@ -98,21 +98,12 @@ def installed_version(self) -> str | None: @property def in_progress(self) -> bool | int | None: """Update installation progress.""" - return self._hm_entity.firmware_update_state in ( - DeviceFirmwareState.DO_UPDATE_PENDING, - DeviceFirmwareState.PERFORMING_UPDATE, - ) + return self._hm_entity.in_progress() @property def latest_version(self) -> str | None: """Latest version available for install.""" - if self._hm_entity.firmware_update_state in ( - DeviceFirmwareState.READY_FOR_UPDATE, - DeviceFirmwareState.DO_UPDATE_PENDING, - DeviceFirmwareState.PERFORMING_UPDATE, - ): - return self._hm_entity.available_firmware - return self._hm_entity.firmware + return self._hm_entity.latest_firmware @property def name(self) -> str | None: diff --git a/requirements_test.txt b/requirements_test.txt index 0e53233c..a43a89f2 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,11 +1,11 @@ -r requirements_test_pre_commit.txt async-upnp-client==0.40.0 -hahomematic==2024.8.15 -homeassistant==2024.9.0b1 +hahomematic==2024.9.0 +homeassistant==2024.9.0b2 mypy==1.11.2 mypy-dev==1.11.0a9 pre-commit==3.8.0 pydevccu==0.1.8 pylint==3.2.6 -pytest-homeassistant-custom-component==0.13.157 +pytest-homeassistant-custom-component==0.13.158