Example #1
0
 def enable_state_handler(self):
     """Start StateHandler thread for monitoring updates."""
     self._state_handler = StateHandler(self._client, self._on_state_change)
Example #2
0
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
Example #3
0
    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)
Example #4
0
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
Example #5
0
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
Example #6
0
 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)