From 73fe35fa3dba71e826bafac432ee7c0492ba10dd Mon Sep 17 00:00:00 2001 From: SukramJ Date: Tue, 12 Nov 2024 08:13:33 +0100 Subject: [PATCH] Add basic support for json clients --- changelog.md | 3 +- hahomematic/client/__init__.py | 79 ++++++++++++++++++++++++------- hahomematic/client/json_rpc.py | 16 +++++++ hahomematic/const.py | 2 +- hahomematic/model/hub/__init__.py | 4 +- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/changelog.md b/changelog.md index d1be8d13..cca73c8c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,8 @@ # Version 2024.11.1 (2024-11-09) -- Add getDeviceDescription, getParamsetDescription, listDevices, getValue, setValue, getParamset, putParamset to json_rpc +- Add basic support for json clients - Add data_point_path event +- Add getDeviceDescription, getParamsetDescription, listDevices, getValue, setValue, getParamset, putParamset to json_rpc - Rename event to data_point_event # Version 2024.11.0 (2024-11-04) diff --git a/hahomematic/client/__init__.py b/hahomematic/client/__init__.py index c40ece8a..92ec1b7e 100644 --- a/hahomematic/client/__init__.py +++ b/hahomematic/client/__init__.py @@ -17,7 +17,7 @@ DEFAULT_CUSTOM_ID, DEFAULT_MAX_WORKERS, DP_KEY, - HOMEGEAR_SERIAL, + DUMMY_SERIAL, INIT_DATETIME, INTERFACES_SUPPORTING_FIRMWARE_UPDATES, VIRTUAL_REMOTE_MODELS, @@ -1168,6 +1168,31 @@ async def _get_system_information(self) -> SystemInformation: class ClientJsonCCU(ClientCCU): """Client implementation for CCU backend.""" + async def init_client(self) -> None: + """Init the client.""" + self._system_information = await self._get_system_information() + + @service(re_raise=False, no_raise_return=False) + async def check_connection_availability(self, handle_ping_pong: bool) -> bool: + """Check if proxy is still initialized.""" + return await self._json_rpc_client.is_present(interface=self.interface) + + async def proxy_init(self) -> ProxyInitState: + """Init the proxy has to tell the CCU / Homegear where to send the events.""" + device_descriptions = await self.list_devices() + await self.central.add_new_devices( + interface_id=self.interface_id, device_descriptions=device_descriptions + ) + return ProxyInitState.INIT_SUCCESS + + async def proxy_de_init(self) -> ProxyInitState: + """De-init to stop CCU from sending events for this remote.""" + return ProxyInitState.DE_INIT_SUCCESS + + async def stop(self) -> None: + """Stop depending services.""" + return + @property def supports_ping_pong(self) -> bool: """Return the supports_ping_pong info of the backend.""" @@ -1355,6 +1380,16 @@ async def _exec_set_value( value=value, ) + async def _get_system_information(self) -> SystemInformation: + """Get system information of the backend.""" + return SystemInformation( + available_interfaces=( + Interface.CUXD, + Interface.CCU_JACK, + ), + serial=f"{self.interface}_{DUMMY_SERIAL}", + ) + class ClientHomegear(Client): """Client implementation for Homegear backend.""" @@ -1390,8 +1425,8 @@ async def fetch_device_details(self) -> None: ): try: self.central.device_details.add_name( - address, - await self._proxy_read.getMetadata(address, _NAME), + address=address, + name=await self._proxy_read.getMetadata(address, _NAME), ) except BaseHomematicException as ex: _LOGGER.warning( @@ -1489,7 +1524,7 @@ async def get_all_functions(self) -> dict[str, set[str]]: async def _get_system_information(self) -> SystemInformation: """Get system information of the backend.""" return SystemInformation( - available_interfaces=(Interface.BIDCOS_RF,), serial=HOMEGEAR_SERIAL + available_interfaces=(Interface.BIDCOS_RF,), serial=f"{self.interface}_{DUMMY_SERIAL}" ) @@ -1525,20 +1560,19 @@ def __init__( async def get_client(self) -> Client: """Identify the used client.""" - client: Client | None = None - check_proxy = await self._get_simple_xml_rpc_proxy() try: - if methods := check_proxy.supported_methods: - # BidCos-Wired does not support getVersion() - self.version = ( - cast(str, await check_proxy.getVersion()) if "getVersion" in methods else "0" - ) - - if client := ( - ClientHomegear(client_config=self) - if "Homegear" in self.version or "pydevccu" in self.version - else ClientCCU(client_config=self) + self.version = await self._get_version() + client: Client | None + if self.interface == Interface.BIDCOS_RF and ( + "Homegear" in self.version or "pydevccu" in self.version ): + client = ClientHomegear(client_config=self) + elif self.interface in (Interface.CCU_JACK, Interface.CUXD): + client = ClientJsonCCU(client_config=self) + else: + client = ClientCCU(client_config=self) + + if client: await client.init_client() if await client.check_connection_availability(handle_ping_pong=False): return client @@ -1548,6 +1582,19 @@ async def get_client(self) -> Client: except Exception as ex: raise NoConnectionException(f"Unable to connect {reduce_args(args=ex.args)}.") from ex + async def _get_version(self) -> str: + """Return the version of the backend.""" + if self.interface in (Interface.CCU_JACK, Interface.CUXD): + return str(self.interface) + check_proxy = await self._get_simple_xml_rpc_proxy() + try: + if (methods := check_proxy.supported_methods) and "getVersion" in methods: + # BidCos-Wired does not support getVersion() + return cast(str, await check_proxy.getVersion()) + except Exception as ex: + raise NoConnectionException(f"Unable to connect {reduce_args(args=ex.args)}.") from ex + return "0" + async def get_xml_rpc_proxy( self, auth_enabled: bool | None = None, max_workers: int = DEFAULT_MAX_WORKERS ) -> XmlRpcProxy: diff --git a/hahomematic/client/json_rpc.py b/hahomematic/client/json_rpc.py index 2bffbf85..3070daa1 100644 --- a/hahomematic/client/json_rpc.py +++ b/hahomematic/client/json_rpc.py @@ -97,6 +97,7 @@ class _JsonRpcMethod(StrEnum): INTERFACE_GET_PARAMSET = "Interface.getParamset" INTERFACE_GET_PARAMSET_DESCRIPTION = "Interface.getParamsetDescription" INTERFACE_GET_VALUE = "Interface.getValue" + INTERFACE_IS_PRESENT = "Interface.isPresent" INTERFACE_LIST_DEVICES = "Interface.listDevices" INTERFACE_LIST_INTERFACES = "Interface.listInterfaces" INTERFACE_PUT_PARAMSET = "Interface.putParamset" @@ -919,6 +920,21 @@ async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, . return tuple(all_programs) + async def is_present(self, interface: Interface) -> bool: + """Get value from CCU.""" + value: bool = False + params = {_JsonKey.INTERFACE: interface} + + response = await self._post( + method=_JsonRpcMethod.INTERFACE_IS_PRESENT, extra_params=params + ) + + _LOGGER.debug("IS_PRESENT: Getting the value") + if json_result := response[_JsonKey.RESULT]: + value = bool(json_result) + + return value + async def has_program_ids(self, channel_hmid: str) -> bool: """Return if a channel has program ids.""" params = {_JsonKey.ID: channel_hmid} diff --git a/hahomematic/const.py b/hahomematic/const.py index 48980dd0..7cce16e9 100644 --- a/hahomematic/const.py +++ b/hahomematic/const.py @@ -69,7 +69,7 @@ REPORT_VALUE_USAGE_VALUE_ID: Final = "PRESS_SHORT" REPORT_VALUE_USAGE_DATA: Final = "reportValueUsageData" -HOMEGEAR_SERIAL = "Homegear_SN0815" +DUMMY_SERIAL = "SN0815" PROGRAM_ADDRESS: Final = "program" SYSVAR_ADDRESS: Final = "sysvar" diff --git a/hahomematic/model/hub/__init__.py b/hahomematic/model/hub/__init__.py index 1b8dd3a3..05ce51fd 100644 --- a/hahomematic/model/hub/__init__.py +++ b/hahomematic/model/hub/__init__.py @@ -63,7 +63,7 @@ async def fetch_sysvar_data(self, scheduled: bool) -> None: """Fetch sysvar data for the hub.""" if self._config.sysvar_scan_enabled: _LOGGER.debug( - "FETCH_SYSVAR_DATA: % fetching of system variables for %s", + "FETCH_SYSVAR_DATA: %s fetching of system variables for %s", "Scheduled" if scheduled else "Manual", self._central.name, ) @@ -76,7 +76,7 @@ async def fetch_program_data(self, scheduled: bool) -> None: """Fetch program data for the hub.""" if self._config.program_scan_enabled: _LOGGER.debug( - "FETCH_PROGRAM_DATA: % fetching of programs for %s", + "FETCH_PROGRAM_DATA: %s fetching of programs for %s", "Scheduled" if scheduled else "Manual", self._central.name, )