"""MAX! Home Automation via HTTP API. # Example configuration.yaml entry maxhomeautomation: gateways: host: localhost port: 8080 scan_interval: 10 cubes: - hex_address: name: radiator_thermostats: - hex_address: name: wall_thermostats: - hex_address: name: window_shutters: - hex_address: name: eco_buttons: - hex_address: name: """ import logging import time from socket import timeout from threading import Lock import voluptuous as vol import requests import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform import json from .consts import * _LOGGER = logging.getLogger(__name__) # DEFAULTS DEFAULT_PORT = 8080 DEFAULT_METHOD = 'GET' DEFAULT_SCAN_INTERVAL = 60 CONFIG_DEVICE = vol.Schema({ vol.Required(CONF_HEX_ADDRESS): cv.string, vol.Required(CONF_NAME): cv.string, }) CONFIG_CUBE = vol.Schema({ vol.Required(CONF_HEX_ADDRESS): cv.string, vol.Required(CONF_NAME): cv.string, vol.Optional(CONF_RADIATOR_THERMOSTATS, default=[]): vol.All(cv.ensure_list, [CONFIG_DEVICE]), vol.Optional(CONF_WALL_THERMOSTATS, default=[]): vol.All(cv.ensure_list, [CONFIG_DEVICE]), vol.Optional(CONF_WINDOWS_SHUTTERS, default=[]): vol.All(cv.ensure_list, [CONFIG_DEVICE]), vol.Optional(CONF_ECO_BUTTONS, default=[]): vol.All(cv.ensure_list, [CONFIG_DEVICE]), }) CONFIG_GATEWAY = vol.Schema({ vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_PORT, default = DEFAULT_PORT): cv.port, vol.Optional(CONF_SCAN_INTERVAL, default = DEFAULT_SCAN_INTERVAL): cv.time_period, vol.Required(CONF_CUBES, default={}): vol.All(cv.ensure_list, [CONFIG_CUBE]), }) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_GATEWAYS, default={}): vol.All(cv.ensure_list, [CONFIG_GATEWAY]), }), }, extra=vol.ALLOW_EXTRA) def setup(hass, config): """Establish connection to MAX!HomeAutomation.""" if DATA_KEY not in hass.data: hass.data[DATA_KEY] = config # Load platform parts load_platform(hass, 'climate', DOMAIN, {}, config) load_platform(hass, 'sensor', DOMAIN, {}, config) load_platform(hass, 'binary_sensor', DOMAIN, {}, config) # platform initialization was successful return True class MaxHomeAutomationDeviceHandler: """Keep the cube instance in one place and centralize the update.""" def __init__(self, gateway_base_url, cube_hex_address, device_hex_address, scan_interval): """Initialize the Cube Handle.""" # store initial values self._gateway_base_url = gateway_base_url self._cube_hex_address = cube_hex_address self._device_hex_address = device_hex_address self._scan_interval = scan_interval # MAX! Home Automation MAX! Cube JSON API URL device_data_url = self._gateway_base_url + "get-status-json?cube={}&device={}".format(self._cube_hex_address, self._device_hex_address) self._device_data_request = requests.Request(DEFAULT_METHOD, device_data_url).prepare() # JSON data initial value self.data = None # thread synchronization stuff self._mutex = Lock() # initially not actual self._updatets = time.time() - self._scan_interval; def update(self): """Pull the latest data from the MAX! Home Automation.""" # Acquire mutex to prevent simultaneous update from multiple threads with self._mutex: # Only update every update_interval if (time.time() - self._updatets) >= self._scan_interval: _LOGGER.debug("Updating") self._updatets = time.time() # fetch JSON data try: with requests.Session() as sess: # call-out response = sess.send(self._device_data_request, timeout=10) # process data json_data = response.text self.data = json.loads(json_data) except Exception as ex: _LOGGER.error("Max! Home Automation connection failed - Cube: {}, Device: {}, JSON data: {}".format (self._cube_hex_address, self._device_hex_address, ex)) self.data = None # set next try to 60times scan interval self._updatets = time.time() + (59 * self._scan_interval) return False else: _LOGGER.debug("Skipping update") class MaxHomeAutomationCubeHandler: """Keep the cube instance in one place and centralize the update.""" def __init__(self, gateway_base_url, cube_hex_address, scan_interval): """Initialize the Cube Handle.""" # store initial values self._gateway_base_url = gateway_base_url; self._cube_hex_address = cube_hex_address; self._scan_interval = scan_interval # MAX! Home Automation MAX! Cube Duty URL cube_duty_url = self._gateway_base_url + "get-duty?cube={}".format(self._cube_hex_address) self._cube_duty_request = requests.Request(DEFAULT_METHOD, cube_duty_url).prepare() # JSON data initial value self.cube_duty = None # thread synchronization stuff self._mutex = Lock() # initially not actual self._updatets = time.time() - self._scan_interval; def update(self): """Pull the latest data from the MAX! Home Automation.""" # Acquire mutex to prevent simultaneous update from multiple threads with self._mutex: # Only update every update_interval if (time.time() - self._updatets) >= self._scan_interval: _LOGGER.debug("Updating") self._updatets = time.time() # fetch Duty data try: with requests.Session() as sess: # call-out response = sess.send(self._cube_duty_request, timeout=10) # process data self.cube_duty = response.text except Exception as ex: _LOGGER.error("Max! Home Automation connection failed - Cube: {}, duty data: {}".format (self._cube_hex_address, ex)) self.cube_duty = None # set next try to 60times scan interval self._updatets = time.time() + (59 * self._scan_interval) return False else: _LOGGER.debug("Skipping update")