diff --git a/.coveragerc b/.coveragerc index 3107af9140a..f062501bd35 100644 --- a/.coveragerc +++ b/.coveragerc @@ -127,7 +127,7 @@ omit = homeassistant/components/eufy.py homeassistant/components/*/eufy.py - homeassistant/components/fibaro.py + homeassistant/components/fibaro/__init__.py homeassistant/components/*/fibaro.py homeassistant/components/gc100.py diff --git a/homeassistant/components/binary_sensor/fibaro.py b/homeassistant/components/binary_sensor/fibaro.py index 8af2bde10ad..1934580c58e 100644 --- a/homeassistant/components/binary_sensor/fibaro.py +++ b/homeassistant/components/binary_sensor/fibaro.py @@ -9,7 +9,7 @@ import logging from homeassistant.components.binary_sensor import ( BinarySensorDevice, ENTITY_ID_FORMAT) from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice) + FIBARO_DEVICES, FibaroDevice) from homeassistant.const import (CONF_DEVICE_CLASS, CONF_ICON) DEPENDENCIES = ['fibaro'] @@ -33,17 +33,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None): return add_entities( - [FibaroBinarySensor(device, hass.data[FIBARO_CONTROLLER]) + [FibaroBinarySensor(device) for device in hass.data[FIBARO_DEVICES]['binary_sensor']], True) class FibaroBinarySensor(FibaroDevice, BinarySensorDevice): """Representation of a Fibaro Binary Sensor.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the binary_sensor.""" self._state = None - super().__init__(fibaro_device, controller) + super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) stype = None devconf = fibaro_device.device_config diff --git a/homeassistant/components/cover/fibaro.py b/homeassistant/components/cover/fibaro.py index dc82087f802..d47dbb20315 100644 --- a/homeassistant/components/cover/fibaro.py +++ b/homeassistant/components/cover/fibaro.py @@ -9,7 +9,7 @@ import logging from homeassistant.components.cover import ( CoverDevice, ENTITY_ID_FORMAT, ATTR_POSITION, ATTR_TILT_POSITION) from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice) + FIBARO_DEVICES, FibaroDevice) DEPENDENCIES = ['fibaro'] @@ -22,16 +22,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None): return add_entities( - [FibaroCover(device, hass.data[FIBARO_CONTROLLER]) for + [FibaroCover(device) for device in hass.data[FIBARO_DEVICES]['cover']], True) class FibaroCover(FibaroDevice, CoverDevice): """Representation a Fibaro Cover.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the Vera device.""" - super().__init__(fibaro_device, controller) + super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) @staticmethod diff --git a/homeassistant/components/fibaro.py b/homeassistant/components/fibaro/__init__.py similarity index 85% rename from homeassistant/components/fibaro.py rename to homeassistant/components/fibaro/__init__.py index d506f6c471d..715cc036265 100644 --- a/homeassistant/components/fibaro.py +++ b/homeassistant/components/fibaro/__init__.py @@ -11,8 +11,8 @@ from typing import Optional import voluptuous as vol from homeassistant.const import ( - ATTR_ARMED, ATTR_BATTERY_LEVEL, CONF_DEVICE_CLASS, - CONF_EXCLUDE, CONF_ICON, CONF_PASSWORD, CONF_URL, CONF_USERNAME, + ATTR_ARMED, ATTR_BATTERY_LEVEL, CONF_DEVICE_CLASS, CONF_EXCLUDE, + CONF_ICON, CONF_PASSWORD, CONF_URL, CONF_USERNAME, CONF_WHITE_VALUE, EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv @@ -24,10 +24,11 @@ REQUIREMENTS = ['fiblary3==0.1.7'] _LOGGER = logging.getLogger(__name__) DOMAIN = 'fibaro' FIBARO_DEVICES = 'fibaro_devices' -FIBARO_CONTROLLER = 'fibaro_controller' +FIBARO_CONTROLLERS = 'fibaro_controllers' ATTR_CURRENT_POWER_W = "current_power_w" ATTR_CURRENT_ENERGY_KWH = "current_energy_kwh" CONF_PLUGINS = "plugins" +CONF_GATEWAYS = 'gateways' CONF_DIMMING = "dimming" CONF_COLOR = "color" CONF_RESET_COLOR = "reset_color" @@ -65,15 +66,20 @@ DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({ FIBARO_ID_LIST_SCHEMA = vol.Schema([cv.string]) +GATEWAY_CONFIG = vol.Schema({ + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_URL): cv.url, + vol.Optional(CONF_PLUGINS, default=False): cv.boolean, + vol.Optional(CONF_EXCLUDE, default=[]): FIBARO_ID_LIST_SCHEMA, + vol.Optional(CONF_DEVICE_CONFIG, default={}): + vol.Schema({cv.string: DEVICE_CONFIG_SCHEMA_ENTRY}) +}, extra=vol.ALLOW_EXTRA) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_URL): cv.url, - vol.Optional(CONF_PLUGINS, default=False): cv.boolean, - vol.Optional(CONF_EXCLUDE, default=[]): FIBARO_ID_LIST_SCHEMA, - vol.Optional(CONF_DEVICE_CONFIG, default={}): - vol.Schema({cv.string: DEVICE_CONFIG_SCHEMA_ENTRY}) + vol.Required(CONF_GATEWAYS): + vol.All(cv.ensure_list, [GATEWAY_CONFIG]) }) }, extra=vol.ALLOW_EXTRA) @@ -81,20 +87,23 @@ CONFIG_SCHEMA = vol.Schema({ class FibaroController(): """Initiate Fibaro Controller Class.""" - def __init__(self, username, password, url, import_plugins, config): + def __init__(self, config): """Initialize the Fibaro controller.""" from fiblary3.client.v4.client import Client as FibaroClient - self._client = FibaroClient(url, username, password) + + 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 = import_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.get(CONF_EXCLUDE, []) + self._excluded_devices = config[CONF_EXCLUDE] self.hub_serial = None # Unique serial number of the hub def connect(self): @@ -167,12 +176,11 @@ class FibaroController(): 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) - elif 'baseType' in device: + if device_type is None and '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: @@ -200,6 +208,7 @@ class FibaroController(): for device in scenes: if not device.visible: continue + device.fibaro_controller = self if device.roomID == 0: room_name = 'Unknown' else: @@ -220,6 +229,7 @@ class FibaroController(): self.fibaro_devices = defaultdict(list) for device in devices: try: + device.fibaro_controller = self if device.roomID == 0: room_name = 'Unknown' else: @@ -242,33 +252,43 @@ class FibaroController(): 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) + _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 -def setup(hass, config): +def setup(hass, base_config): """Set up the Fibaro Component.""" - hass.data[FIBARO_CONTROLLER] = controller = \ - FibaroController(config[DOMAIN][CONF_USERNAME], - config[DOMAIN][CONF_PASSWORD], - config[DOMAIN][CONF_URL], - config[DOMAIN][CONF_PLUGINS], - config[DOMAIN]) + gateways = base_config[DOMAIN][CONF_GATEWAYS] + hass.data[FIBARO_CONTROLLERS] = {} def stop_fibaro(event): """Stop Fibaro Thread.""" _LOGGER.info("Shutting down Fibaro connection") - hass.data[FIBARO_CONTROLLER].disable_state_handler() + for controller in hass.data[FIBARO_CONTROLLERS].values(): + controller.disable_state_handler() - if controller.connect(): - hass.data[FIBARO_DEVICES] = controller.fibaro_devices + hass.data[FIBARO_DEVICES] = {} + for component in FIBARO_COMPONENTS: + hass.data[FIBARO_DEVICES][component] = [] + + for gateway in gateways: + controller = FibaroController(gateway) + if controller.connect(): + hass.data[FIBARO_CONTROLLERS][controller.hub_serial] = controller + for component in FIBARO_COMPONENTS: + hass.data[FIBARO_DEVICES][component].extend( + controller.fibaro_devices[component]) + + if hass.data[FIBARO_CONTROLLERS]: for component in FIBARO_COMPONENTS: - discovery.load_platform(hass, component, DOMAIN, {}, config) - controller.enable_state_handler() + discovery.load_platform(hass, component, DOMAIN, {}, + base_config) + for controller in hass.data[FIBARO_CONTROLLERS].values(): + controller.enable_state_handler() hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_fibaro) return True @@ -278,10 +298,10 @@ def setup(hass, config): class FibaroDevice(Entity): """Representation of a Fibaro device entity.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the device.""" self.fibaro_device = fibaro_device - self.controller = controller + self.controller = fibaro_device.fibaro_controller self._name = fibaro_device.friendly_name self.ha_id = fibaro_device.ha_id diff --git a/homeassistant/components/light/fibaro.py b/homeassistant/components/light/fibaro.py index 6f2842524a2..9b3b3850f39 100644 --- a/homeassistant/components/light/fibaro.py +++ b/homeassistant/components/light/fibaro.py @@ -12,7 +12,7 @@ from functools import partial from homeassistant.const import ( CONF_WHITE_VALUE) from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice, + FIBARO_DEVICES, FibaroDevice, CONF_DIMMING, CONF_COLOR, CONF_RESET_COLOR) from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_WHITE_VALUE, ENTITY_ID_FORMAT, @@ -50,14 +50,14 @@ async def async_setup_platform(hass, return async_add_entities( - [FibaroLight(device, hass.data[FIBARO_CONTROLLER]) + [FibaroLight(device) for device in hass.data[FIBARO_DEVICES]['light']], True) class FibaroLight(FibaroDevice, Light): """Representation of a Fibaro Light, including dimmable.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the light.""" self._brightness = None self._color = (0, 0) @@ -81,7 +81,7 @@ class FibaroLight(FibaroDevice, Light): if devconf.get(CONF_WHITE_VALUE, supports_white_v): self._supported_flags |= SUPPORT_WHITE_VALUE - super().__init__(fibaro_device, controller) + super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) @property diff --git a/homeassistant/components/scene/fibaro.py b/homeassistant/components/scene/fibaro.py index 7a36900f884..a0bd4e7ff40 100644 --- a/homeassistant/components/scene/fibaro.py +++ b/homeassistant/components/scene/fibaro.py @@ -9,7 +9,7 @@ import logging from homeassistant.components.scene import ( Scene) from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice) + FIBARO_DEVICES, FibaroDevice) DEPENDENCIES = ['fibaro'] @@ -23,7 +23,7 @@ async def async_setup_platform(hass, config, async_add_entities, return async_add_entities( - [FibaroScene(scene, hass.data[FIBARO_CONTROLLER]) + [FibaroScene(scene) for scene in hass.data[FIBARO_DEVICES]['scene']], True) diff --git a/homeassistant/components/sensor/fibaro.py b/homeassistant/components/sensor/fibaro.py index e5ed5638c5b..e437ef8710d 100644 --- a/homeassistant/components/sensor/fibaro.py +++ b/homeassistant/components/sensor/fibaro.py @@ -12,7 +12,7 @@ from homeassistant.const import ( from homeassistant.helpers.entity import Entity from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice) + FIBARO_DEVICES, FibaroDevice) SENSOR_TYPES = { 'com.fibaro.temperatureSensor': @@ -37,18 +37,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None): return add_entities( - [FibaroSensor(device, hass.data[FIBARO_CONTROLLER]) + [FibaroSensor(device) for device in hass.data[FIBARO_DEVICES]['sensor']], True) class FibaroSensor(FibaroDevice, Entity): """Representation of a Fibaro Sensor.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the sensor.""" self.current_value = None self.last_changed_time = None - super().__init__(fibaro_device, controller) + super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) if fibaro_device.type in SENSOR_TYPES: self._unit = SENSOR_TYPES[fibaro_device.type][1] diff --git a/homeassistant/components/switch/fibaro.py b/homeassistant/components/switch/fibaro.py index d3e96646a45..8b59aabec72 100644 --- a/homeassistant/components/switch/fibaro.py +++ b/homeassistant/components/switch/fibaro.py @@ -9,7 +9,7 @@ import logging from homeassistant.util import convert from homeassistant.components.switch import ENTITY_ID_FORMAT, SwitchDevice from homeassistant.components.fibaro import ( - FIBARO_CONTROLLER, FIBARO_DEVICES, FibaroDevice) + FIBARO_DEVICES, FibaroDevice) DEPENDENCIES = ['fibaro'] _LOGGER = logging.getLogger(__name__) @@ -21,17 +21,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None): return add_entities( - [FibaroSwitch(device, hass.data[FIBARO_CONTROLLER]) for + [FibaroSwitch(device) for device in hass.data[FIBARO_DEVICES]['switch']], True) class FibaroSwitch(FibaroDevice, SwitchDevice): """Representation of a Fibaro Switch.""" - def __init__(self, fibaro_device, controller): + def __init__(self, fibaro_device): """Initialize the Fibaro device.""" self._state = False - super().__init__(fibaro_device, controller) + super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) def turn_on(self, **kwargs):