diff --git a/changelog.txt b/changelog.txt index bf8693c2..9d430b19 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,13 @@ +Version 0.0.12 (2021-11-27) +- Add more type converter +- Move get_tls_context to helper +- Update requirements +- Cleanup constants +- Use flags from parameter_data +- Add wildcard start to exclude parameters that start with word +- Fix channel assignement for dimmers +- Fix entity name: add channel only if a parameter name exists is in multiple channels of the device. + Version 0.0.11 (2021-11-26) - Fix: cover open/close default values to float - Fix: add missing async/await diff --git a/hahomematic/central_unit.py b/hahomematic/central_unit.py index 095b81dc..fee5387a 100644 --- a/hahomematic/central_unit.py +++ b/hahomematic/central_unit.py @@ -20,10 +20,7 @@ DATA_NO_LOAD, DATA_NO_SAVE, DATA_SAVE_SUCCESS, - DEFAULT_CONNECT, DEFAULT_ENCODING, - DEFAULT_JSON_PORT, - DEFAULT_NAME, DEFAULT_PASSWORD, DEFAULT_TLS, DEFAULT_USERNAME, @@ -63,7 +60,6 @@ def __init__(self, central_config): self.enable_virtual_channels = self.central_config.enable_virtual_channels self.host = self.central_config.host self.json_port = self.central_config.json_port - self.connect = self.central_config.connect self.password = self.central_config.password if self.password is None: self.username = None @@ -76,6 +72,8 @@ def __init__(self, central_config): # Caches for CCU data # {interface_id, {address, paramsets}} self.paramsets_cache = {} + + self.address_parameter_cache = {} # {interface_id, {address, name}} self.names_cache = {} # {interface_id, {counter, device}} @@ -112,6 +110,7 @@ def __init__(self, central_config): INSTANCES[self.instance_name] = self self._load_caches() + self.init_address_parameter_list() self._connection_checker = ConnectionChecker(self) self.hub = None @@ -126,6 +125,33 @@ async def init_hub(self): else: self.hub = HmDummyHub(self) + def init_address_parameter_list(self): + """Initialize an address/parameter list to identify if a parameter name exists is in multiple channels.""" + for device_paramsets in self.paramsets_cache.values(): + for address, paramsets in device_paramsets.items(): + if ":" not in address: + continue + d_address = address.split(":")[0] + p_channel = address.split(":")[1] + + for paramset in paramsets.values(): + for parameter in paramset: + if (d_address, parameter) not in self.address_parameter_cache: + self.address_parameter_cache[(d_address, parameter)] = [] + self.address_parameter_cache[(d_address, parameter)].append( + p_channel + ) + + def has_multiple_channels(self, address, parameter) -> bool: + """Check if parameter is in multiple channels per device.""" + if ":" not in address: + return False + d_address = address.split(":")[0] + channels = self.address_parameter_cache.get((d_address, parameter)) + if channels: + return len(set(channels)) > 1 + return False + @property def version(self): """Return the version of the backend.""" @@ -491,6 +517,7 @@ def _save_paramsets(): json.dump(self.paramsets_cache, fptr) return DATA_SAVE_SUCCESS + self.init_address_parameter_list() return await self.async_add_executor_job(_save_paramsets) def load_paramsets(self): @@ -674,17 +701,16 @@ def __init__( entry_id, loop, xml_rpc_server: xml_rpc.XMLRPCServer, - name=DEFAULT_NAME, + name, host=LOCALHOST, username=DEFAULT_USERNAME, password=DEFAULT_PASSWORD, tls=DEFAULT_TLS, verify_tls=DEFAULT_VERIFY_TLS, client_session=None, - connect=DEFAULT_CONNECT, callback_host=None, callback_port=None, - json_port=DEFAULT_JSON_PORT, + json_port=None, json_tls=DEFAULT_TLS, enable_virtual_channels=False, enable_sensors_for_own_system_variables=False, @@ -699,7 +725,6 @@ def __init__( self.tls = tls self.verify_tls = verify_tls self.client_session = client_session - self.connect = connect self.callback_host = callback_host self.callback_port = callback_port self.json_port = json_port diff --git a/hahomematic/client.py b/hahomematic/client.py index 04e726b0..cc1e2eb6 100644 --- a/hahomematic/client.py +++ b/hahomematic/client.py @@ -23,7 +23,6 @@ BACKEND_CCU, BACKEND_HOMEGEAR, BACKEND_PYDEVCCU, - DEFAULT_NAME, DEFAULT_PATH, HM_VIRTUAL_REMOTES, PORT_RFD, @@ -31,7 +30,6 @@ PROXY_DE_INIT_SKIPPED, PROXY_DE_INIT_SUCCESS, PROXY_INIT_FAILED, - PROXY_INIT_SKIPPED, PROXY_INIT_SUCCESS, RELEVANT_PARAMSETS, ) @@ -128,9 +126,6 @@ async def proxy_init(self) -> int: To receive events the proxy has to tell the CCU / Homegear where to send the events. For that we call the init-method. """ - if not self.central.connect: - _LOGGER.debug("proxy_init: Skipping init for %s", self.name) - return PROXY_INIT_SKIPPED if self.central is None: _LOGGER.warning("proxy_init: Local central_unit missing for %s", self.name) self.time_initialized = 0 @@ -156,9 +151,6 @@ async def proxy_de_init(self) -> int: """ if self.json_rpc_session.is_activated: await self.json_rpc_session.logout() - if not self.central.connect: - _LOGGER.debug("proxy_de_init: Skipping de-init for %s", self.name) - return PROXY_DE_INIT_SKIPPED if self.central is None: _LOGGER.warning("proxy_de_init: Local central missing for %s", self.name) return PROXY_DE_INIT_FAILED @@ -685,7 +677,7 @@ class ClientConfig: def __init__( self, central, - name=DEFAULT_NAME, + name, port=PORT_RFD, path=DEFAULT_PATH, callback_host=None, diff --git a/hahomematic/const.py b/hahomematic/const.py index e8293888..3fe7af64 100644 --- a/hahomematic/const.py +++ b/hahomematic/const.py @@ -1,8 +1,6 @@ """ Constants used by hahomematic. """ - -VERSION = "0.0.3" DEFAULT_ENCODING = "UTF-8" LOCALHOST = "localhost" @@ -12,24 +10,35 @@ IP_ANY_V6 = "::" PORT_ANY = 0 -PORT_REGA_HSS = 1999 -PORT_REGA_HSS_TLS = 41999 +PORT_GROUPS_NAME = "groups" +PORT_GROUPS = 9292 +PORT_GROUPS_TLS = 49292 +PORT_HMIP_NAME = "hmip" +PORT_HMIP = 2010 +PORT_HMIP_TLS = 42010 +PORT_HS485D_NAME = "hs485d" PORT_HS485D = 2000 PORT_HS485D_TLS = 42000 +PORT_RFD_NAME = "rfd" PORT_RFD = 2001 PORT_RFD_TLS = 42001 -PORT_HMIP = 2010 -PORT_HMIP_TLS = 42010 -PORT_GROUPS = 9292 -PORT_GROUPS_TLS = 49292 -PORT_JSON_RPC = 80 -PORT_JSON_RPC_TLS = 443 + +PORT_REGA_HSS_NAME = "rega hss" +PORT_REGA_HSS = 1999 +PORT_REGA_HSS_TLS = 41999 +PORT_REGA_SCRIPT_NAME = "rega script" PORT_REGA_SCRIPT = 8181 PORT_REGA_SCRIPT_TLS = 48181 +DEFAULT_PORTS = { + PORT_RFD_NAME: (PORT_RFD, PORT_RFD_TLS), + PORT_HMIP_NAME: (PORT_HMIP, PORT_HMIP_TLS), + PORT_GROUPS_NAME: (PORT_GROUPS, PORT_GROUPS_TLS), + PORT_HS485D_NAME: (PORT_HS485D, PORT_HS485D_TLS), +} + PRIMARY_PORTS = [PORT_HMIP, PORT_HMIP_TLS, PORT_RFD, PORT_RFD_TLS] -JSON_RPC_PATH = "/api/homematic.cgi" PATH_JSON_RPC = "/api/homematic.cgi" PATH_TCL_REGA = "/tclrega.exe" @@ -50,8 +59,6 @@ HH_EVENT_DELETE_DEVICES = "deleteDevices" HH_EVENT_DEVICES_CREATED = "devicesCreated" -HH_EVENT_ENTITIES_CREATED = "entitiesCreated" -HH_EVENT_ENTITY_CREATED = "entityCreated" HH_EVENT_ERROR = "error" HH_EVENT_LIST_DEVICES = "listDevices" HH_EVENT_NEW_DEVICES = "newDevices" @@ -128,23 +135,12 @@ "DEVICE_IN_BOOTLOADER", "DEW_POINT_ALARM", "EMERGENCY_OPERATION", - "ERROR_CODE", - "ERROR_DEGRADED_CHAMBER", - "ERROR_NON_FLAT_POSITIONING", - "ERROR_OVERLOAD", - "ERROR_POWER_FAILURE", - "ERROR_REDUCED", - "ERROR_UNDERVOLTAGE", - "ERROR_UPDATE", - "ERROR_WIND_COMMUNICATION", - "ERROR_WIND_NORTH", "EXTERNAL_CLOCK", "FROST_PROTECTION", "HEATING_COOLING", "HUMIDITY_LIMITER", "INCLUSION_UNSUPPORTED_DEVICE", "INHIBIT", - "INSTALL_TEST", "INSTALL_MODE", "PARTY_SET_POINT_TEMPERATURE", "PARTY_TIME_END", @@ -152,10 +148,6 @@ "PROCESS", "QUICK_VETO_TIME", "SECTION", - "STATUS_FLAG_ERROR", - "STATUS_FLAG_LOW_BAT", - "STATUS_FLAG_PLAYING_FILE_ACTIVE", - "STATUS_FLAG_PLAYLIST_ACTIVE", "STICKY_UNREACH", "SWITCH_POINT_OCCURED", "TEMPERATURE_LIMITER", @@ -166,7 +158,7 @@ "WOCHENPROGRAMM", ] -IGNORED_PARAMETERS_WILDCARDS = [ +IGNORED_PARAMETERS_WILDCARDS_END = [ "OVERFLOW", "OVERHEAT", "OVERRUN", @@ -174,6 +166,10 @@ "STATUS", "WORKING", ] +IGNORED_PARAMETERS_WILDCARDS_START = [ + "ERROR", + "STATUS_FLAG", +] HIDDEN_PARAMETERS = [PARAM_UN_REACH, PARAM_CONFIG_PENDING] @@ -181,10 +177,8 @@ BACKEND_HOMEGEAR = "Homegear" BACKEND_PYDEVCCU = "PyDevCCU" - PROXY_INIT_FAILED = 0 PROXY_INIT_SUCCESS = 1 -PROXY_INIT_SKIPPED = 2 PROXY_DE_INIT_FAILED = 4 PROXY_DE_INIT_SUCCESS = 8 PROXY_DE_INIT_SKIPPED = 16 @@ -200,45 +194,32 @@ ATTR_CALLBACK_HOST = "callback_host" ATTR_CALLBACK_PORT = "callback_port" ATTR_CHANNELS = "channels" -ATTR_CONNECT = "connect" ATTR_ERROR = "error" ATTR_HOST = "host" -ATTR_ID = "id" ATTR_INTERFACE = "interface" ATTR_INTERFACE_ID = "interface_id" ATTR_IP = "ip" -ATTR_JSON = "json" ATTR_JSON_PORT = "json_port" -ATTR_METADATA = "metadata" ATTR_NAME = "name" ATTR_PASSWORD = "password" ATTR_PARAMETER = "parameter" -ATTR_PARAMSET_KEY = "paramset_key" -ATTR_PARAMSET = "paramset" -ATTR_RX_MODE = "rx_mode" -ATTR_PATH = "path" ATTR_PORT = "port" -ATTR_RESOLVE_NAMES = "resolve_names" ATTR_RESULT = "result" ATTR_SESSION_ID = "_session_id_" ATTR_TLS = "tls" ATTR_TYPE = "type" -ATTR_UNIQUE_ID = "unique_id" ATTR_USERNAME = "username" ATTR_VALUE = "value" -ATTR_VALUE_TYPE = "value_type" ATTR_VERIFY_TLS = "verify_tls" ATTR_HM_ALARM = "ALARM" ATTR_HM_ADDRESS = "ADDRESS" -ATTR_HM_CONTROL = "CONTROL" ATTR_HM_DEFAULT = "DEFAULT" ATTR_HM_FIRMWARE = "FIRMWARE" +ATTR_HM_FLAGS = "FLAGS" ATTR_HM_OPERATIONS = "OPERATIONS" ATTR_HM_PARAMSETS = "PARAMSETS" ATTR_HM_TYPE = "TYPE" -ATTR_HM_PARENT_TYPE = "PARENT_TYPE" -ATTR_HM_VERSION = "VERSION" ATTR_HM_LIST = "LIST" ATTR_HM_LOGIC = "LOGIC" ATTR_HM_NAME = "NAME" @@ -247,10 +228,8 @@ ATTR_HM_UNIT = "UNIT" ATTR_HM_MAX = "MAX" ATTR_HM_MIN = "MIN" -ATTR_HM_DEFAULT = "DEFAULT" # Optional member for TYPE: FLOAT, INTEGER ATTR_HM_SPECIAL = "SPECIAL" # Which has the following keys -ATTR_HM_ID = "ID" # String ATTR_HM_VALUE = "VALUE" # Float or integer, depending on TYPE # Members for ENUM ATTR_HM_VALUE_LIST = "VALUE_LIST" @@ -269,26 +248,18 @@ FLAG_VISIBLE = 1 FLAG_INTERAL = 2 -FLAG_TRANSFORM = 4 +# FLAG_TRANSFORM = 4 # not used FLAG_SERVICE = 8 -FLAG_STICKY = 10 # This might be wrong. Documentation says 0x10 +# FLAG_STICKY = 10 # This might be wrong. Documentation says 0x10 # not used -DEFAULT_CALLBACK_HOST = None -DEFAULT_CALLBACK_PORT = None -DEFAULT_CONNECT = True -DEFAULT_JSON_PORT = 80 -DEFAULT_NAME = "default" DEFAULT_PASSWORD = None DEFAULT_PATH = None DEFAULT_USERNAME = "Admin" -DEFAULT_REMOTE = "default" -DEFAULT_RESOLVE_NAMES = False DEFAULT_TIMEOUT = 30 DEFAULT_INIT_TIMEOUT = 90 DEFAULT_TLS = False DEFAULT_VERIFY_TLS = False - HA_PLATFORM_ACTION = "action" HA_PLATFORM_BINARY_SENSOR = "binary_sensor" HA_PLATFORM_BUTTON = "button" @@ -321,182 +292,3 @@ HM_VIRTUAL_REMOTE_HM = "BidCoS-RF" HM_VIRTUAL_REMOTE_HMIP = "HmIP-RCV-1" HM_VIRTUAL_REMOTES = [HM_VIRTUAL_REMOTE_HM, HM_VIRTUAL_REMOTE_HMIP] - -hm = [ - "ACCESS_AUTHORIZATION", - "ADAPTION_DRIVE", - "ADJUSTING_COMMAND", - "ADJUSTING_DATA", - "ALARM_COUNT", - "ALL_LEDS", - "ARMSTATE", - "ARROW_DOWN", - "ARROW_UP", - "BACKLIGHT", - "BATTERY_STATE", - "BEEP", - "BELL", - "BLIND", - "BOOST_STATE", - "BULB", - "CLEAR_WINDOW_OPEN_SYMBOL", - "CLOCK", - "COLOR_LIST_1", - "COLOR_LIST_10", - "COLOR_LIST_11", - "COLOR_LIST_12", - "COLOR_LIST_2", - "COLOR_LIST_3", - "COLOR_LIST_4", - "COLOR_LIST_5", - "COLOR_LIST_6", - "COLOR_LIST_7", - "COLOR_LIST_8", - "COLOR_LIST_9", - "COMBINED_PARAMETER", - "COMMUNICATION_REPORTING", - "CONTROL_DIFFERENTIAL_TEMPERATURE", - "COUNTERREADING10", - "DATE_TIME_UNKNOWN", - "DECISION_VALUE", - "DIRECTION", - "DIRECTION_SLATS", - "DURATION_UNIT", - "DURATION_VALUE", - "ENTER_BOOTLOADER", - "ERROR", - "ERROR_ALARM_TEST", - "ERROR_BATTERY", - "ERROR_M1", - "ERROR_M2", - "ERROR_M3", - "ERROR_OVERHEAT", - "ERROR_POWER", - "ERROR_SABOTAGE", - "ERROR_SMOKE_CHAMBER", - "ERROR_UNDERVOLTAGE", - "ERR_DETECT_EIA485_SERVICE", - "ERR_TTCU_INTERNAL_TEST", - "ERR_TTCU_LOCK_ROLLERS_SHORTED", - "ERR_TTCU_MAGNET_ERROR", - "ERR_TTCU_POWER_ONTIME_EXCEEDED", - "ERR_TTCU_SENSOR_STRIP_DISCONNECTED", - "ERR_TTCU_SENSOR_STRIP_SHORTED", - "ERR_TTCU_STOP_AFTER_10_CLOSING_TRIES", - "ERR_TTCU_TURN_TILT_ACT_ALLOY_MOSFET", - "ERR_TTCU_TURN_TILT_ACT_ASYNCHRON", - "ERR_TTCU_TURN_TILT_ACT_BLOCKED", - "ERR_TTCU_TURN_TILT_ACT_CONTACT_PROBLEM", - "ERR_TTCU_TURN_TILT_ACT_NO_SPEED_SIGNAL", - "ERR_TTCU_TURN_TILT_ACT_OVERCURRENT", - "ERR_TTCU_TURN_TILT_ACT_SHORTED", - "ERR_TTCU_WRONG_VOLTAGE_POLARITY", - "ERR_TTM_INTERNAL", - "ERR_TTM_OVERVOLT", - "ERR_TTM_UNDERVOLT", - "ERR_WINDOW_NOT_FOUND", - "ERR_WIN_STAY_IN_INITIAL_OPERATION", - "EXTERNAL_CLOCK", - "FAULT_REPORTING", - "FREE_TO_USE", - "GAS_POWER", - "HANDLE_LED_MODE", - "HANDLE_LOCK", - "HEATER_STATE", - "HOLIDAY_MODE", - "HUMIDITY_LIMITER", - "IDENTIFICATION_MODE_KEY_VISUAL", - "IDENTIFICATION_MODE_LCD_BACKLIGHT", - "IDENTIFY_DURATION", - "IDENTIFY_TARGET_LEVEL", - "INACTIVE_MODE", - "INHIBIT", - "INSTALL_MODE", - "LOWBAT_REPORTING", - "LOWBAT_SENSOR", - "MY_HUMIDITY", - "NEXT_TRANSMISSION", - "OLD_LEVEL", - "ON_TIME", - "ON_TIME_LIST_1", - "ON_TIME_LIST_10", - "ON_TIME_LIST_11", - "ON_TIME_LIST_12", - "ON_TIME_LIST_2", - "ON_TIME_LIST_3", - "ON_TIME_LIST_4", - "ON_TIME_LIST_5", - "ON_TIME_LIST_6", - "ON_TIME_LIST_7", - "ON_TIME_LIST_8", - "ON_TIME_LIST_9", - "OUTPUT_SELECT_SIZE", - "PARTY_MODE_SUBMIT", - "PARTY_START_DAY", - "PARTY_START_MONTH", - "PARTY_START_TIME", - "PARTY_START_YEAR", - "PARTY_STOP_DAY", - "PARTY_STOP_MONTH", - "PARTY_STOP_TIME", - "PARTY_STOP_YEAR", - "PARTY_TEMPERATURE", - "PHONE", - "PRESENCE_DETECTION_ACTIVE", - "PRESENCE_DETECTION_STATE", - "RELEASE_TURN", - "RELOCK_DELAY", - "REPETITIONS", - "RESET_MOTION", - "RESET_PRESENCE", - "SCENE", - "SECURE_STATE", - "SELF_CALIBRATION", - "SELF_CALIBRATION_RESULT", - "SEQUENCE_OK", - "SERVICE_COUNT", - "SET_SYMBOL_FOR_HEATING_PHASE", - "SHADING_SPEED", - "SHEV_POS", - "SMOKE_DETECTOR_COMMAND", - "SMOKE_DETECTOR_TEST_RESULT", - "SOUNDFILE", - "SOUNDFILE_LIST_1", - "SOUNDFILE_LIST_10", - "SOUNDFILE_LIST_11", - "SOUNDFILE_LIST_12", - "SOUNDFILE_LIST_2", - "SOUNDFILE_LIST_3", - "SOUNDFILE_LIST_4", - "SOUNDFILE_LIST_5", - "SOUNDFILE_LIST_6", - "SOUNDFILE_LIST_7", - "SOUNDFILE_LIST_8", - "SOUNDFILE_LIST_9", - "STATE_UNCERTAIN", - "STATUS_FLAG_ERROR", - "STATUS_FLAG_LOW_BAT", - "STATUS_FLAG_PLAYING_FILE_ACTIVE", - "STATUS_FLAG_PLAYLIST_ACTIVE", - "STICKY_BATTERY", - "STICKY_ERR_TTM_OVERVOLT", - "STICKY_ERR_TTM_UNDERVOLT", - "STICKY_POWER", - "STICKY_SABOTAGE", - "SUBMIT", - "TEMPERATURE_LIMITER", - "TEXT", - "TIPTRONIC_STATE", - "USER_PROGRAM", - "VALVE_ADAPTION", - "WEEK_PROGRAM_TARGET_CHANNEL_LOCK", - "WEEK_PROGRAM_TARGET_CHANNEL_LOCKS", - "WINDOW_OPEN_REPORTING", - "WINDOW_TYPE", - "WINTER_MODE", - "WIN_RELEASE", - "WIN_RELEASE_ACT", - "WORKING", - "WORKING_SLATS", - "WP_OPTIONS", -] diff --git a/hahomematic/device.py b/hahomematic/device.py index 0606271d..8cc1fbdb 100644 --- a/hahomematic/device.py +++ b/hahomematic/device.py @@ -10,14 +10,17 @@ from hahomematic.const import ( ALARM_EVENTS, ATTR_HM_FIRMWARE, + ATTR_HM_FLAGS, ATTR_HM_OPERATIONS, ATTR_HM_TYPE, CLICK_EVENTS, + FLAG_INTERAL, HA_DOMAIN, HH_EVENT_DEVICES_CREATED, HM_VIRTUAL_REMOTES, IGNORED_PARAMETERS, - IGNORED_PARAMETERS_WILDCARDS, + IGNORED_PARAMETERS_WILDCARDS_END, + IGNORED_PARAMETERS_WILDCARDS_START, IMPULSE_EVENTS, OPERATION_EVENT, OPERATION_WRITE, @@ -251,9 +254,9 @@ def create_entities(self) -> set[GenericEntity] | None: if ( not parameter_data[ATTR_HM_OPERATIONS] & OPERATION_EVENT and not parameter_data[ATTR_HM_OPERATIONS] & OPERATION_WRITE - ): + ) or parameter_data[ATTR_HM_FLAGS] & FLAG_INTERAL: _LOGGER.debug( - "Device.create_entities: Skipping %s (no event)", + "Device.create_entities: Skipping %s (no event or internal)", parameter, ) continue @@ -333,7 +336,7 @@ def create_event(self, address, parameter, parameter_data) -> BaseEvent | None: ) _LOGGER.debug( - "create_event: Creating action_event for %s, %s, %s", + "create_event: Creating event for %s, %s, %s", address, parameter, self.interface_id, @@ -375,9 +378,9 @@ def create_entity(self, address, parameter, parameter_data) -> GenericEntity | N """ if ( parameter in IGNORED_PARAMETERS - or parameter.endswith(tuple(IGNORED_PARAMETERS_WILDCARDS)) - and parameter not in WHITELIST_PARAMETERS - ): + or parameter.endswith(tuple(IGNORED_PARAMETERS_WILDCARDS_END)) + or parameter.startswith(tuple(IGNORED_PARAMETERS_WILDCARDS_START)) + ) and parameter not in WHITELIST_PARAMETERS: _LOGGER.debug( "create_entity: Ignoring parameter: %s (%s)", parameter, address ) diff --git a/hahomematic/devices/light.py b/hahomematic/devices/light.py index 0a17ee8c..c839b76d 100644 --- a/hahomematic/devices/light.py +++ b/hahomematic/devices/light.py @@ -322,8 +322,8 @@ def make_ip_light_bsl(device, address, group_base_channels: [int]): "HmIP-BSL": (make_ip_light_bsl, [7, 11]), "HmIP-BSM": (make_ip_light, [3]), "HmIP-BDT": (make_ip_dimmer, [3]), - "HmIP-FDT": (make_ip_dimmer, [3]), - "HmIP-PDT*": (make_ip_dimmer, [3]), + "HmIP-FDT": (make_ip_dimmer, [1]), + "HmIP-PDT*": (make_ip_dimmer, [2]), "HM-LC-Dim1*": (make_rf_dimmer, []), "HM-LC-Dim2*": (make_rf_dimmer, []), "HMW-LC-Dim1*": (make_rf_dimmer, []), diff --git a/hahomematic/entity.py b/hahomematic/entity.py index 7c4a9007..4a7251c2 100644 --- a/hahomematic/entity.py +++ b/hahomematic/entity.py @@ -10,6 +10,7 @@ from hahomematic.const import ( ATTR_ADDRESS, ATTR_HM_DEFAULT, + ATTR_HM_FLAGS, ATTR_HM_MAX, ATTR_HM_MIN, ATTR_HM_OPERATIONS, @@ -29,11 +30,17 @@ EVENT_IMPULSE, EVENT_KEYPRESS, EVENT_UN_REACH, + FLAG_SERVICE, + FLAG_VISIBLE, HA_PLATFORM_EVENT, HIDDEN_PARAMETERS, HM_ENTITY_UNIT_REPLACE, OPERATION_READ, TYPE_ACTION, + TYPE_BOOL, + TYPE_FLOAT, + TYPE_INTEGER, + TYPE_STRING, ) from hahomematic.devices.device_description import ( DD_FIELDS, @@ -184,6 +191,9 @@ def _assign_parameter_data(self): """Assign parameter data to instance variables.""" self._type = self._parameter_data.get(ATTR_HM_TYPE) self._default = self._parameter_data.get(ATTR_HM_DEFAULT) + flags = self._parameter_data.get(ATTR_HM_FLAGS, 0) + self._visible = flags & FLAG_VISIBLE == FLAG_VISIBLE + self._service = flags & FLAG_SERVICE == FLAG_SERVICE self._max = self._parameter_data.get(ATTR_HM_MAX) self._min = self._parameter_data.get(ATTR_HM_MIN) self._operations = self._parameter_data.get(ATTR_HM_OPERATIONS) @@ -234,10 +244,14 @@ def hmtype(self): def _convert_value(self, value): """Convert value to a given hm_type.""" - if self._type == "FLOAT": + if self._type == TYPE_BOOL: + return bool(value) + if self._type == TYPE_FLOAT: return float(value) - if self._type == "INTEGER": + if self._type == TYPE_INTEGER: return int(value) + if self._type == TYPE_STRING: + return str(value) return value async def send_value(self, value) -> None: diff --git a/hahomematic/helpers.py b/hahomematic/helpers.py index acdceda0..64288bd2 100644 --- a/hahomematic/helpers.py +++ b/hahomematic/helpers.py @@ -2,6 +2,7 @@ Helper functions used within hahomematic """ import logging +import ssl from hahomematic.const import ( ATTR_HM_ALARM, @@ -78,8 +79,10 @@ def get_entity_name(central, interface_id, address, parameter, unique_id) -> str if name.count(":") == 1: d_name = name.split(":")[0] p_name = parameter.title().replace("_", " ") - c_no = name.split(":")[1] - c_name = "" if c_no == "0" else f" ch{c_no}" + c_name = "" + if central.has_multiple_channels(address=address, parameter=parameter): + c_no = name.split(":")[1] + c_name = "" if c_no == "0" else f" ch{c_no}" name = f"{d_name} {p_name}{c_name}" else: d_name = name @@ -96,3 +99,14 @@ def get_custom_entity_name( address = f"{address}:{channel_no}" return central.names_cache.get(interface_id, {}).get(address, unique_id) + + +def get_tls_context(verify_tls): + """Return tls verified/unverified ssl/tls context""" + if verify_tls: + ssl_context = ssl.create_default_context() + else: + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + return ssl_context diff --git a/hahomematic/hub.py b/hahomematic/hub.py index 7d016365..e2134b13 100644 --- a/hahomematic/hub.py +++ b/hahomematic/hub.py @@ -124,10 +124,12 @@ async def set_state(self, value): old_state = self._state if isinstance(old_state, bool): value = bool(value) + elif isinstance(old_state, float): + value = float(value) + elif isinstance(old_state, int): + value = int(value) elif isinstance(old_state, str): value = str(value) - else: - value = float(value) self._state = value self.update_entity() @@ -141,8 +143,8 @@ def __init__(self, central, use_entities=False): unique_id = generate_unique_id(central.instance_name, prefix="hub") name = central.instance_name super().__init__(central, unique_id, name) - self.hub_entities: dict[str, BaseHubEntity] = {} - self._variables: dict[str, BaseHubEntity] = {} + self.hub_entities: dict[str, HmSystemVariable] = {} + self._variables: dict[str, Any] = {} self._use_entities = use_entities @property @@ -194,7 +196,7 @@ async def _update_entities(self): ): self._variables[name] = value continue - entity = self.hub_entities.get(name) + entity: HmSystemVariable = self.hub_entities.get(name) if entity: await entity.set_state(value) else: diff --git a/hahomematic/json_rpc_client.py b/hahomematic/json_rpc_client.py index 4cda9a85..34efac8e 100644 --- a/hahomematic/json_rpc_client.py +++ b/hahomematic/json_rpc_client.py @@ -3,7 +3,6 @@ """ import json import logging -import ssl from aiohttp import ClientConnectorError, ClientError, ClientSession, TCPConnector @@ -16,12 +15,9 @@ ATTR_USERNAME, PATH_JSON_RPC, ) +from hahomematic.helpers import get_tls_context _LOGGER = logging.getLogger(__name__) -VERIFIED_CTX = ssl.create_default_context() -UNVERIFIED_CTX = ssl.create_default_context() -UNVERIFIED_CTX.check_hostname = False -UNVERIFIED_CTX.verify_mode = ssl.CERT_NONE class JsonRpcAioHttpClient: @@ -47,18 +43,7 @@ def __init__( self._password = self._central_config.password self._json_tls = self._central_config.json_tls self._verify_tls = self._central_config.verify_tls - self._ssl_context = self._get_tls_context() - - def _get_tls_context(self): - ssl_context = None - if self._json_tls: - if self._verify_tls: - ssl_context = ssl.create_default_context() - else: - ssl_context = ssl.create_default_context() - ssl_context.check_hostname = False - ssl_context.verify_mode = ssl.CERT_NONE - return ssl_context + self._tls_context = get_tls_context(self._verify_tls) @property def is_activated(self): @@ -176,7 +161,7 @@ async def _post( data=payload, headers=headers, timeout=config.TIMEOUT, - ssl=self._ssl_context, + ssl=self._tls_context, ) else: resp = await self._client_session.post( diff --git a/hahomematic/proxy.py b/hahomematic/proxy.py index 27b169a6..abeca17f 100644 --- a/hahomematic/proxy.py +++ b/hahomematic/proxy.py @@ -3,15 +3,17 @@ """ import logging -import ssl import xmlrpc.client + from hahomematic.const import ATTR_TLS, ATTR_VERIFY_TLS +from hahomematic.helpers import get_tls_context _LOGGER = logging.getLogger(__name__) ATTR_CONTEXT = "context" ATTR_ENCODING_ISO_8859_1 = "ISO-8859-1" + class ProxyException(Exception): """hahomematic Proxy exception.""" @@ -34,19 +36,10 @@ def __init__(self, executor_func, *args, **kwargs): self._tls = kwargs.pop(ATTR_TLS, False) self._verify_tls = kwargs.pop(ATTR_VERIFY_TLS, True) if self._tls: - kwargs[ATTR_CONTEXT] = self._get_tls_context() - xmlrpc.client.ServerProxy.__init__(self, encoding=ATTR_ENCODING_ISO_8859_1, *args, **kwargs) - - def _get_tls_context(self): - ssl_context = None - if self._tls: - if self._verify_tls: - ssl_context = ssl.create_default_context() - else: - ssl_context = ssl.create_default_context() - ssl_context.check_hostname = False - ssl_context.verify_mode = ssl.CERT_NONE - return ssl_context + kwargs[ATTR_CONTEXT] = get_tls_context(self._verify_tls) + xmlrpc.client.ServerProxy.__init__( + self, encoding=ATTR_ENCODING_ISO_8859_1, *args, **kwargs + ) async def __async_request(self, *args, **kwargs): """ @@ -86,8 +79,8 @@ def __init__(self, *args, **kwargs): """ self._tls = kwargs.pop("tls", False) self._verify_tls = kwargs.pop("verify_tls", True) - if self._tls and not self._verify_tls and self._verify_tls is not None: - kwargs["context"] = ssl._create_unverified_context() + if self._tls: + kwargs["context"] = get_tls_context(self._verify_tls) xmlrpc.client.ServerProxy.__init__(self, encoding="ISO-8859-1", *args, **kwargs) def __request(self, *args, **kwargs): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..61535c93 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +aiohttp>=3.8.0 +voluptuous>=0.12.2 + diff --git a/requirements_test.txt b/requirements_test.txt index 1e3ebb71..84682d61 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -aiohttp==3.8.0 -voluptuous==0.12.2 +aiohttp>=3.8.0 +voluptuous>=0.12.2 pylint==2.11.1 pylint-strict-informational==0.1 diff --git a/setup.py b/setup.py index b61d3ca7..390a3829 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ def readme(): PACKAGE_NAME = "hahomematic" HERE = os.path.abspath(os.path.dirname(__file__)) -VERSION = "0.0.11" +VERSION = "0.0.12" PACKAGES = find_packages(exclude=["tests", "tests.*", "dist", "build"])