From ecc1d6d27a82aa9860aa6ad506c6aa39ff50eb2e Mon Sep 17 00:00:00 2001 From: SukramJ Date: Tue, 29 Oct 2024 19:32:35 +0100 Subject: [PATCH] Bump hahomematic to 2024.10.17 (#751) * Bump hahomematic to 2024.10.17 * Add issue messages * Update test_recorder.py --- README.md | 15 +- changelog.md | 5 +- .../homematicip_local/climate.py | 36 ++--- custom_components/homematicip_local/const.py | 103 +++++------- .../homematicip_local/control_unit.py | 55 ++++--- custom_components/homematicip_local/cover.py | 4 +- custom_components/homematicip_local/event.py | 14 +- custom_components/homematicip_local/light.py | 4 +- .../homematicip_local/logbook.py | 4 +- .../homematicip_local/manifest.json | 2 +- .../homematicip_local/services.py | 151 ++++++++---------- custom_components/homematicip_local/siren.py | 4 +- .../homematicip_local/strings.json | 4 + .../homematicip_local/support.py | 23 +-- custom_components/homematicip_local/switch.py | 4 +- .../homematicip_local/translations/de.json | 4 + .../homematicip_local/translations/en.json | 4 + requirements_test.txt | 2 +- tests/test_config_flow.py | 6 +- tests/test_recorder.py | 10 +- 20 files changed, 210 insertions(+), 244 deletions(-) diff --git a/README.md b/README.md index 4c36cd57..ba7ffd2c 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,18 @@ The `ERROR*` parameters are evaluated for this event type in the backend. Go to the devices page of the integration and select a device. Click the three-dot menu at the button and press Delete. This will only delete the device from Home Assistant and not from the CCU. -### What is the meaning of `XmlRPC-Server received no events`? +### What is the meaning of `Error fetching initial data` / `Fehler beim Abrufen der Anfangsdaten`? + +This integration uses a [REGA script](https://github.com/danielperna84/hahomematic/blob/devel/hahomematic/rega_scripts/fetch_all_device_data.fn) to fetch as much data in a single call as possible, to avoid multiple request to get the required initial data. +In rare cases the output of the script can be invalid, so a further processing is not possible, and the fallback solution is to fetch all required data with individual calls, that cause a higher duty cycle during the start phase of the integration. + +This problem can be analysed by executing the [REGA script](https://github.com/danielperna84/hahomematic/blob/devel/hahomematic/rega_scripts/fetch_all_device_data.fn) in the CCU. The parameter ##interface## (line 17) must be replaced with the interface mention from the poped-up issue. The expected result is a valid json. +Search (search for GET_ALL_DEVICE_DATA) within the issue tracker and discussion forum for related items. + +Please don't create an issue, because this is not an issue with the integration. +Use an existing discussion or start a new one, and attach the result of the executed REGA script. + +### What is the meaning of `XmlRPC-Server received no events` / `XmlRPC-Server empfängt keine Ereignisse`? This integration does not fetch new updates from the backend, it **receives** state changes and new values for devices from the backend by the XmlRPC server. @@ -546,7 +557,7 @@ This persistent notification is only displayed in HA if the received PONG events So the message means there is a problem in the communication from the backend to HA that was **identified** by the integration but not **caused**. -### What is the meaning of `Ping/Pong Mismatch on Interface`? +### What is the meaning of `Pending Pong mismatch on interface` / `Austehende Pong Ereignisse auf Interface`? Only relevant for CCU. diff --git a/changelog.md b/changelog.md index 65f2d1a6..044dee42 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,12 @@ # Version 1.69.0 (2024-10-27) -- Bump hahomematic to 2024.10.16 +- Bump hahomematic to 2024.10.17 + - Fire interface event, if data could not be fetched with script from CCU - Optimize MASTER data load - Rename model to better distinguish from HA + - Use enum for json/event keys - Add missing action icons - Follow backend changes +- Use enum for services # Version 1.68.1 (2024-10-26) - Bump hahomematic to 2024.10.14 diff --git a/custom_components/homematicip_local/climate.py b/custom_components/homematicip_local/climate.py index 93eb8aed..3c15be04 100644 --- a/custom_components/homematicip_local/climate.py +++ b/custom_components/homematicip_local/climate.py @@ -44,19 +44,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomematicConfigEntry -from .const import ( - SERVICE_COPY_SCHEDULE, - SERVICE_COPY_SCHEDULE_PROFILE, - SERVICE_DISABLE_AWAY_MODE, - SERVICE_ENABLE_AWAY_MODE_BY_CALENDAR, - SERVICE_ENABLE_AWAY_MODE_BY_DURATION, - SERVICE_GET_SCHEDULE_PROFILE, - SERVICE_GET_SCHEDULE_PROFILE_WEEKDAY, - SERVICE_SET_SCHEDULE_PROFILE, - SERVICE_SET_SCHEDULE_PROFILE_WEEKDAY, - SERVICE_SET_SCHEDULE_SIMPLE_PROFILE, - SERVICE_SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY, -) +from .const import HmipLocalServices from .control_unit import ControlUnit, signal_new_data_point from .generic_entity import HaHomematicGenericEntity, HaHomematicGenericRestoreEntity @@ -143,7 +131,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( - name=SERVICE_ENABLE_AWAY_MODE_BY_CALENDAR, + name=HmipLocalServices.ENABLE_AWAY_MODE_BY_CALENDAR, schema={ vol.Optional(ATTR_AWAY_START): cv.datetime, vol.Required(ATTR_AWAY_END): cv.datetime, @@ -154,7 +142,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: func="async_enable_away_mode_by_calendar", ) platform.async_register_entity_service( - name=SERVICE_ENABLE_AWAY_MODE_BY_DURATION, + name=HmipLocalServices.ENABLE_AWAY_MODE_BY_DURATION, schema={ vol.Required(ATTR_AWAY_HOURS): cv.positive_int, vol.Required(ATTR_AWAY_TEMPERATURE, default=18.0): vol.All( @@ -164,13 +152,13 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: func="async_enable_away_mode_by_duration", ) platform.async_register_entity_service( - name=SERVICE_DISABLE_AWAY_MODE, + name=HmipLocalServices.DISABLE_AWAY_MODE, schema={}, func="async_disable_away_mode", ) platform.async_register_entity_service( - name=SERVICE_COPY_SCHEDULE, + name=HmipLocalServices.COPY_SCHEDULE, schema={ vol.Required(ATTR_SOURCE_ENTITY_ID): cv.string, }, @@ -178,7 +166,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_COPY_SCHEDULE_PROFILE, + name=HmipLocalServices.COPY_SCHEDULE_PROFILE, schema={ vol.Optional(ATTR_SOURCE_ENTITY_ID): cv.string, vol.Required(ATTR_SOURCE_PROFILE): cv.string, @@ -189,7 +177,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_GET_SCHEDULE_PROFILE, + name=HmipLocalServices.GET_SCHEDULE_PROFILE, schema={ vol.Required(ATTR_PROFILE): cv.string, }, @@ -198,7 +186,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_GET_SCHEDULE_PROFILE_WEEKDAY, + name=HmipLocalServices.GET_SCHEDULE_PROFILE_WEEKDAY, schema={ vol.Required(ATTR_PROFILE): cv.string, vol.Required(ATTR_WEEKDAY): cv.string, @@ -208,7 +196,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_SET_SCHEDULE_PROFILE, + name=HmipLocalServices.SET_SCHEDULE_PROFILE, schema={ vol.Required(ATTR_PROFILE): cv.string, vol.Required(ATTR_PROFILE_DATA): dict, @@ -217,7 +205,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_SET_SCHEDULE_PROFILE_WEEKDAY, + name=HmipLocalServices.SET_SCHEDULE_PROFILE_WEEKDAY, schema={ vol.Required(ATTR_PROFILE): cv.string, vol.Required(ATTR_WEEKDAY): cv.string, @@ -227,7 +215,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_SET_SCHEDULE_SIMPLE_PROFILE, + name=HmipLocalServices.SET_SCHEDULE_SIMPLE_PROFILE, schema={ vol.Required(ATTR_PROFILE): cv.string, vol.Required(ATTR_BASE_TEMPERATURE): cv.positive_float, @@ -237,7 +225,7 @@ def async_add_climate(data_points: tuple[BaseCustomDpClimate, ...]) -> None: ) platform.async_register_entity_service( - name=SERVICE_SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY, + name=HmipLocalServices.SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY, schema={ vol.Required(ATTR_PROFILE): cv.string, vol.Required(ATTR_WEEKDAY): cv.string, diff --git a/custom_components/homematicip_local/const.py b/custom_components/homematicip_local/const.py index 6bd6dffb..fdba06f1 100644 --- a/custom_components/homematicip_local/const.py +++ b/custom_components/homematicip_local/const.py @@ -11,7 +11,7 @@ DOMAIN: Final = "homematicip_local" HMIP_LOCAL_MIN_HA_VERSION: Final = "2024.10.0dev0" -HMIP_LOCAL_HAHOMEMATIC_VERSION: Final = "2024.10.16" +HMIP_LOCAL_HAHOMEMATIC_VERSION: Final = "2024.10.17" DEFAULT_DEVICE_FIRMWARE_CHECK_ENABLED: Final = True DEFAULT_DEVICE_FIRMWARE_CHECK_INTERVAL: Final = 21600 # 6h @@ -56,71 +56,42 @@ EVENT_TITLE: Final = "title" EVENT_UNAVAILABLE: Final = "unavailable" -SERVICE_CLEAR_CACHE: Final = "clear_cache" -SERVICE_COPY_SCHEDULE: Final = "copy_schedule" -SERVICE_COPY_SCHEDULE_PROFILE: Final = "copy_schedule_profile" -SERVICE_CREATE_CENTRAL_LINKS: Final = "create_central_links" -SERVICE_DISABLE_AWAY_MODE: Final = "disable_away_mode" -SERVICE_ENABLE_AWAY_MODE_BY_CALENDAR: Final = "enable_away_mode_by_calendar" -SERVICE_ENABLE_AWAY_MODE_BY_DURATION: Final = "enable_away_mode_by_duration" -SERVICE_EXPORT_DEVICE_DEFINITION: Final = "export_device_definition" -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_PARAMSET: Final = "get_link_paramset" -SERVICE_GET_LINK_PEERS: Final = "get_link_peers" -SERVICE_GET_PARAMSET: Final = "get_paramset" -SERVICE_GET_SCHEDULE_PROFILE: Final = "get_schedule_profile" -SERVICE_GET_SCHEDULE_PROFILE_WEEKDAY: Final = "get_schedule_profile_weekday" -SERVICE_LIGHT_SET_ON_TIME: Final = "light_set_on_time" -SERVICE_PUT_LINK_PARAMSET: Final = "put_link_paramset" -SERVICE_PUT_PARAMSET: Final = "put_paramset" -SERVICE_REMOVE_CENTRAL_LINKS: Final = "remove_central_links" -SERVICE_SET_COVER_COMBINED_POSITION: Final = "set_cover_combined_position" -SERVICE_SET_DEVICE_VALUE: Final = "set_device_value" -SERVICE_SET_INSTALL_MODE: Final = "set_install_mode" -SERVICE_SET_SCHEDULE_PROFILE: Final = "set_schedule_profile" -SERVICE_SET_SCHEDULE_PROFILE_WEEKDAY: Final = "set_schedule_profile_weekday" -SERVICE_SET_SCHEDULE_SIMPLE_PROFILE: Final = "set_schedule_simple_profile" -SERVICE_SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY: Final = "set_schedule_simple_profile_weekday" -SERVICE_SET_VARIABLE_VALUE: Final = "set_variable_value" -SERVICE_SWITCH_SET_ON_TIME: Final = "switch_set_on_time" -SERVICE_TURN_ON_SIREN: Final = "turn_on_siren" -SERVICE_UPDATE_DEVICE_FIRMWARE_DATA: Final = "update_device_firmware_data" - -HMIP_LOCAL_SERVICES: Final = ( - SERVICE_CLEAR_CACHE, - SERVICE_COPY_SCHEDULE, - SERVICE_COPY_SCHEDULE_PROFILE, - SERVICE_CREATE_CENTRAL_LINKS, - SERVICE_DISABLE_AWAY_MODE, - SERVICE_ENABLE_AWAY_MODE_BY_CALENDAR, - SERVICE_ENABLE_AWAY_MODE_BY_DURATION, - SERVICE_EXPORT_DEVICE_DEFINITION, - SERVICE_FETCH_SYSTEM_VARIABLES, - SERVICE_FORCE_DEVICE_AVAILABILITY, - SERVICE_GET_DEVICE_VALUE, - SERVICE_GET_LINK_PARAMSET, - SERVICE_GET_LINK_PEERS, - SERVICE_GET_PARAMSET, - SERVICE_GET_SCHEDULE_PROFILE, - SERVICE_GET_SCHEDULE_PROFILE_WEEKDAY, - SERVICE_LIGHT_SET_ON_TIME, - SERVICE_PUT_LINK_PARAMSET, - SERVICE_PUT_PARAMSET, - SERVICE_REMOVE_CENTRAL_LINKS, - SERVICE_SET_COVER_COMBINED_POSITION, - SERVICE_SET_DEVICE_VALUE, - SERVICE_SET_INSTALL_MODE, - SERVICE_SET_SCHEDULE_PROFILE, - SERVICE_SET_SCHEDULE_PROFILE_WEEKDAY, - SERVICE_SET_SCHEDULE_SIMPLE_PROFILE, - SERVICE_SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY, - SERVICE_SET_VARIABLE_VALUE, - SERVICE_SWITCH_SET_ON_TIME, - SERVICE_TURN_ON_SIREN, - SERVICE_UPDATE_DEVICE_FIRMWARE_DATA, -) + +class HmipLocalServices(StrEnum): + """Enum with services.""" + + CLEAR_CACHE = "clear_cache" + COPY_SCHEDULE = "copy_schedule" + COPY_SCHEDULE_PROFILE = "copy_schedule_profile" + CREATE_CENTRAL_LINKS = "create_central_links" + DISABLE_AWAY_MODE = "disable_away_mode" + ENABLE_AWAY_MODE_BY_CALENDAR = "enable_away_mode_by_calendar" + ENABLE_AWAY_MODE_BY_DURATION = "enable_away_mode_by_duration" + EXPORT_DEVICE_DEFINITION = "export_device_definition" + FETCH_SYSTEM_VARIABLES = "fetch_system_variables" + FORCE_DEVICE_AVAILABILITY = "force_device_availability" + GET_DEVICE_VALUE = "get_device_value" + GET_LINK_PARAMSET = "get_link_paramset" + GET_LINK_PEERS = "get_link_peers" + GET_PARAMSET = "get_paramset" + GET_SCHEDULE_PROFILE = "get_schedule_profile" + GET_SCHEDULE_PROFILE_WEEKDAY = "get_schedule_profile_weekday" + LIGHT_SET_ON_TIME = "light_set_on_time" + PUT_LINK_PARAMSET = "put_link_paramset" + PUT_PARAMSET = "put_paramset" + REMOVE_CENTRAL_LINKS = "remove_central_links" + SET_COVER_COMBINED_POSITION = "set_cover_combined_position" + SET_DEVICE_VALUE = "set_device_value" + SET_INSTALL_MODE = "set_install_mode" + SET_SCHEDULE_PROFILE = "set_schedule_profile" + SET_SCHEDULE_PROFILE_WEEKDAY = "set_schedule_profile_weekday" + SET_SCHEDULE_SIMPLE_PROFILE = "set_schedule_simple_profile" + SET_SCHEDULE_SIMPLE_PROFILE_WEEKDAY = "set_schedule_simple_profile_weekday" + SET_VARIABLE_VALUE = "set_variable_value" + SWITCH_SET_ON_TIME = "switch_set_on_time" + TURN_ON_SIREN = "turn_on_siren" + UPDATE_DEVICE_FIRMWARE_DATA = "update_device_firmware_data" + TOTAL_SYSVAR: Final[tuple[str, ...]] = ( "svEnergyCounter_", diff --git a/custom_components/homematicip_local/control_unit.py b/custom_components/homematicip_local/control_unit.py index 469f8d50..12c31fca 100644 --- a/custom_components/homematicip_local/control_unit.py +++ b/custom_components/homematicip_local/control_unit.py @@ -16,20 +16,12 @@ CALLBACK_TYPE, CONF_PASSWORD, CONF_USERNAME, - EVENT_ADDRESS, - EVENT_AVAILABLE, - EVENT_DATA, - EVENT_INTERFACE_ID, - EVENT_PARAMETER, - EVENT_PONG_MISMATCH_COUNT, - EVENT_SECONDS_SINCE_LAST_EVENT, - EVENT_TYPE, - EVENT_VALUE, IP_ANY_V4, PORT_ANY, BackendSystemEvent, DataPointCategory, DeviceFirmwareState, + EventKey, EventType, InterfaceEventType, InterfaceName, @@ -346,17 +338,17 @@ def _async_backend_system_callback( def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, Any]) -> None: """Execute the callback used for device related events.""" - interface_id = event_data[EVENT_INTERFACE_ID] + interface_id = event_data[EventKey.INTERFACE_ID] if event_type == EventType.INTERFACE: - interface_event_type = event_data[EVENT_TYPE] + interface_event_type = event_data[EventKey.TYPE] issue_id = f"{interface_event_type}-{interface_id}" event_data = cast(dict[str, Any], INTERFACE_EVENT_SCHEMA(event_data)) - data = event_data[EVENT_DATA] + data = event_data[EventKey.DATA] if interface_event_type == InterfaceEventType.CALLBACK: if not self._enable_system_notifications: _LOGGER.debug("SYSTEM NOTIFICATION disabled for CALLBACK") return - if data[EVENT_AVAILABLE]: + if data[EventKey.AVAILABLE]: async_delete_issue(hass=self._hass, domain=DOMAIN, issue_id=issue_id) else: async_create_issue( @@ -368,15 +360,17 @@ def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, severity=IssueSeverity.WARNING, translation_key="xmlrpc_server_receives_no_events", translation_placeholders={ - EVENT_INTERFACE_ID: interface_id, - EVENT_SECONDS_SINCE_LAST_EVENT: data[EVENT_SECONDS_SINCE_LAST_EVENT], + EventKey.INTERFACE_ID: interface_id, + EventKey.SECONDS_SINCE_LAST_EVENT: data[ + EventKey.SECONDS_SINCE_LAST_EVENT + ], }, ) elif interface_event_type == InterfaceEventType.PENDING_PONG: if not self._enable_system_notifications: _LOGGER.debug("SYSTEM NOTIFICATION disabled for PENDING_PONG") return - if data[EVENT_PONG_MISMATCH_COUNT] == 0: + if data[EventKey.PONG_MISMATCH_COUNT] == 0: async_delete_issue( hass=self._hass, domain=DOMAIN, @@ -393,11 +387,11 @@ def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, translation_key="pending_pong_mismatch", translation_placeholders={ CONF_INSTANCE_NAME: self._instance_name, - EVENT_INTERFACE_ID: interface_id, + EventKey.INTERFACE_ID: interface_id, }, ) elif interface_event_type == InterfaceEventType.PROXY: - if data[EVENT_AVAILABLE]: + if data[EventKey.AVAILABLE]: async_delete_issue(hass=self._hass, domain=DOMAIN, issue_id=issue_id) else: async_create_issue( @@ -408,12 +402,23 @@ def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, severity=IssueSeverity.WARNING, translation_key="interface_not_reachable", translation_placeholders={ - EVENT_INTERFACE_ID: interface_id, + EventKey.INTERFACE_ID: interface_id, }, ) - + elif interface_event_type == InterfaceEventType.FETCH_DATA: + async_create_issue( + hass=self._hass, + domain=DOMAIN, + issue_id=issue_id, + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="fetch_data", + translation_placeholders={ + EventKey.INTERFACE_ID: interface_id, + }, + ) else: - device_address = event_data[EVENT_ADDRESS] + device_address = event_data[EventKey.ADDRESS] name: str | None = None if device_entry := self._async_get_device_entry(device_address=device_address): name = device_entry.name_by_user or device_entry.name @@ -426,8 +431,8 @@ def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, event_data=event_data, ) elif event_type == EventType.DEVICE_AVAILABILITY: - parameter = event_data[EVENT_PARAMETER] - unavailable = event_data[EVENT_VALUE] + parameter = event_data[EventKey.PARAMETER] + unavailable = event_data[EventKey.VALUE] if parameter in (Parameter.STICKY_UN_REACH, Parameter.UN_REACH): title = f"{DOMAIN.upper()} Device not reachable" event_data.update( @@ -448,13 +453,13 @@ def _async_homematic_callback(self, event_type: EventType, event_data: dict[str, event_data=event_data, ) elif event_type == EventType.DEVICE_ERROR: - error_parameter = event_data[EVENT_PARAMETER] + error_parameter = event_data[EventKey.PARAMETER] if error_parameter in FILTER_ERROR_EVENT_PARAMETERS: return error_parameter_display = error_parameter.replace("_", " ").title() title = f"{DOMAIN.upper()} Device Error" error_message: str = "" - error_value = event_data[EVENT_VALUE] + error_value = event_data[EventKey.VALUE] display_error: bool = False if isinstance(error_value, bool): display_error = error_value diff --git a/custom_components/homematicip_local/cover.py b/custom_components/homematicip_local/cover.py index 1f036096..5d621b98 100644 --- a/custom_components/homematicip_local/cover.py +++ b/custom_components/homematicip_local/cover.py @@ -25,7 +25,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomematicConfigEntry -from .const import SERVICE_SET_COVER_COMBINED_POSITION +from .const import HmipLocalServices from .control_unit import ControlUnit, signal_new_data_point from .generic_entity import HaHomematicGenericRestoreEntity from .services import CONF_WAIT_FOR_CALLBACK @@ -109,7 +109,7 @@ def async_add_cover(data_points: tuple[HmGenericCover, ...]) -> None: platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( - SERVICE_SET_COVER_COMBINED_POSITION, + HmipLocalServices.SET_COVER_COMBINED_POSITION, { vol.Required(ATTR_POSITION): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), vol.Optional(ATTR_TILT_POSITION): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), diff --git a/custom_components/homematicip_local/event.py b/custom_components/homematicip_local/event.py index 4b9e074e..c4e90197 100644 --- a/custom_components/homematicip_local/event.py +++ b/custom_components/homematicip_local/event.py @@ -5,13 +5,7 @@ import logging from typing import Any -from hahomematic.const import ( - CALLBACK_TYPE, - DATA_POINT_EVENTS, - EVENT_ADDRESS, - EVENT_INTERFACE_ID, - DataPointCategory, -) +from hahomematic.const import CALLBACK_TYPE, DATA_POINT_EVENTS, DataPointCategory, EventKey from hahomematic.model.device import Channel, Device from hahomematic.model.event import GenericEvent @@ -76,7 +70,7 @@ class HaHomematicEvent(EventEntity): _attr_has_entity_name = True _attr_should_poll = False - _unrecorded_attributes = frozenset({EVENT_ADDRESS, EVENT_INTERFACE_ID, EVENT_MODEL}) + _unrecorded_attributes = frozenset({EventKey.ADDRESS, EventKey.INTERFACE_ID, EVENT_MODEL}) def __init__( self, @@ -97,8 +91,8 @@ def __init__( identifiers={(DOMAIN, self._hm_device.identifier)}, ) self._attr_extra_state_attributes = { - EVENT_INTERFACE_ID: self._hm_device.interface_id, - EVENT_ADDRESS: self._hm_channel.address, + EventKey.INTERFACE_ID: self._hm_device.interface_id, + EventKey.ADDRESS: self._hm_channel.address, EVENT_MODEL: self._hm_device.model, } self._unregister_callbacks: list[CALLBACK_TYPE] = [] diff --git a/custom_components/homematicip_local/light.py b/custom_components/homematicip_local/light.py index 0b9087e4..07fb5c14 100644 --- a/custom_components/homematicip_local/light.py +++ b/custom_components/homematicip_local/light.py @@ -32,7 +32,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomematicConfigEntry -from .const import SERVICE_LIGHT_SET_ON_TIME +from .const import HmipLocalServices from .control_unit import ControlUnit, signal_new_data_point from .generic_entity import HaHomematicGenericRestoreEntity @@ -81,7 +81,7 @@ def async_add_light(data_points: tuple[CustomDpDimmer, ...]) -> None: platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( - SERVICE_LIGHT_SET_ON_TIME, + HmipLocalServices.LIGHT_SET_ON_TIME, { vol.Required(ATTR_ON_TIME): vol.All(vol.Coerce(int), vol.Range(min=0, max=8580000)), }, diff --git a/custom_components/homematicip_local/logbook.py b/custom_components/homematicip_local/logbook.py index f941e27c..63bfa358 100644 --- a/custom_components/homematicip_local/logbook.py +++ b/custom_components/homematicip_local/logbook.py @@ -4,7 +4,7 @@ from collections.abc import Callable -from hahomematic.const import EVENT_PARAMETER, EventType +from hahomematic.const import EventKey, EventType from homeassistant.components.logbook import LOGBOOK_ENTRY_MESSAGE, LOGBOOK_ENTRY_NAME from homeassistant.core import Event, HomeAssistant, callback @@ -25,7 +25,7 @@ def async_describe_homematic_device_error_event(event: Event) -> dict[str, str]: """Describe Homematic(IP) Local logbook device error event.""" if not is_valid_event(event_data=event.data, schema=DEVICE_ERROR_EVENT_SCHEMA): return {} - error_name = event.data[EVENT_PARAMETER].replace("_", " ").title() + error_name = event.data[EventKey.PARAMETER].replace("_", " ").title() error_value = event.data[EVENT_ERROR_VALUE] is_error = event.data[EVENT_ERROR] error_message = ( diff --git a/custom_components/homematicip_local/manifest.json b/custom_components/homematicip_local/manifest.json index a3ef8415..02558a40 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.10.16"], + "requirements": ["hahomematic==2024.10.17"], "ssdp": [ { "manufacturer": "EQ3", diff --git a/custom_components/homematicip_local/services.py b/custom_components/homematicip_local/services.py index a4f7ff7c..d453f5f8 100644 --- a/custom_components/homematicip_local/services.py +++ b/custom_components/homematicip_local/services.py @@ -28,26 +28,7 @@ from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.service import async_register_admin_service, verify_domain_control -from .const import ( - DOMAIN, - HMIP_LOCAL_SERVICES, - SERVICE_CLEAR_CACHE, - SERVICE_CREATE_CENTRAL_LINKS, - SERVICE_EXPORT_DEVICE_DEFINITION, - SERVICE_FETCH_SYSTEM_VARIABLES, - SERVICE_FORCE_DEVICE_AVAILABILITY, - SERVICE_GET_DEVICE_VALUE, - SERVICE_GET_LINK_PARAMSET, - SERVICE_GET_LINK_PEERS, - SERVICE_GET_PARAMSET, - SERVICE_PUT_LINK_PARAMSET, - SERVICE_PUT_PARAMSET, - SERVICE_REMOVE_CENTRAL_LINKS, - SERVICE_SET_DEVICE_VALUE, - SERVICE_SET_INSTALL_MODE, - SERVICE_SET_VARIABLE_VALUE, - SERVICE_UPDATE_DEVICE_FIRMWARE_DATA, -) +from .const import DOMAIN, HmipLocalServices from .control_unit import ControlUnit from .support import get_device_address_at_interface_from_identifiers @@ -83,7 +64,7 @@ } ) -SCHEMA_SERVICE_CREATE_CENTRAL_LINKS = vol.All( +SCHEMA_CREATE_CENTRAL_LINKS = vol.All( cv.has_at_least_one_key(CONF_DEVICE_ID, CONF_ENTRY_ID), cv.has_at_most_one_key(CONF_DEVICE_ID, CONF_ENTRY_ID), vol.Schema( @@ -94,31 +75,31 @@ ), ) -SCHEMA_SERVICE_CLEAR_CACHE = vol.Schema( +SCHEMA_CLEAR_CACHE = vol.Schema( { vol.Required(CONF_ENTRY_ID): cv.string, } ) -SCHEMA_SERVICE_EXPORT_DEVICE_DEFINITION = vol.Schema( +SCHEMA_EXPORT_DEVICE_DEFINITION = vol.Schema( { vol.Required(CONF_DEVICE_ID): cv.string, } ) -SCHEMA_SERVICE_FETCH_SYSTEM_VARIABLES = vol.Schema( +SCHEMA_FETCH_SYSTEM_VARIABLES = vol.Schema( { vol.Required(CONF_ENTRY_ID): cv.string, } ) -SCHEMA_SERVICE_FORCE_DEVICE_AVAILABILITY = vol.All( +SCHEMA_FORCE_DEVICE_AVAILABILITY = 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, ) -SCHEMA_SERVICE_GET_DEVICE_VALUE = vol.All( +SCHEMA_GET_DEVICE_VALUE = 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( @@ -129,7 +110,7 @@ ), ) -SCHEMA_SERVICE_GET_LINK_PEERS = vol.All( +SCHEMA_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( @@ -139,14 +120,14 @@ ), ) -SCHEMA_SERVICE_GET_LINK_PARAMSET = vol.All( +SCHEMA_GET_LINK_PARAMSET = vol.All( { vol.Optional(CONF_RECEIVER_CHANNEL_ADDRESS): haval.channel_address, vol.Optional(CONF_SENDER_CHANNEL_ADDRESS): haval.channel_address, } ) -SCHEMA_SERVICE_GET_PARAMSET = vol.All( +SCHEMA_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), BASE_SCHEMA_DEVICE.extend( @@ -159,7 +140,7 @@ ), ) -SCHEMA_SERVICE_REMOVE_CENTRAL_LINKS = vol.All( +SCHEMA_REMOVE_CENTRAL_LINKS = vol.All( cv.has_at_least_one_key(CONF_DEVICE_ID, CONF_ENTRY_ID), cv.has_at_most_one_key(CONF_DEVICE_ID, CONF_ENTRY_ID), vol.Schema( @@ -170,7 +151,7 @@ ), ) -SCHEMA_SERVICE_SET_VARIABLE_VALUE = vol.Schema( +SCHEMA_SET_VARIABLE_VALUE = vol.Schema( { vol.Required(CONF_ENTRY_ID): cv.string, vol.Required(CONF_NAME): cv.string, @@ -178,7 +159,7 @@ } ) -SCHEMA_SERVICE_SET_INSTALL_MODE = vol.Schema( +SCHEMA_SET_INSTALL_MODE = vol.Schema( { vol.Required(CONF_INTERFACE_ID): cv.string, vol.Optional(CONF_TIME, default=60): cv.positive_int, @@ -187,7 +168,7 @@ } ) -SCHEMA_SERVICE_SET_DEVICE_VALUE = vol.All( +SCHEMA_SET_DEVICE_VALUE = 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( @@ -204,7 +185,7 @@ ), ) -SCHEMA_SERVICE_PUT_LINK_PARAMSET = vol.All( +SCHEMA_PUT_LINK_PARAMSET = vol.All( { vol.Optional(CONF_RECEIVER_CHANNEL_ADDRESS): haval.channel_address, vol.Optional(CONF_SENDER_CHANNEL_ADDRESS): haval.channel_address, @@ -213,7 +194,7 @@ } ) -SCHEMA_SERVICE_PUT_PARAMSET = vol.All( +SCHEMA_PUT_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), BASE_SCHEMA_DEVICE.extend( @@ -229,7 +210,7 @@ ), ) -SCHEMA_SERVICE_UPDATE_DEVICE_FIRMWARE_DATA = vol.Schema( +SCHEMA_UPDATE_DEVICE_FIRMWARE_DATA = vol.Schema( { vol.Required(CONF_ENTRY_ID): cv.string, } @@ -244,162 +225,162 @@ async def async_call_hmip_local_service(service: ServiceCall) -> ServiceResponse """Call correct Homematic(IP) Local service.""" service_name = service.service - if service_name == SERVICE_CREATE_CENTRAL_LINKS: + if service_name == HmipLocalServices.CREATE_CENTRAL_LINKS: await _async_service_create_central_link(hass=hass, service=service) - elif service_name == SERVICE_CLEAR_CACHE: + elif service_name == HmipLocalServices.CLEAR_CACHE: await _async_service_clear_cache(hass=hass, service=service) - elif service_name == SERVICE_EXPORT_DEVICE_DEFINITION: + elif service_name == HmipLocalServices.EXPORT_DEVICE_DEFINITION: await _async_service_export_device_definition(hass=hass, service=service) - elif service_name == SERVICE_FETCH_SYSTEM_VARIABLES: + elif service_name == HmipLocalServices.FETCH_SYSTEM_VARIABLES: await _async_service_fetch_system_variables(hass=hass, service=service) - elif service_name == SERVICE_FORCE_DEVICE_AVAILABILITY: + elif service_name == HmipLocalServices.FORCE_DEVICE_AVAILABILITY: await _async_service_force_device_availability(hass=hass, service=service) - elif service_name == SERVICE_GET_DEVICE_VALUE: + elif service_name == HmipLocalServices.GET_DEVICE_VALUE: return await _async_service_get_device_value(hass=hass, service=service) - elif service_name == SERVICE_GET_LINK_PEERS: + elif service_name == HmipLocalServices.GET_LINK_PEERS: return await _async_service_get_link_peers(hass=hass, service=service) - elif service_name == SERVICE_GET_LINK_PARAMSET: + elif service_name == HmipLocalServices.GET_LINK_PARAMSET: return await _async_service_get_link_paramset(hass=hass, service=service) - elif service_name == SERVICE_GET_PARAMSET: + elif service_name == HmipLocalServices.GET_PARAMSET: return await _async_service_get_paramset(hass=hass, service=service) - elif service_name == SERVICE_PUT_LINK_PARAMSET: + elif service_name == HmipLocalServices.PUT_LINK_PARAMSET: await _async_service_put_link_paramset(hass=hass, service=service) - elif service_name == SERVICE_PUT_PARAMSET: + elif service_name == HmipLocalServices.PUT_PARAMSET: await _async_service_put_paramset(hass=hass, service=service) - elif service_name == SERVICE_REMOVE_CENTRAL_LINKS: + elif service_name == HmipLocalServices.REMOVE_CENTRAL_LINKS: await _async_service_remove_central_link(hass=hass, service=service) - elif service_name == SERVICE_SET_INSTALL_MODE: + elif service_name == HmipLocalServices.SET_INSTALL_MODE: await _async_service_set_install_mode(hass=hass, service=service) - elif service_name == SERVICE_SET_DEVICE_VALUE: + elif service_name == HmipLocalServices.SET_DEVICE_VALUE: await _async_service_set_device_value(hass=hass, service=service) - elif service_name == SERVICE_SET_VARIABLE_VALUE: + elif service_name == HmipLocalServices.SET_VARIABLE_VALUE: await _async_service_set_variable_value(hass=hass, service=service) - elif service_name == SERVICE_UPDATE_DEVICE_FIRMWARE_DATA: + elif service_name == HmipLocalServices.UPDATE_DEVICE_FIRMWARE_DATA: await _async_service_update_device_firmware_data(hass=hass, service=service) return None async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_CREATE_CENTRAL_LINKS, + service=HmipLocalServices.CREATE_CENTRAL_LINKS, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_CREATE_CENTRAL_LINKS, + schema=SCHEMA_CREATE_CENTRAL_LINKS, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_CLEAR_CACHE, + service=HmipLocalServices.CLEAR_CACHE, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_CLEAR_CACHE, + schema=SCHEMA_CLEAR_CACHE, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_EXPORT_DEVICE_DEFINITION, + service=HmipLocalServices.EXPORT_DEVICE_DEFINITION, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_EXPORT_DEVICE_DEFINITION, + schema=SCHEMA_EXPORT_DEVICE_DEFINITION, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_FETCH_SYSTEM_VARIABLES, + service=HmipLocalServices.FETCH_SYSTEM_VARIABLES, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_FETCH_SYSTEM_VARIABLES, + schema=SCHEMA_FETCH_SYSTEM_VARIABLES, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_FORCE_DEVICE_AVAILABILITY, + service=HmipLocalServices.FORCE_DEVICE_AVAILABILITY, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_FORCE_DEVICE_AVAILABILITY, + schema=SCHEMA_FORCE_DEVICE_AVAILABILITY, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_GET_DEVICE_VALUE, + service=HmipLocalServices.GET_DEVICE_VALUE, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_GET_DEVICE_VALUE, + schema=SCHEMA_GET_DEVICE_VALUE, supports_response=SupportsResponse.OPTIONAL, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_GET_LINK_PEERS, + service=HmipLocalServices.GET_LINK_PEERS, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_GET_LINK_PEERS, + schema=SCHEMA_GET_LINK_PEERS, supports_response=SupportsResponse.OPTIONAL, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_GET_LINK_PARAMSET, + service=HmipLocalServices.GET_LINK_PARAMSET, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_GET_LINK_PARAMSET, + schema=SCHEMA_GET_LINK_PARAMSET, supports_response=SupportsResponse.OPTIONAL, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_GET_PARAMSET, + service=HmipLocalServices.GET_PARAMSET, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_GET_PARAMSET, + schema=SCHEMA_GET_PARAMSET, supports_response=SupportsResponse.OPTIONAL, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_REMOVE_CENTRAL_LINKS, + service=HmipLocalServices.REMOVE_CENTRAL_LINKS, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_REMOVE_CENTRAL_LINKS, + schema=SCHEMA_REMOVE_CENTRAL_LINKS, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_SET_VARIABLE_VALUE, + service=HmipLocalServices.SET_VARIABLE_VALUE, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_SET_VARIABLE_VALUE, + schema=SCHEMA_SET_VARIABLE_VALUE, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_SET_DEVICE_VALUE, + service=HmipLocalServices.SET_DEVICE_VALUE, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_SET_DEVICE_VALUE, + schema=SCHEMA_SET_DEVICE_VALUE, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_SET_INSTALL_MODE, + service=HmipLocalServices.SET_INSTALL_MODE, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_SET_INSTALL_MODE, + schema=SCHEMA_SET_INSTALL_MODE, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_PUT_LINK_PARAMSET, + service=HmipLocalServices.PUT_LINK_PARAMSET, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_PUT_LINK_PARAMSET, + schema=SCHEMA_PUT_LINK_PARAMSET, ) hass.services.async_register( domain=DOMAIN, - service=SERVICE_PUT_PARAMSET, + service=HmipLocalServices.PUT_PARAMSET, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_PUT_PARAMSET, + schema=SCHEMA_PUT_PARAMSET, ) async_register_admin_service( hass=hass, domain=DOMAIN, - service=SERVICE_UPDATE_DEVICE_FIRMWARE_DATA, + service=HmipLocalServices.UPDATE_DEVICE_FIRMWARE_DATA, service_func=async_call_hmip_local_service, - schema=SCHEMA_SERVICE_UPDATE_DEVICE_FIRMWARE_DATA, + schema=SCHEMA_UPDATE_DEVICE_FIRMWARE_DATA, ) @@ -408,7 +389,7 @@ async def async_unload_services(hass: HomeAssistant) -> None: if len(async_get_loaded_config_entries(hass=hass)) > 0: return - for hmip_local_service in HMIP_LOCAL_SERVICES: + for hmip_local_service in HmipLocalServices: hass.services.async_remove(domain=DOMAIN, service=hmip_local_service) diff --git a/custom_components/homematicip_local/siren.py b/custom_components/homematicip_local/siren.py index 8443a365..daabf1a4 100644 --- a/custom_components/homematicip_local/siren.py +++ b/custom_components/homematicip_local/siren.py @@ -23,7 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomematicConfigEntry -from .const import SERVICE_TURN_ON_SIREN +from .const import HmipLocalServices from .control_unit import ControlUnit, signal_new_data_point from .generic_entity import HaHomematicGenericRestoreEntity @@ -56,7 +56,7 @@ def async_add_siren(data_points: tuple[BaseCustomDpSiren, ...]) -> None: platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( - SERVICE_TURN_ON_SIREN, + HmipLocalServices.TURN_ON_SIREN, { vol.Optional(ATTR_TONE): cv.string, vol.Optional(ATTR_LIGHT): cv.string, diff --git a/custom_components/homematicip_local/strings.json b/custom_components/homematicip_local/strings.json index 24d09c0e..4369130c 100644 --- a/custom_components/homematicip_local/strings.json +++ b/custom_components/homematicip_local/strings.json @@ -564,6 +564,10 @@ } }, "issues": { + "fetch_data": { + "description": "Unable to fetch data using a rega script.\n\nThis indicates an issue on your CCU.\n\nUsing fallback, that leeds to a higher DutyCycle during Integration startup for interface {interface_id}", + "title": "Error fetching initial data" + }, "interface_not_reachable": { "description": "No connection to interface {interface_id}.\n\nThis Integration tries an automatic reconnect.\n\nCheck the Home Assistant log files for more details", "title": "Interface {interface_id} not reachable" diff --git a/custom_components/homematicip_local/support.py b/custom_components/homematicip_local/support.py index 43eaa6cc..b36dd523 100644 --- a/custom_components/homematicip_local/support.py +++ b/custom_components/homematicip_local/support.py @@ -6,7 +6,7 @@ import logging from typing import Any, TypeAlias, TypeVar, cast -from hahomematic.const import EVENT_CHANNEL_NO, EVENT_PARAMETER, EVENT_VALUE, IDENTIFIER_SEPARATOR +from hahomematic.const import IDENTIFIER_SEPARATOR, EventKey from hahomematic.model.custom import CustomDataPoint from hahomematic.model.data_point import EVENT_DATA_SCHEMA, CallbackDataPoint from hahomematic.model.generic import GenericDataPoint @@ -46,9 +46,9 @@ { vol.Required(CONF_TYPE): str, vol.Required(CONF_SUBTYPE): int, - vol.Remove(EVENT_CHANNEL_NO): int, - vol.Remove(EVENT_PARAMETER): str, - vol.Remove(EVENT_VALUE): vol.Any(bool, int), + vol.Remove(str(EventKey.CHANNEL_NO)): int, + vol.Remove(str(EventKey.PARAMETER)): str, + vol.Remove(str(EventKey.VALUE)): vol.Any(bool, int), }, extra=vol.ALLOW_EXTRA, ) @@ -75,17 +75,18 @@ _LOGGER = logging.getLogger(__name__) -def cleanup_click_event_data(event_data: dict[str, Any]) -> dict[str, Any]: +def cleanup_click_event_data(event_data: dict[Any, Any]) -> dict[str, Any]: """Cleanup the click_event.""" - event_data.update( + cleand_event_data = {str(key): value for key, value in event_data.items()} + cleand_event_data.update( { - CONF_TYPE: event_data[EVENT_PARAMETER].lower(), - CONF_SUBTYPE: event_data[EVENT_CHANNEL_NO], + CONF_TYPE: cleand_event_data[EventKey.PARAMETER].lower(), + CONF_SUBTYPE: cleand_event_data[EventKey.CHANNEL_NO], } ) - del event_data[EVENT_PARAMETER] - del event_data[EVENT_CHANNEL_NO] - return event_data + del cleand_event_data[EventKey.PARAMETER] + del cleand_event_data[EventKey.CHANNEL_NO] + return cleand_event_data def is_valid_event(event_data: Mapping[str, Any], schema: vol.Schema) -> bool: diff --git a/custom_components/homematicip_local/switch.py b/custom_components/homematicip_local/switch.py index f93ddf45..33db51a4 100644 --- a/custom_components/homematicip_local/switch.py +++ b/custom_components/homematicip_local/switch.py @@ -19,7 +19,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomematicConfigEntry -from .const import SERVICE_SWITCH_SET_ON_TIME +from .const import HmipLocalServices from .control_unit import ControlUnit, signal_new_data_point from .generic_entity import HaHomematicGenericRestoreEntity, HaHomematicGenericSysvarEntity @@ -92,7 +92,7 @@ def async_add_hub_switch(data_points: tuple[SysvarDpSwitch, ...]) -> None: platform = entity_platform.async_get_current_platform() platform.async_register_entity_service( - SERVICE_SWITCH_SET_ON_TIME, + HmipLocalServices.SWITCH_SET_ON_TIME, { vol.Required(ATTR_ON_TIME): vol.All(vol.Coerce(int), vol.Range(min=0, max=8580000)), }, diff --git a/custom_components/homematicip_local/translations/de.json b/custom_components/homematicip_local/translations/de.json index fa23a3ea..c928f27b 100644 --- a/custom_components/homematicip_local/translations/de.json +++ b/custom_components/homematicip_local/translations/de.json @@ -568,6 +568,10 @@ } }, "issues": { + "fetch_data": { + "description": "Daten können mit einem Rega-Skript nicht abgerufen werden.\n\nDies weist auf ein Problem mit Ihrer CCU hin.\n\nDurch die Verwendung eines Fallbacks kommt es zu einem erhöhten DutyCycle beim Integrationsstart für die Schnittstelle {interface_id}", + "title": "Fehler beim Abrufen der Anfangsdaten" + }, "interface_not_reachable": { "description": "Keine Verbindung zu Schnittstelle {interface_id}.\n\nDiese Integration versucht eine automatische Wiederverbindung.\n\nWeitere Informationen finden Sie in den Protokolldateien von Home Assistant", "title": "Interface {interface_id} nicht erreichbar" diff --git a/custom_components/homematicip_local/translations/en.json b/custom_components/homematicip_local/translations/en.json index 24d09c0e..4369130c 100644 --- a/custom_components/homematicip_local/translations/en.json +++ b/custom_components/homematicip_local/translations/en.json @@ -564,6 +564,10 @@ } }, "issues": { + "fetch_data": { + "description": "Unable to fetch data using a rega script.\n\nThis indicates an issue on your CCU.\n\nUsing fallback, that leeds to a higher DutyCycle during Integration startup for interface {interface_id}", + "title": "Error fetching initial data" + }, "interface_not_reachable": { "description": "No connection to interface {interface_id}.\n\nThis Integration tries an automatic reconnect.\n\nCheck the Home Assistant log files for more details", "title": "Interface {interface_id} not reachable" diff --git a/requirements_test.txt b/requirements_test.txt index fe585f89..3ee7b8c2 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,7 +1,7 @@ -r requirements_test_pre_commit.txt async-upnp-client==0.41.0 -hahomematic==2024.10.16 +hahomematic==2024.10.17 homeassistant==2024.10.3 mypy==1.12.0 mypy-dev==1.11.0a9 diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 30152a4c..0dc6ad36 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -6,7 +6,7 @@ from unittest.mock import patch from hahomematic.const import CONF_PASSWORD, CONF_USERNAME, InterfaceName, SystemInformation -from hahomematic.exceptions import AuthFailure, NoConnection +from hahomematic.exceptions import AuthFailure, NoConnectionException import pytest from pytest_homeassistant_custom_component.common import MockConfigEntry @@ -495,7 +495,7 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None: with ( patch( "custom_components.homematicip_local.config_flow._async_validate_config_and_get_system_information", - side_effect=NoConnection("no host"), + side_effect=NoConnectionException("no host"), ), patch( "custom_components.homematicip_local.async_setup_entry", @@ -546,7 +546,7 @@ async def test_options_form_cannot_connect( with ( patch( "custom_components.homematicip_local.config_flow._async_validate_config_and_get_system_information", - side_effect=NoConnection("no host"), + side_effect=NoConnectionException("no host"), ), patch( "custom_components.homematicip_local.async_setup_entry", diff --git a/tests/test_recorder.py b/tests/test_recorder.py index f9566dd8..f7c89324 100644 --- a/tests/test_recorder.py +++ b/tests/test_recorder.py @@ -2,7 +2,7 @@ from __future__ import annotations -from hahomematic.const import EVENT_ADDRESS, EVENT_INTERFACE_ID, DeviceFirmwareState +from hahomematic.const import DeviceFirmwareState, EventKey import pytest from pytest_homeassistant_custom_component.components.recorder.common import ( async_wait_recording_done, @@ -92,8 +92,8 @@ async def no_test_event_entity_un_recorded( hass=hass, control=control, entity_id=entity_id, entity_name=entity_name ) assert ha_state.state == STATE_UNKNOWN - assert ha_state.attributes[EVENT_ADDRESS] == "VCU2128127:1" - assert ha_state.attributes[EVENT_INTERFACE_ID] == "CentralTest-BidCos-RF" + assert ha_state.attributes[EventKey.ADDRESS] == "VCU2128127:1" + assert ha_state.attributes[EventKey.INTERFACE_ID] == "CentralTest-BidCos-RF" assert ha_state.attributes[EVENT_MODEL] == "HmIP-BSM" await async_wait_recording_done(hass) @@ -109,8 +109,8 @@ async def no_test_event_entity_un_recorded( for entity_states in states.values(): for state in entity_states: if state.entity_id == entity_id: - assert EVENT_ADDRESS not in state.attributes - assert EVENT_INTERFACE_ID not in state.attributes + assert EventKey.ADDRESS not in state.attributes + assert EventKey.INTERFACE_ID not in state.attributes assert EVENT_MODEL not in state.attributes break