class FibaroController(): """Initiate Fibaro Controller Class.""" def __init__(self, username, password, url, import_plugins, config): """Initialize the Fibaro controller.""" from fiblary3.client.v4.client import Client as FibaroClient self._client = FibaroClient(url, username, password) self._scene_map = None # Whether to import devices from plugins self._import_plugins = import_plugins self._device_config = config[CONF_DEVICE_CONFIG] self._room_map = None # Mapping roomId to room object self._device_map = None # Mapping deviceId to device object self.fibaro_devices = None # List of devices by type self._callbacks = {} # Update value callbacks by deviceId self._state_handler = None # Fiblary's StateHandler object self._excluded_devices = config.get(CONF_EXCLUDE, []) self.hub_serial = None # Unique serial number of the hub def connect(self): """Start the communication with the Fibaro controller.""" try: login = self._client.login.get() info = self._client.info.get() self.hub_serial = slugify(info.serialNumber) except AssertionError: _LOGGER.error("Can't connect to Fibaro HC. " "Please check URL.") return False if login is None or login.status is False: _LOGGER.error("Invalid login for Fibaro HC. " "Please check username and password.") return False self._room_map = {room.id: room for room in self._client.rooms.list()} self._read_devices() self._read_scenes() return True def enable_state_handler(self): """Start StateHandler thread for monitoring updates.""" from fiblary3.client.v4.client import StateHandler self._state_handler = StateHandler(self._client, self._on_state_change) def disable_state_handler(self): """Stop StateHandler thread used for monitoring updates.""" self._state_handler.stop() self._state_handler = None def _on_state_change(self, state): """Handle change report received from the HomeCenter.""" callback_set = set() for change in state.get('changes', []): try: dev_id = change.pop('id') if dev_id not in self._device_map.keys(): continue device = self._device_map[dev_id] for property_name, value in change.items(): if property_name == 'log': if value and value != "transfer OK": _LOGGER.debug("LOG %s: %s", device.friendly_name, value) continue if property_name == 'logTemp': continue if property_name in device.properties: device.properties[property_name] = \ value _LOGGER.debug("<- %s.%s = %s", device.ha_id, property_name, str(value)) else: _LOGGER.warning("%s.%s not found", device.ha_id, property_name) if dev_id in self._callbacks: callback_set.add(dev_id) except (ValueError, KeyError): pass for item in callback_set: self._callbacks[item]() def register(self, device_id, callback): """Register device with a callback for updates.""" self._callbacks[device_id] = callback @staticmethod def _map_device_to_type(device): """Map device to HA device type.""" # Use our lookup table to identify device type if 'type' in device: device_type = FIBARO_TYPEMAP.get(device.type) elif 'baseType' in device: device_type = FIBARO_TYPEMAP.get(device.baseType) else: device_type = None # We can also identify device type by its capabilities if device_type is None: if 'setBrightness' in device.actions: device_type = 'light' elif 'turnOn' in device.actions: device_type = 'switch' elif 'open' in device.actions: device_type = 'cover' elif 'value' in device.properties: if device.properties.value in ('true', 'false'): device_type = 'binary_sensor' else: device_type = 'sensor' # Switches that control lights should show up as lights if device_type == 'switch' and \ device.properties.get('isLight', 'false') == 'true': device_type = 'light' return device_type def _read_scenes(self): scenes = self._client.scenes.list() self._scene_map = {} for device in scenes: if not device.visible: continue if device.roomID == 0: room_name = 'Unknown' else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = '{} {}'.format(room_name, device.name) device.ha_id = '{}_{}_{}'.format(slugify(room_name), slugify(device.name), device.id) device.unique_id_str = "{}.{}".format(self.hub_serial, device.id) self._scene_map[device.id] = device self.fibaro_devices['scene'].append(device) def _read_devices(self): """Read and process the device list.""" devices = self._client.devices.list() self._device_map = {} self.fibaro_devices = defaultdict(list) for device in devices: try: if device.roomID == 0: room_name = 'Unknown' else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = room_name + ' ' + device.name device.ha_id = '{}_{}_{}'.format(slugify(room_name), slugify(device.name), device.id) if device.enabled and \ ('isPlugin' not in device or (not device.isPlugin or self._import_plugins)) and \ device.ha_id not in self._excluded_devices: device.mapped_type = self._map_device_to_type(device) device.device_config = \ self._device_config.get(device.ha_id, {}) else: device.mapped_type = None if device.mapped_type: device.unique_id_str = "{}.{}".format( self.hub_serial, device.id) self._device_map[device.id] = device self.fibaro_devices[device.mapped_type].append(device) else: _LOGGER.debug("%s (%s, %s) not used", device.ha_id, device.type, device.baseType) except (KeyError, ValueError): pass
class FibaroController: """Initiate Fibaro Controller Class.""" def __init__(self, config): """Initialize the Fibaro controller.""" self._client = FibaroClient(config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]) self._scene_map = None # Whether to import devices from plugins self._import_plugins = config[CONF_PLUGINS] self._device_config = config[CONF_DEVICE_CONFIG] self._room_map = None # Mapping roomId to room object self._device_map = None # Mapping deviceId to device object self.fibaro_devices = None # List of devices by type self._callbacks = {} # Update value callbacks by deviceId self._state_handler = None # Fiblary's StateHandler object self._excluded_devices = config[CONF_EXCLUDE] self.hub_serial = None # Unique serial number of the hub def connect(self): """Start the communication with the Fibaro controller.""" try: login = self._client.login.get() info = self._client.info.get() self.hub_serial = slugify(info.serialNumber) except AssertionError: _LOGGER.error("Can't connect to Fibaro HC. Please check URL") return False if login is None or login.status is False: _LOGGER.error( "Invalid login for Fibaro HC. Please check username and password" ) return False self._room_map = {room.id: room for room in self._client.rooms.list()} self._read_devices() self._read_scenes() return True def enable_state_handler(self): """Start StateHandler thread for monitoring updates.""" self._state_handler = StateHandler(self._client, self._on_state_change) def disable_state_handler(self): """Stop StateHandler thread used for monitoring updates.""" self._state_handler.stop() self._state_handler = None def _on_state_change(self, state): """Handle change report received from the HomeCenter.""" callback_set = set() for change in state.get("changes", []): try: dev_id = change.pop("id") if dev_id not in self._device_map: continue device = self._device_map[dev_id] for property_name, value in change.items(): if property_name == "log": if value and value != "transfer OK": _LOGGER.debug("LOG %s: %s", device.friendly_name, value) continue if property_name == "logTemp": continue if property_name in device.properties: device.properties[property_name] = value _LOGGER.debug("<- %s.%s = %s", device.ha_id, property_name, str(value)) else: _LOGGER.warning("%s.%s not found", device.ha_id, property_name) if dev_id in self._callbacks: callback_set.add(dev_id) except (ValueError, KeyError): pass for item in callback_set: for callback in self._callbacks[item]: callback() def register(self, device_id, callback): """Register device with a callback for updates.""" self._callbacks.setdefault(device_id, []) self._callbacks[device_id].append(callback) def get_children(self, device_id): """Get a list of child devices.""" return [ device for device in self._device_map.values() if device.parentId == device_id ] def get_children2(self, device_id, endpoint_id): """Get a list of child devices for the same endpoint.""" return [ device for device in self._device_map.values() if device.parentId == device_id and ( "endPointId" not in device.properties or device.properties.endPointId == endpoint_id) ] def get_siblings(self, device): """Get the siblings of a device.""" if "endPointId" in device.properties: return self.get_children2( self._device_map[device.id].parentId, self._device_map[device.id].properties.endPointId, ) return self.get_children(self._device_map[device.id].parentId) @staticmethod def _map_device_to_type(device): """Map device to HA device type.""" # Use our lookup table to identify device type device_type = None if "type" in device: device_type = FIBARO_TYPEMAP.get(device.type) if device_type is None and "baseType" in device: device_type = FIBARO_TYPEMAP.get(device.baseType) # We can also identify device type by its capabilities if device_type is None: if "setBrightness" in device.actions: device_type = "light" elif "turnOn" in device.actions: device_type = "switch" elif "open" in device.actions: device_type = "cover" elif "secure" in device.actions: device_type = "lock" elif "value" in device.properties: if device.properties.value in ("true", "false"): device_type = "binary_sensor" else: device_type = "sensor" # Switches that control lights should show up as lights if device_type == "switch" and device.properties.get("isLight", False): device_type = "light" return device_type def _read_scenes(self): scenes = self._client.scenes.list() self._scene_map = {} for device in scenes: if "name" not in device or "id" not in device: continue device.fibaro_controller = self if "roomID" not in device or device.roomID == 0: room_name = "Unknown" else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = f"{room_name} {device.name}" device.ha_id = ( f"scene_{slugify(room_name)}_{slugify(device.name)}_{device.id}" ) device.unique_id_str = f"{self.hub_serial}.scene.{device.id}" self._scene_map[device.id] = device self.fibaro_devices["scene"].append(device) _LOGGER.debug("%s scene -> %s", device.ha_id, device) def _read_devices(self): """Read and process the device list.""" devices = self._client.devices.list() self._device_map = {} self.fibaro_devices = defaultdict(list) last_climate_parent = None last_endpoint = None for device in devices: try: if "name" not in device or "id" not in device: continue device.fibaro_controller = self if "roomID" not in device or device.roomID == 0: room_name = "Unknown" else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = f"{room_name} {device.name}" device.ha_id = ( f"{slugify(room_name)}_{slugify(device.name)}_{device.id}") if (device.enabled and ("isPlugin" not in device or (not device.isPlugin or self._import_plugins)) and device.ha_id not in self._excluded_devices): device.mapped_type = self._map_device_to_type(device) device.device_config = self._device_config.get( device.ha_id, {}) else: device.mapped_type = None if (dtype := device.mapped_type) is None: continue device.unique_id_str = f"{self.hub_serial}.{device.id}" self._device_map[device.id] = device _LOGGER.debug( "%s (%s, %s) -> %s %s", device.ha_id, device.type, device.baseType, dtype, str(device), ) if dtype != "climate": self.fibaro_devices[dtype].append(device) continue # We group climate devices into groups with the same # endPointID belonging to the same parent device. if "endPointId" in device.properties: _LOGGER.debug( "climate device: %s, endPointId: %s", device.ha_id, device.properties.endPointId, ) else: _LOGGER.debug("climate device: %s, no endPointId", device.ha_id) # If a sibling of this device has been added, skip this one # otherwise add the first visible device in the group # which is a hack, but solves a problem with FGT having # hidden compatibility devices before the real device if last_climate_parent != device.parentId or ( "endPointId" in device.properties and last_endpoint != device.properties.endPointId): _LOGGER.debug("Handle separately") self.fibaro_devices[dtype].append(device) last_climate_parent = device.parentId if "endPointId" in device.properties: last_endpoint = device.properties.endPointId else: last_endpoint = 0 else: _LOGGER.debug("not handling separately") except (KeyError, ValueError): pass
class FibaroController(): """Initiate Fibaro Controller Class.""" def __init__(self, config): """Initialize the Fibaro controller.""" from fiblary3.client.v4.client import Client as FibaroClient self._client = FibaroClient( config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]) self._scene_map = None # Whether to import devices from plugins self._import_plugins = config[CONF_PLUGINS] self._device_config = config[CONF_DEVICE_CONFIG] self._room_map = None # Mapping roomId to room object self._device_map = None # Mapping deviceId to device object self.fibaro_devices = None # List of devices by type self._callbacks = {} # Update value callbacks by deviceId self._state_handler = None # Fiblary's StateHandler object self._excluded_devices = config[CONF_EXCLUDE] self.hub_serial = None # Unique serial number of the hub def connect(self): """Start the communication with the Fibaro controller.""" try: login = self._client.login.get() info = self._client.info.get() self.hub_serial = slugify(info.serialNumber) except AssertionError: _LOGGER.error("Can't connect to Fibaro HC. " "Please check URL.") return False if login is None or login.status is False: _LOGGER.error("Invalid login for Fibaro HC. " "Please check username and password") return False self._room_map = {room.id: room for room in self._client.rooms.list()} self._read_devices() self._read_scenes() return True def enable_state_handler(self): """Start StateHandler thread for monitoring updates.""" from fiblary3.client.v4.client import StateHandler self._state_handler = StateHandler(self._client, self._on_state_change) def disable_state_handler(self): """Stop StateHandler thread used for monitoring updates.""" self._state_handler.stop() self._state_handler = None def _on_state_change(self, state): """Handle change report received from the HomeCenter.""" callback_set = set() for change in state.get('changes', []): try: dev_id = change.pop('id') if dev_id not in self._device_map.keys(): continue device = self._device_map[dev_id] for property_name, value in change.items(): if property_name == 'log': if value and value != "transfer OK": _LOGGER.debug("LOG %s: %s", device.friendly_name, value) continue if property_name == 'logTemp': continue if property_name in device.properties: device.properties[property_name] = \ value _LOGGER.debug("<- %s.%s = %s", device.ha_id, property_name, str(value)) else: _LOGGER.warning("%s.%s not found", device.ha_id, property_name) if dev_id in self._callbacks: callback_set.add(dev_id) except (ValueError, KeyError): pass for item in callback_set: self._callbacks[item]() def register(self, device_id, callback): """Register device with a callback for updates.""" self._callbacks[device_id] = callback @staticmethod def _map_device_to_type(device): """Map device to HA device type.""" # Use our lookup table to identify device type device_type = None if 'type' in device: device_type = FIBARO_TYPEMAP.get(device.type) if device_type is None and 'baseType' in device: device_type = FIBARO_TYPEMAP.get(device.baseType) # We can also identify device type by its capabilities if device_type is None: if 'setBrightness' in device.actions: device_type = 'light' elif 'turnOn' in device.actions: device_type = 'switch' elif 'open' in device.actions: device_type = 'cover' elif 'value' in device.properties: if device.properties.value in ('true', 'false'): device_type = 'binary_sensor' else: device_type = 'sensor' # Switches that control lights should show up as lights if device_type == 'switch' and \ device.properties.get('isLight', 'false') == 'true': device_type = 'light' return device_type def _read_scenes(self): scenes = self._client.scenes.list() self._scene_map = {} for device in scenes: if not device.visible: continue device.fibaro_controller = self if device.roomID == 0: room_name = 'Unknown' else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = '{} {}'.format(room_name, device.name) device.ha_id = '{}_{}_{}'.format( slugify(room_name), slugify(device.name), device.id) device.unique_id_str = "{}.{}".format( self.hub_serial, device.id) self._scene_map[device.id] = device self.fibaro_devices['scene'].append(device) def _read_devices(self): """Read and process the device list.""" devices = self._client.devices.list() self._device_map = {} self.fibaro_devices = defaultdict(list) for device in devices: try: device.fibaro_controller = self if device.roomID == 0: room_name = 'Unknown' else: room_name = self._room_map[device.roomID].name device.room_name = room_name device.friendly_name = room_name + ' ' + device.name device.ha_id = '{}_{}_{}'.format( slugify(room_name), slugify(device.name), device.id) if device.enabled and \ ('isPlugin' not in device or (not device.isPlugin or self._import_plugins)) and \ device.ha_id not in self._excluded_devices: device.mapped_type = self._map_device_to_type(device) device.device_config = \ self._device_config.get(device.ha_id, {}) else: device.mapped_type = None if device.mapped_type: device.unique_id_str = "{}.{}".format( self.hub_serial, device.id) self._device_map[device.id] = device self.fibaro_devices[device.mapped_type].append(device) _LOGGER.debug("%s (%s, %s) -> %s. Prop: %s Actions: %s", device.ha_id, device.type, device.baseType, device.mapped_type, str(device.properties), str(device.actions)) except (KeyError, ValueError): pass