From 674682e88f5e2efef70a7760249dab88c337f212 Mon Sep 17 00:00:00 2001
From: Sebastian Muszynski <basti@linkt.de>
Date: Thu, 19 Apr 2018 09:11:38 +0200
Subject: [PATCH] Support for multiple MAX!Cube LAN gateways added (#13517)

---
 .../components/binary_sensor/maxcube.py       | 23 ++++-----
 homeassistant/components/climate/maxcube.py   | 22 ++++-----
 homeassistant/components/maxcube.py           | 49 ++++++++++++++-----
 3 files changed, 60 insertions(+), 34 deletions(-)

diff --git a/homeassistant/components/binary_sensor/maxcube.py b/homeassistant/components/binary_sensor/maxcube.py
index 1043004243a..c131de5420a 100644
--- a/homeassistant/components/binary_sensor/maxcube.py
+++ b/homeassistant/components/binary_sensor/maxcube.py
@@ -7,7 +7,7 @@ https://home-assistant.io/components/maxcube/
 import logging
 
 from homeassistant.components.binary_sensor import BinarySensorDevice
-from homeassistant.components.maxcube import MAXCUBE_HANDLE
+from homeassistant.components.maxcube import DATA_KEY
 from homeassistant.const import STATE_UNKNOWN
 
 _LOGGER = logging.getLogger(__name__)
@@ -15,16 +15,17 @@ _LOGGER = logging.getLogger(__name__)
 
 def setup_platform(hass, config, add_devices, discovery_info=None):
     """Iterate through all MAX! Devices and add window shutters."""
-    cube = hass.data[MAXCUBE_HANDLE].cube
     devices = []
+    for handler in hass.data[DATA_KEY].values():
+        cube = handler.cube
+        for device in cube.devices:
+            name = "{} {}".format(
+                cube.room_by_id(device.room_id).name, device.name)
 
-    for device in cube.devices:
-        name = "{} {}".format(
-            cube.room_by_id(device.room_id).name, device.name)
-
-        # Only add Window Shutters
-        if cube.is_windowshutter(device):
-            devices.append(MaxCubeShutter(hass, name, device.rf_address))
+            # Only add Window Shutters
+            if cube.is_windowshutter(device):
+                devices.append(
+                    MaxCubeShutter(handler, name, device.rf_address))
 
     if devices:
         add_devices(devices)
@@ -33,12 +34,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
 class MaxCubeShutter(BinarySensorDevice):
     """Representation of a MAX! Cube Binary Sensor device."""
 
-    def __init__(self, hass, name, rf_address):
+    def __init__(self, handler, name, rf_address):
         """Initialize MAX! Cube BinarySensorDevice."""
         self._name = name
         self._sensor_type = 'window'
         self._rf_address = rf_address
-        self._cubehandle = hass.data[MAXCUBE_HANDLE]
+        self._cubehandle = handler
         self._state = STATE_UNKNOWN
 
     @property
diff --git a/homeassistant/components/climate/maxcube.py b/homeassistant/components/climate/maxcube.py
index 067d11437b2..712ebb4f4ce 100644
--- a/homeassistant/components/climate/maxcube.py
+++ b/homeassistant/components/climate/maxcube.py
@@ -10,7 +10,7 @@ import logging
 from homeassistant.components.climate import (
     ClimateDevice, STATE_AUTO, SUPPORT_TARGET_TEMPERATURE,
     SUPPORT_OPERATION_MODE)
-from homeassistant.components.maxcube import MAXCUBE_HANDLE
+from homeassistant.components.maxcube import DATA_KEY
 from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE
 
 _LOGGER = logging.getLogger(__name__)
@@ -24,16 +24,16 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
 
 def setup_platform(hass, config, add_devices, discovery_info=None):
     """Iterate through all MAX! Devices and add thermostats."""
-    cube = hass.data[MAXCUBE_HANDLE].cube
-
     devices = []
+    for handler in hass.data[DATA_KEY].values():
+        cube = handler.cube
+        for device in cube.devices:
+            name = '{} {}'.format(
+                cube.room_by_id(device.room_id).name, device.name)
 
-    for device in cube.devices:
-        name = '{} {}'.format(
-            cube.room_by_id(device.room_id).name, device.name)
-
-        if cube.is_thermostat(device) or cube.is_wallthermostat(device):
-            devices.append(MaxCubeClimate(hass, name, device.rf_address))
+            if cube.is_thermostat(device) or cube.is_wallthermostat(device):
+                devices.append(
+                    MaxCubeClimate(handler, name, device.rf_address))
 
     if devices:
         add_devices(devices)
@@ -42,14 +42,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
 class MaxCubeClimate(ClimateDevice):
     """MAX! Cube ClimateDevice."""
 
-    def __init__(self, hass, name, rf_address):
+    def __init__(self, handler, name, rf_address):
         """Initialize MAX! Cube ClimateDevice."""
         self._name = name
         self._unit_of_measurement = TEMP_CELSIUS
         self._operation_list = [STATE_AUTO, STATE_MANUAL, STATE_BOOST,
                                 STATE_VACATION]
         self._rf_address = rf_address
-        self._cubehandle = hass.data[MAXCUBE_HANDLE]
+        self._cubehandle = handler
 
     @property
     def supported_features(self):
diff --git a/homeassistant/components/maxcube.py b/homeassistant/components/maxcube.py
index a0a8db6ba4d..13d3e0b444b 100644
--- a/homeassistant/components/maxcube.py
+++ b/homeassistant/components/maxcube.py
@@ -22,12 +22,22 @@ _LOGGER = logging.getLogger(__name__)
 DEFAULT_PORT = 62910
 DOMAIN = 'maxcube'
 
-MAXCUBE_HANDLE = 'maxcube'
+DATA_KEY = 'maxcube'
+
+NOTIFICATION_ID = 'maxcube_notification'
+NOTIFICATION_TITLE = 'Max!Cube gateway setup'
+
+CONF_GATEWAYS = 'gateways'
+
+CONFIG_GATEWAY = vol.Schema({
+    vol.Required(CONF_HOST): cv.string,
+    vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+})
 
 CONFIG_SCHEMA = vol.Schema({
     DOMAIN: vol.Schema({
-        vol.Required(CONF_HOST): cv.string,
-        vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+        vol.Required(CONF_GATEWAYS, default={}):
+            vol.All(cv.ensure_list, [CONFIG_GATEWAY])
     }),
 }, extra=vol.ALLOW_EXTRA)
 
@@ -36,18 +46,33 @@ def setup(hass, config):
     """Establish connection to MAX! Cube."""
     from maxcube.connection import MaxCubeConnection
     from maxcube.cube import MaxCube
+    if DATA_KEY not in hass.data:
+        hass.data[DATA_KEY] = {}
 
-    host = config.get(DOMAIN).get(CONF_HOST)
-    port = config.get(DOMAIN).get(CONF_PORT)
-
-    try:
-        cube = MaxCube(MaxCubeConnection(host, port))
-    except timeout:
-        _LOGGER.error("Connection to Max!Cube could not be established")
-        cube = None
+    if DOMAIN not in config:
         return False
 
-    hass.data[MAXCUBE_HANDLE] = MaxCubeHandle(cube)
+    connection_failed = 0
+    gateways = config[DOMAIN][CONF_GATEWAYS]
+    for gateway in gateways:
+        host = gateway[CONF_HOST]
+        port = gateway[CONF_PORT]
+
+        try:
+            cube = MaxCube(MaxCubeConnection(host, port))
+            hass.data[DATA_KEY][host] = MaxCubeHandle(cube)
+        except timeout as ex:
+            _LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex))
+            hass.components.persistent_notification.create(
+                'Error: {}<br />'
+                'You will need to restart Home Assistant after fixing.'
+                ''.format(ex),
+                title=NOTIFICATION_TITLE,
+                notification_id=NOTIFICATION_ID)
+            connection_failed += 1
+
+    if connection_failed >= len(gateways):
+        return False
 
     load_platform(hass, 'climate', DOMAIN)
     load_platform(hass, 'binary_sensor', DOMAIN)