From e3304caf0695a95601bcd04809f4f26bb89bbf6d Mon Sep 17 00:00:00 2001 From: MakeMeASandwich Date: Sat, 17 Oct 2015 19:36:52 +0200 Subject: [PATCH 01/44] add hyperion light support --- .coveragerc | 1 + homeassistant/components/light/hyperion.py | 135 +++++++++++++++++++++ homeassistant/util/color.py | 40 ++++++ 3 files changed, 176 insertions(+) create mode 100644 homeassistant/components/light/hyperion.py diff --git a/.coveragerc b/.coveragerc index 7e474d287c2..d472423c950 100644 --- a/.coveragerc +++ b/.coveragerc @@ -49,6 +49,7 @@ omit = homeassistant/components/light/hue.py homeassistant/components/light/limitlessled.py homeassistant/components/light/blinksticklight.py + homeassistant/components/light/hyperion.py homeassistant/components/media_player/cast.py homeassistant/components/media_player/denon.py homeassistant/components/media_player/firetv.py diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py new file mode 100644 index 00000000000..b11485ff8d5 --- /dev/null +++ b/homeassistant/components/light/hyperion.py @@ -0,0 +1,135 @@ +""" +homeassistant.components.light.hyperion +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for Hyperion remotes. + +Configuration: + +To connect to [a Hyperion server](https://github.com/tvdzwan/hyperion) you +will need to add something like the following to your configuration.yaml file: + +light: + platform: hyperion + host: 192.168.1.98 + port: 19444 + +The JSON server port is 19444 by default. +""" +import logging +import socket +import json + +from homeassistant.const import CONF_HOST +from homeassistant.components.light import (Light, ATTR_XY_COLOR, + ATTR_BRIGHTNESS) +from homeassistant.util.color import color_RGB_to_xy, \ + color_xy_brightness_to_RGB + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = [] + + +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Sets up a Hyperion server remote """ + host = config.get(CONF_HOST, None) + port = config.get("port", 19444) + add_devices_callback([Hyperion(host, port)]) + + +class Hyperion(Light): + """ Represents a Hyperion remote """ + + def __init__(self, host, port): + self._host = host + self._port = port + self._name = "unknown" + self._is_available = False + self._xy_color = color_RGB_to_xy(0, 0, 0) + self._brightness = 255 + + @property + def name(self): + """ Get the hostname of the server. """ + return self._name + + @property + def color_xy(self): + """ XY color value. """ + return self._xy_color + + @property + def brightness(self): + """ Brightness. """ + return self._brightness + + @property + def is_on(self): + """ True if device is on. """ + self.check_remote() + return self._is_available + + def turn_on(self, **kwargs): + """ Turn the lights on. """ + if self._is_available: + if ATTR_XY_COLOR in kwargs: + self._xy_color = kwargs[ATTR_XY_COLOR] + if ATTR_BRIGHTNESS in kwargs: + self._brightness = kwargs[ATTR_BRIGHTNESS] + self.update_remote() + + def turn_off(self, **kwargs): + """ Disconnect the remote. """ + self.json_request({"command": "clearall"}) + + def check_remote(self): + """ Ping the remote and gets the hostname. """ + response = self.json_request({"command": "serverinfo"}) + if response: + self._name = response["info"]["hostname"] + + def update_remote(self): + """ Set the remote's lights. """ + rgb = color_xy_brightness_to_RGB(self._xy_color[0], self._xy_color[1], + self._brightness) + rgb = [int(c) for c in rgb] + self.json_request( + {"command": "color", "priority": 128, "color": rgb}) + + def json_request(self, request, wait_for_response=False): + """ Communicate with the json server. """ + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + sock.connect((self._host, self._port)) + except OSError: + self._is_available = False + return None + + sock.send(bytearray(json.dumps(request) + "\n", "utf-8")) + + try: + buf = sock.recv(4096) + except socket.timeout: + return None + + buffering = True + while buffering: + if "\n" in str(buf, "utf-8"): + response = str(buf, "utf-8").split("\n")[0] + buffering = False + else: + try: + more = sock.recv(4096) + except socket.timeout: + more = None + if not more: + buffering = False + response = str(buf, "utf-8") + else: + buf += more + + sock.close() + + j = json.loads(response) + self._is_available = True + return j diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index 5f967fa87b9..26dca7ab0c6 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -39,3 +39,43 @@ def color_RGB_to_xy(R, G, B): # Convert XYZ to xy, see CIE 1931 color space on wikipedia return X / (X + Y + Z), Y / (X + Y + Z) + + +# taken from +# https://github.com/benknight/hue-python-rgb-converter/blob/master/rgb_cie.py +# pylint: disable=bad-builtin +def color_xy_brightness_to_RGB(vX, vY, brightness): + ''' + Convert from XYZ to RGB. + ''' + brightness /= 255. + if brightness == 0: + return (0, 0, 0) + + Y = brightness + X = (Y / vY) * vX + Z = (Y / vY) * (1 - vX - vY) + + # Convert to RGB using Wide RGB D65 conversion. + r = X * 1.612 - Y * 0.203 - Z * 0.302 + g = -X * 0.509 + Y * 1.412 + Z * 0.066 + b = X * 0.026 - Y * 0.072 + Z * 0.962 + + # Apply reverse gamma correction. + r, g, b = map( + lambda x: (12.92 * x) if (x <= 0.0031308) else + ((1.0 + 0.055) * pow(x, (1.0 / 2.4)) - 0.055), + [r, g, b] + ) + + # Bring all negative components to zero. + r, g, b = map(lambda x: max(0, x), [r, g, b]) + + # If one component is greater than 1, weight components by that value. + max_component = max(r, g, b) + if max_component > 1: + r, g, b = map(lambda x: x / max_component, [r, g, b]) + + r, g, b = map(lambda x: int(x * 255), [r, g, b]) + + return (r, g, b) From 7141a9992762a797b2e7077e69098f604eff62fa Mon Sep 17 00:00:00 2001 From: MakeMeASandwich Date: Sun, 18 Oct 2015 09:10:41 +0200 Subject: [PATCH 02/44] fix flake warning --- homeassistant/components/light/hyperion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index b11485ff8d5..ddd98661155 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -22,8 +22,8 @@ import json from homeassistant.const import CONF_HOST from homeassistant.components.light import (Light, ATTR_XY_COLOR, ATTR_BRIGHTNESS) -from homeassistant.util.color import color_RGB_to_xy, \ - color_xy_brightness_to_RGB +from homeassistant.util.color import (color_RGB_to_xy, + color_xy_brightness_to_RGB) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [] From 2e9ee2863734fa3e1cec6e450c0888a8e3f3887f Mon Sep 17 00:00:00 2001 From: MakeMeASandwich Date: Tue, 20 Oct 2015 17:30:23 +0200 Subject: [PATCH 03/44] light/hyperion: use RGB, clean code --- homeassistant/components/light/hyperion.py | 86 +++++++++++----------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index ddd98661155..eed30f8b2d0 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -20,10 +20,7 @@ import socket import json from homeassistant.const import CONF_HOST -from homeassistant.components.light import (Light, ATTR_XY_COLOR, - ATTR_BRIGHTNESS) -from homeassistant.util.color import (color_RGB_to_xy, - color_xy_brightness_to_RGB) +from homeassistant.components.light import (Light, ATTR_RGB_COLOR) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = [] @@ -33,7 +30,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Sets up a Hyperion server remote """ host = config.get(CONF_HOST, None) port = config.get("port", 19444) - add_devices_callback([Hyperion(host, port)]) + device = Hyperion(host, port) + if device.setup(): + add_devices_callback([device]) + else: + return False class Hyperion(Light): @@ -42,76 +43,74 @@ class Hyperion(Light): def __init__(self, host, port): self._host = host self._port = port - self._name = "unknown" - self._is_available = False - self._xy_color = color_RGB_to_xy(0, 0, 0) - self._brightness = 255 + self._name = host + self._is_available = True + self._rgb_color = [255, 255, 255] @property def name(self): - """ Get the hostname of the server. """ + """ Return the hostname of the server. """ return self._name @property - def color_xy(self): - """ XY color value. """ - return self._xy_color - - @property - def brightness(self): - """ Brightness. """ - return self._brightness + def rgb_color(self): + """ Last RGB color value set. """ + return self._rgb_color @property def is_on(self): - """ True if device is on. """ - self.check_remote() + """ True if the device is online. """ return self._is_available def turn_on(self, **kwargs): """ Turn the lights on. """ if self._is_available: - if ATTR_XY_COLOR in kwargs: - self._xy_color = kwargs[ATTR_XY_COLOR] - if ATTR_BRIGHTNESS in kwargs: - self._brightness = kwargs[ATTR_BRIGHTNESS] - self.update_remote() + if ATTR_RGB_COLOR in kwargs: + self._rgb_color = kwargs[ATTR_RGB_COLOR] + + self.json_request({"command": "color", "priority": 128, + "color": self._rgb_color}) def turn_off(self, **kwargs): """ Disconnect the remote. """ self.json_request({"command": "clearall"}) - def check_remote(self): - """ Ping the remote and gets the hostname. """ + def update(self): + """ Ping the remote. """ + # just see if the remote port is open + self._is_available = self.json_request() + + def setup(self): + """ Get the hostname of the remote. """ response = self.json_request({"command": "serverinfo"}) if response: self._name = response["info"]["hostname"] + return True - def update_remote(self): - """ Set the remote's lights. """ - rgb = color_xy_brightness_to_RGB(self._xy_color[0], self._xy_color[1], - self._brightness) - rgb = [int(c) for c in rgb] - self.json_request( - {"command": "color", "priority": 128, "color": rgb}) + return False - def json_request(self, request, wait_for_response=False): + def json_request(self, request=None, wait_for_response=False): """ Communicate with the json server. """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(5) sock.connect((self._host, self._port)) except OSError: - self._is_available = False - return None + return False + + if not request: + # no communication needed, simple presence detection returns True + return True sock.send(bytearray(json.dumps(request) + "\n", "utf-8")) - try: buf = sock.recv(4096) except socket.timeout: - return None + # something is wrong, assume it's offline + return False + # read until a newline or timeout buffering = True while buffering: if "\n" in str(buf, "utf-8"): @@ -129,7 +128,4 @@ class Hyperion(Light): buf += more sock.close() - - j = json.loads(response) - self._is_available = True - return j + return json.loads(response) From 29c9c5a7eca4854940f695e76b8fe48f172d06d5 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Fri, 23 Oct 2015 17:01:42 +0200 Subject: [PATCH 04/44] Add new OpenWRT presence detection routine based on ubus instead of luci --- .../components/device_tracker/ubus.py | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 homeassistant/components/device_tracker/ubus.py diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py new file mode 100644 index 00000000000..1e650297399 --- /dev/null +++ b/homeassistant/components/device_tracker/ubus.py @@ -0,0 +1,171 @@ +""" +homeassistant.components.device_tracker.ubus +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Device tracker platform that supports scanning a OpenWRT router for device +presence. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.ubus.html +""" +import logging +import json +from datetime import timedelta +import re +import threading +import requests + +from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD +from homeassistant.helpers import validate_config +from homeassistant.util import Throttle +from homeassistant.components.device_tracker import DOMAIN + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) + +_LOGGER = logging.getLogger(__name__) + + +def get_scanner(hass, config): + """ Validates config and returns a Luci scanner. """ + if not validate_config(config, + {DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]}, + _LOGGER): + return None + + scanner = UbusDeviceScanner(config[DOMAIN]) + + return scanner if scanner.success_init else None + + +# pylint: disable=too-many-instance-attributes +class UbusDeviceScanner(object): + """ + This class queries a wireless router running OpenWrt firmware + for connected devices. Adapted from Tomato scanner. + + Configure your routers' ubus ACL based on following instructions: + + http://wiki.openwrt.org/doc/techref/ubus + + Read only access will be fine. + + To use this class you have to install rpcd-mod-file package in your OpenWrt router: + + opkg install rpcd-mod-file + + """ + + def __init__(self, config): + host = config[CONF_HOST] + username, password = config[CONF_USERNAME], config[CONF_PASSWORD] + + self.parse_api_pattern = re.compile(r"(?P\w*) = (?P.*);") + self.lock = threading.Lock() + self.last_results = {} + self.url = 'http://{}/ubus'.format(host) + + self.session_id= _get_session_id(self.url, username, password) + self.hostapd = [] + self.leasefile = None + self.mac2name = None + self.success_init = self.session_id is not None + + def scan_devices(self): + """ + Scans for new devices and return a list containing found device ids. + """ + + self._update_info() + + return self.last_results + + def get_device_name(self, device): + """ Returns the name of the given device or None if we don't know. """ + + with self.lock: + if self.leasefile is None: + result = _req_json_rpc(self.url, self.session_id, 'call', 'uci', 'get', config="dhcp", type="dnsmasq") + if result: + self.leasefile=next (iter (result["values"].values()))["leasefile"] + else: + return + + if self.mac2name is None: + result = _req_json_rpc(self.url, self.session_id, 'call', 'file', 'read', path=self.leasefile) + + if result: + self.mac2name = dict() + for line in result["data"].splitlines(): + [time, mac, ip, name, lid] = line.split(" ") + self.mac2name[mac.upper()] = name + else: + # Error, handled in the _req_json_rpc + return + + return self.mac2name.get(device.upper(), None) + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """ + Ensures the information from the Luci router is up to date. + Returns boolean if scanning successful. + """ + if not self.success_init: + return False + + with self.lock: + _LOGGER.info("Checking ARP") + + if not self.hostapd: + hostapd = _req_json_rpc(self.url, self.session_id, 'list', 'hostapd.*', '') + for key in hostapd.keys(): + self.hostapd.append(key) + + self.last_results = [] + results = 0 + for hostapd in self.hostapd: + result = _req_json_rpc(self.url, self.session_id, 'call', hostapd, 'get_clients') + + if result: + results = results + 1 + for key in result["clients"].keys(): + self.last_results.append(key) + + if results: + return True + else: + return False + +def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): + """ Perform one JSON RPC operation. """ + + data = json.dumps({ "jsonrpc": "2.0", + "id": 1, + "method": rpcmethod, + "params": [ session_id, + subsystem, + method, + params] + }) + + try: + res = requests.post(url, data=data, timeout=5) + + except requests.exceptions.Timeout: + return + + if res.status_code == 200: + response = res.json() + + if (rpcmethod == "call"): + return response["result"][1] + else: + return response["result"] + +def _get_session_id(url, username, password): + """ Get authentication token for the given host+username+password. """ + res = _req_json_rpc(url, "00000000000000000000000000000000", 'call', 'session', 'login', username=username, password=password) + return res["ubus_rpc_session"] + + +# root@dom:~# ubus call uci get '{ "config": "dhcp", "type": "dnsmasq" }' \ No newline at end of file From 50fbd83b3d7c131744c90cc40bc68cbc823d9eee Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Sat, 24 Oct 2015 11:20:57 +0200 Subject: [PATCH 05/44] corrected flake8 warnings --- .../components/device_tracker/ubus.py | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index 1e650297399..a231af02be7 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -46,13 +46,14 @@ class UbusDeviceScanner(object): Configure your routers' ubus ACL based on following instructions: http://wiki.openwrt.org/doc/techref/ubus - + Read only access will be fine. - - To use this class you have to install rpcd-mod-file package in your OpenWrt router: - + + To use this class you have to install rpcd-mod-file package + in your OpenWrt router: + opkg install rpcd-mod-file - + """ def __init__(self, config): @@ -64,9 +65,9 @@ class UbusDeviceScanner(object): self.last_results = {} self.url = 'http://{}/ubus'.format(host) - self.session_id= _get_session_id(self.url, username, password) + self.session_id = _get_session_id(self.url, username, password) self.hostapd = [] - self.leasefile = None + self.leasefile = None self.mac2name = None self.success_init = self.session_id is not None @@ -84,24 +85,28 @@ class UbusDeviceScanner(object): with self.lock: if self.leasefile is None: - result = _req_json_rpc(self.url, self.session_id, 'call', 'uci', 'get', config="dhcp", type="dnsmasq") + result = _req_json_rpc(self.url, self.session_id, + 'call', 'uci', 'get', + config="dhcp", type="dnsmasq") if result: - self.leasefile=next (iter (result["values"].values()))["leasefile"] + self.leasefile = next(iter(result["values"]. + values()))["leasefile"] else: - return - + return + if self.mac2name is None: - result = _req_json_rpc(self.url, self.session_id, 'call', 'file', 'read', path=self.leasefile) - + result = _req_json_rpc(self.url, self.session_id, + 'call', 'file', 'read', + path=self.leasefile) if result: self.mac2name = dict() for line in result["data"].splitlines(): [time, mac, ip, name, lid] = line.split(" ") - self.mac2name[mac.upper()] = name + self.mac2name[mac.upper()] = name else: # Error, handled in the _req_json_rpc return - + return self.mac2name.get(device.upper(), None) @Throttle(MIN_TIME_BETWEEN_SCANS) @@ -115,57 +120,60 @@ class UbusDeviceScanner(object): with self.lock: _LOGGER.info("Checking ARP") - + if not self.hostapd: - hostapd = _req_json_rpc(self.url, self.session_id, 'list', 'hostapd.*', '') + hostapd = _req_json_rpc(self.url, self.session_id, + 'list', 'hostapd.*', '') for key in hostapd.keys(): self.hostapd.append(key) - + self.last_results = [] - results = 0 + results = 0 for hostapd in self.hostapd: - result = _req_json_rpc(self.url, self.session_id, 'call', hostapd, 'get_clients') - + result = _req_json_rpc(self.url, self.session_id, + 'call', hostapd, 'get_clients') + if result: - results = results + 1 + results = results + 1 for key in result["clients"].keys(): self.last_results.append(key) - - if results: + + if results: return True else: return False + def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): """ Perform one JSON RPC operation. """ - - data = json.dumps({ "jsonrpc": "2.0", - "id": 1, - "method": rpcmethod, - "params": [ session_id, - subsystem, - method, - params] - }) + + data = json.dumps({"jsonrpc": "2.0", + "id": 1, + "method": rpcmethod, + "params": [session_id, + subsystem, + method, + params] + }) try: res = requests.post(url, data=data, timeout=5) - + except requests.exceptions.Timeout: return - + if res.status_code == 200: response = res.json() - + if (rpcmethod == "call"): return response["result"][1] else: return response["result"] + def _get_session_id(url, username, password): """ Get authentication token for the given host+username+password. """ - res = _req_json_rpc(url, "00000000000000000000000000000000", 'call', 'session', 'login', username=username, password=password) + res = _req_json_rpc(url, "00000000000000000000000000000000", 'call', + 'session', 'login', username=username, + password=password) return res["ubus_rpc_session"] - - -# root@dom:~# ubus call uci get '{ "config": "dhcp", "type": "dnsmasq" }' \ No newline at end of file From 1be48f54c04382ed0badc038ffd04918f2c1315b Mon Sep 17 00:00:00 2001 From: MakeMeASandwich Date: Sun, 25 Oct 2015 11:08:59 +0100 Subject: [PATCH 06/44] light/hyperion: close sockets, report setup success --- homeassistant/components/light/hyperion.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index eed30f8b2d0..fe48d58f945 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -33,6 +33,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): device = Hyperion(host, port) if device.setup(): add_devices_callback([device]) + return True else: return False @@ -97,10 +98,12 @@ class Hyperion(Light): try: sock.connect((self._host, self._port)) except OSError: + sock.close() return False if not request: # no communication needed, simple presence detection returns True + sock.close() return True sock.send(bytearray(json.dumps(request) + "\n", "utf-8")) @@ -108,6 +111,7 @@ class Hyperion(Light): buf = sock.recv(4096) except socket.timeout: # something is wrong, assume it's offline + sock.close() return False # read until a newline or timeout From a236b87ccfb779cf4a41569843d41dc16383dcfb Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 13 Oct 2015 21:59:13 +0000 Subject: [PATCH 07/44] new attempt for PR --- homeassistant/components/media_player/plex.py | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 5fac9ecb0f0..33bab1954ee 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -16,9 +16,7 @@ from homeassistant.const import ( STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN) import homeassistant.util as util -REQUIREMENTS = ['https://github.com/adrienbrault/python-plexapi/archive/' - 'df2d0847e801d6d5cda920326d693cf75f304f1a.zip' - '#python-plexapi==1.0.2'] +REQUIREMENTS = ['plexapi==1.1.0'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) @@ -45,24 +43,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): def update_devices(): """ Updates the devices objects. """ try: - devices = plexuser.devices() + devices = plexserver.clients() except BadRequest: _LOGGER.exception("Error listing plex devices") return new_plex_clients = [] for device in devices: - if (all(x not in ['client', 'player'] for x in device.provides) - or 'PlexAPI' == device.product): + # For now, let's allow all deviceClass types + if device.deviceClass in []: continue - if device.clientIdentifier not in plex_clients: + if device.machineIdentifier not in plex_clients: new_client = PlexClient(device, plex_sessions, update_devices, update_sessions) - plex_clients[device.clientIdentifier] = new_client + plex_clients[device.machineIdentifier] = new_client new_plex_clients.append(new_client) else: - plex_clients[device.clientIdentifier].set_device(device) + plex_clients[device.machineIdentifier].set_device(device) if new_plex_clients: add_devices(new_plex_clients) @@ -101,10 +99,10 @@ class PlexClient(MediaPlayerDevice): @property def session(self): """ Returns the session, if any. """ - if self.device.clientIdentifier not in self.plex_sessions: + if self.device.machineIdentifier not in self.plex_sessions: return None - return self.plex_sessions[self.device.clientIdentifier] + return self.plex_sessions[self.device.machineIdentifier] @property def name(self): @@ -120,7 +118,8 @@ class PlexClient(MediaPlayerDevice): return STATE_PLAYING elif state == 'paused': return STATE_PAUSED - elif self.device.isReachable: + # This is nasty. Need ti find a way to determine alive + elif self.device: return STATE_IDLE else: return STATE_OFF @@ -196,16 +195,16 @@ class PlexClient(MediaPlayerDevice): def media_play(self): """ media_play media player. """ - self.device.play({'type': 'video'}) + self.device.play() def media_pause(self): """ media_pause media player. """ - self.device.pause({'type': 'video'}) + self.device.pause() def media_next_track(self): """ Send next track command. """ - self.device.skipNext({'type': 'video'}) + self.device.skipNext() def media_previous_track(self): """ Send previous track command. """ - self.device.skipPrevious({'type': 'video'}) + self.device.skipPrevious() From 8e9cafd29d02488a62026a22b880194338e90760 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Fri, 16 Oct 2015 18:15:04 +0000 Subject: [PATCH 08/44] Updated requirements_all.txt. Added placeholder to the empty deviceClass filter. Will remove this if deemed unneeded, later --- homeassistant/components/media_player/plex.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 33bab1954ee..21890524348 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): new_plex_clients = [] for device in devices: # For now, let's allow all deviceClass types - if device.deviceClass in []: + if device.deviceClass in ['badClient']: continue if device.machineIdentifier not in plex_clients: diff --git a/requirements_all.txt b/requirements_all.txt index c63eea25853..11d91043d12 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -134,7 +134,7 @@ https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5 SoCo==0.11.1 # PlexAPI (media_player.plex) -https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 +plexapi==1.1.0 # SNMP (device_tracker.snmp) pysnmp==4.2.5 From db7e46abd1686194e3b0d61b8897d5a79704d061 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Sun, 18 Oct 2015 20:02:18 +0000 Subject: [PATCH 09/44] Intermediate save --- homeassistant/components/discovery.py | 2 + homeassistant/components/media_player/plex.py | 71 +++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 0b3cc1025cc..9fc7ee6651c 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -28,6 +28,7 @@ SERVICE_HUE = 'philips_hue' SERVICE_CAST = 'google_cast' SERVICE_NETGEAR = 'netgear_router' SERVICE_SONOS = 'sonos' +SERVICE_PLEX = 'plex' SERVICE_HANDLERS = { SERVICE_WEMO: "switch", @@ -35,6 +36,7 @@ SERVICE_HANDLERS = { SERVICE_HUE: "light", SERVICE_NETGEAR: 'device_tracker', SERVICE_SONOS: 'media_player', + SERVICE_PLEX: 'media_player', } diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 21890524348..b18814a8ced 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -20,6 +20,8 @@ REQUIREMENTS = ['plexapi==1.1.0'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) +PLEX_CONFIG_FILE = 'plex.conf' + _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK @@ -28,14 +30,73 @@ SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK # pylint: disable=abstract-method, unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the plex platform. """ + try: + # pylint: disable=unused-variable + from plexapi.myplex import MyPlexUser + from plexapi.exceptions import BadRequest + except ImportError: + _LOGGER.exception("Error while importing dependency plexapi.") + return + + if discovery_info is not None: + host = urlparse(discovery_info[1]).url + _LOGGER.error('Discovered PLEX server: %s'%host) + else: + # 'name' is currently used for plexserver + # This indicates old config method + host = config.get('name','') + + if host in _CONFIGURING: + return + + setup_plexserver(host, hass, add_devices) + + +def setup_plexserver(host, hass, add_devices): + ''' Setup a plexserver based on host parameter''' from plexapi.myplex import MyPlexUser + from plexapi.server import PlexServer from plexapi.exceptions import BadRequest - name = config.get('name', '') - user = config.get('user', '') - password = config.get('password', '') - plexuser = MyPlexUser.signin(user, password) - plexserver = plexuser.getResource(name).connect() + conf_file = hass.config.path(PHUE_CONFIG_FILE)) + + # Compatability mode. If there's name, user, etc set in + # configuration, let's use those, not to break anything + # We may want to use this method as option when HA's + # configuration options increase + if config.get('name', ''): + name = config.get('name', '') + user = config.get('user', '') + password = config.get('password', '') + plexuser = MyPlexUser.signin(user, password) + plexserver = plexuser.getResource(name).connect() + + # Discovery mode. Parse config file, attempt conenction + # Request configuration on connect fail + else: + + try: + # Get configuration from config file + # FIXME unauthenticated plex servers dont require + # a token, so config file isn't mandatory + with open(conf_file,'r') as f: + conf_dict = eval(f.read()) + + plexserver = PlexServer( + host, + conf_dict.get(host)['token']) + except IOError: # File not found + + except NotFound: # Wrong host was given or need token? + _LOGGER.exception("Error connecting to the Hue bridge at %s", host) + return + + except phue.PhueRegistrationException: + _LOGGER.warning("Connected to Hue at %s but not registered.", host) + + request_configuration(host, hass, add_devices_callback) + return + plex_clients = {} plex_sessions = {} From 6a82504e5e1890c67f8e08ce67a9f7e6ef9f19b4 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 20 Oct 2015 16:59:22 +0000 Subject: [PATCH 10/44] further discovery integration into plex --- homeassistant/components/discovery.py | 2 +- .../components/media_player/__init__.py | 1 + homeassistant/components/media_player/plex.py | 45 ++++++++++++++----- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 9fc7ee6651c..1e04f20ea3e 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -28,7 +28,7 @@ SERVICE_HUE = 'philips_hue' SERVICE_CAST = 'google_cast' SERVICE_NETGEAR = 'netgear_router' SERVICE_SONOS = 'sonos' -SERVICE_PLEX = 'plex' +SERVICE_PLEX = 'plex_mediaserver' SERVICE_HANDLERS = { SERVICE_WEMO: "switch", diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 294fccbb1f5..8040ef9c067 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -28,6 +28,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}' DISCOVERY_PLATFORMS = { discovery.SERVICE_CAST: 'cast', discovery.SERVICE_SONOS: 'sonos', + discovery.SERVICE_PLEX: 'plex', } SERVICE_YOUTUBE_VIDEO = 'play_youtube_video' diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index b18814a8ced..ae619e64355 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -8,6 +8,7 @@ https://home-assistant.io/components/media_player.plex.html """ import logging from datetime import timedelta +from urllib.parse import urlparse from homeassistant.components.media_player import ( MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, @@ -22,6 +23,8 @@ MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) PLEX_CONFIG_FILE = 'plex.conf' +# Map ip to request id for configuring +_CONFIGURING = {} _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK @@ -39,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return if discovery_info is not None: - host = urlparse(discovery_info[1]).url + host = urlparse(discovery_info[1]).netloc _LOGGER.error('Discovered PLEX server: %s'%host) else: # 'name' is currently used for plexserver @@ -49,16 +52,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if host in _CONFIGURING: return - setup_plexserver(host, hass, add_devices) + setup_plexserver(host, config, hass, add_devices) def setup_plexserver(host, hass, add_devices): ''' Setup a plexserver based on host parameter''' - from plexapi.myplex import MyPlexUser from plexapi.server import PlexServer from plexapi.exceptions import BadRequest - conf_file = hass.config.path(PHUE_CONFIG_FILE)) + conf_file = hass.config.path(PLEX_CONFIG_FILE) # Compatability mode. If there's name, user, etc set in # configuration, let's use those, not to break anything @@ -75,6 +77,7 @@ def setup_plexserver(host, hass, add_devices): # Request configuration on connect fail else: + print('WEEEEJ, host: %s'%host) try: # Get configuration from config file # FIXME unauthenticated plex servers dont require @@ -83,20 +86,16 @@ def setup_plexserver(host, hass, add_devices): conf_dict = eval(f.read()) plexserver = PlexServer( - host, + 'http://%s'%host, conf_dict.get(host)['token']) except IOError: # File not found + request_configuration(host, hass, add_devices_callback) + return except NotFound: # Wrong host was given or need token? _LOGGER.exception("Error connecting to the Hue bridge at %s", host) return - except phue.PhueRegistrationException: - _LOGGER.warning("Connected to Hue at %s but not registered.", host) - - request_configuration(host, hass, add_devices_callback) - return - plex_clients = {} plex_sessions = {} @@ -143,6 +142,30 @@ def setup_plexserver(host, hass, add_devices): update_sessions() +def request_configuration(host, hass, add_devices_callback): + """ Request configuration steps from the user. """ + configurator = get_component('configurator') + + # We got an error if this method is called while we are configuring + if host in _CONFIGURING: + configurator.notify_errors( + _CONFIGURING[host], "Failed to register, please try again.") + + return + + def plex_configuration_callback(data): + """ Actions to do when our configuration callback is called. """ + setup_plexserrver(host, hass, add_devices_callback) + + _CONFIGURING[host] = configurator.request_config( + hass, "Plex Media Server", plex_configuration_callback, + description=("Enter the X-Plex-Token as descrobed here
" + 'Plex documentation'), + description_image="/static/images/config_plexserver.jpg", + submit_caption="I have pressed the button" + ) + + class PlexClient(MediaPlayerDevice): """ Represents a Plex device. """ From 884525df33ffb939b354177d6f99ead8ccb28840 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 22 Oct 2015 21:16:04 +0000 Subject: [PATCH 11/44] Basic discovery works, added plex logo for configurator. Missing configurator support for fields. Todo: config save on successful connect --- .../images/config_plex_mediaserver.png | Bin 0 -> 18619 bytes homeassistant/components/media_player/plex.py | 102 ++++++++---------- 2 files changed, 47 insertions(+), 55 deletions(-) create mode 100644 homeassistant/components/frontend/www_static/images/config_plex_mediaserver.png diff --git a/homeassistant/components/frontend/www_static/images/config_plex_mediaserver.png b/homeassistant/components/frontend/www_static/images/config_plex_mediaserver.png new file mode 100644 index 0000000000000000000000000000000000000000..97a1b4b352cdbf2629e1612db15e30bcf2af18a4 GIT binary patch literal 18619 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" - 'Plex documentation'), - description_image="/static/images/config_plexserver.jpg", - submit_caption="I have pressed the button" + description=('Enter the X-Plex-Token'), + description_image="/static/images/config_plex_mediaserver.png", + submit_caption="Confirm", + fields=[{'Token':'token'}] ) From 847d9736aa002a6db025d0b4633eccf373231b56 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Sun, 25 Oct 2015 10:45:15 +0000 Subject: [PATCH 12/44] Configurator works, config saving basic implementation --- homeassistant/components/media_player/plex.py | 129 +++++++++++------- 1 file changed, 83 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 4651a963fea..dc9b7e82070 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -6,7 +6,7 @@ Provides an interface to the Plex API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/media_player.plex.html """ -import logging +import logging, json, os from datetime import timedelta from urllib.parse import urlparse @@ -31,60 +31,91 @@ _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK +def config_from_file(filename, config=None): + ''' Small configuration file management function''' + if config: + # We're writing configuration + try: + with open(filename,'w') as f: + f.write(json.dumps(config)) + except IOError as e: + _LOGGER.error('Saving config file failed: %s'%e) + return False + return True + else: + # We're reading config + if os.path.isfile(filename): + try: + with open(filename,'r') as f: + return json.loads(f.read()) + except IOError as e: + _LOGGER.error('Reading config file failed: %s'%e) + return False + else: + return {} + # pylint: disable=abstract-method, unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Sets up the plex platform. """ + # Via discovery if discovery_info is not None: # Parse discovery data host = urlparse(discovery_info[1]).netloc _LOGGER.info('Discovered PLEX server: %s'%host) - else: - host = config.get(CONF_HOST, None) - if host in _CONFIGURING: - return - - setup_plexserver(host, hass, add_devices_callback) - - -def setup_plexserver(host, hass, add_devices_callback): - ''' Setup a plexserver based on host parameter''' - import plexapi - - # Config parsing & discovery mix - conf_file = hass.config.path(PLEX_CONFIG_FILE) - try: - with open(conf_file,'r') as f: - conf_dict = eval(f.read()) - except IOError: # File not found - if host == None: - # No discovery, no config, quit here + if host in _CONFIGURING: return - conf_dict = {} - - if host == None: - # Called by module inclusion, let's only use config - host,token = conf_dict.popitem() - token = token['token'] - elif host not in conf_dict.keys(): - # Not in config - conf_dict[host] = { 'token' : '' } token = None + else: + # Setup a configured PlexServer + config = config_from_file(hass.config.path(PLEX_CONFIG_FILE)) + if len(config): + host,token = config.popitem() + token = token['token'] + else: + # Empty config file? + return + + setup_plexserver(host, token, hass, add_devices_callback) + + +def setup_plexserver(host, token, hass, add_devices_callback): + ''' Setup a plexserver based on host parameter''' + from plexapi.server import PlexServer + from plexapi.exceptions import BadRequest + import plexapi + _LOGGER.info('Connecting to: htts://%s using token: %s' % (host, token)) try: - plexserver = plexapi.PlexServer('http://%s'%host, token) - except Exception: + plexserver = plexapi.server.PlexServer('http://%s'%host, token) + except (plexapi.exceptions.BadRequest, + plexapi.exceptions.Unauthorized, + plexapi.exceptions.NotFound) as e: + _LOGGER.info(e) + # No token or wrong token request_configuration(host, hass, add_devices_callback) return - except plexapi.exceptions.BadRequest as e: - _LOGGER.error('BLABLA1') - request_configuration(host, hass, add_devices_callback) + except Exception as e: + _LOGGER.error('Misc Exception : %s'%e) return + # If we came here and configuring this host, mark as done + if host in _CONFIGURING: + request_id = _CONFIGURING.pop(host) + configurator = get_component('configurator') + configurator.request_done(request_id) + _LOGGER.info('Discovery configuration done!') + + # Save config + if not config_from_file( + hass.config.path(PLEX_CONFIG_FILE), + {host: {'token': token}}): + _LOGGER.error('failed to save config file') + _LOGGER.info('Connected to: htts://%s using token: %s' % (host, token)) @@ -96,7 +127,7 @@ def setup_plexserver(host, hass, add_devices_callback): """ Updates the devices objects. """ try: devices = plexserver.clients() - except BadRequest: + except plexapi.exceptions.BadRequest: _LOGGER.exception("Error listing plex devices") return @@ -115,14 +146,14 @@ def setup_plexserver(host, hass, add_devices_callback): plex_clients[device.machineIdentifier].set_device(device) if new_plex_clients: - add_devices(new_plex_clients) + add_devices_callback(new_plex_clients) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_sessions(): """ Updates the sessions objects. """ try: sessions = plexserver.sessions() - except BadRequest: + except plexapi.exceptions.BadRequest: _LOGGER.exception("Error listing plex sessions") return @@ -147,14 +178,14 @@ def request_configuration(host, hass, add_devices_callback): def plex_configuration_callback(data): """ Actions to do when our configuration callback is called. """ - setup_plexserver(host, hass, add_devices_callback) + setup_plexserver(host, data.get('token'), hass, add_devices_callback) _CONFIGURING[host] = configurator.request_config( hass, "Plex Media Server", plex_configuration_callback, description=('Enter the X-Plex-Token'), description_image="/static/images/config_plex_mediaserver.png", submit_caption="Confirm", - fields=[{'Token':'token'}] + fields=[{'id': 'token', 'name':'X-Plex-Token', 'type':''}] ) @@ -172,6 +203,17 @@ class PlexClient(MediaPlayerDevice): """ Sets the device property. """ self.device = device + @property + def unique_id(self): + """ Returns the id of this plex client """ + return "{}.{}".format( + self.__class__, self.device.machineIdentifier or self.device.name ) + + @property + def name(self): + """ Returns the name of the device. """ + return self.device.name or self.device.product or self.device.deviceClass + @property def session(self): """ Returns the session, if any. """ @@ -180,11 +222,6 @@ class PlexClient(MediaPlayerDevice): return self.plex_sessions[self.device.machineIdentifier] - @property - def name(self): - """ Returns the name of the device. """ - return self.device.name or self.device.product or self.device.device - @property def state(self): """ Returns the state of the device. """ @@ -194,7 +231,7 @@ class PlexClient(MediaPlayerDevice): return STATE_PLAYING elif state == 'paused': return STATE_PAUSED - # This is nasty. Need ti find a way to determine alive + # This is nasty. Need to find a way to determine alive elif self.device: return STATE_IDLE else: From b023348795c79d60613c83f79374ce9cd163c126 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:04:37 +0100 Subject: [PATCH 13/44] Add link to docs --- homeassistant/components/history.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/history.py b/homeassistant/components/history.py index a723f9cbd71..a2d389a789b 100644 --- a/homeassistant/components/history.py +++ b/homeassistant/components/history.py @@ -1,8 +1,10 @@ """ homeassistant.components.history ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provide pre-made queries on top of the recorder component. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/history.html """ import re from datetime import timedelta From 1a018e3ee7f4f20ffa3f1b37dc2ec7a9d9c3c84e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:06:10 +0100 Subject: [PATCH 14/44] Remove configuration details --- homeassistant/components/ifttt.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/ifttt.py b/homeassistant/components/ifttt.py index 1eacd61bcee..4a17a5046c6 100644 --- a/homeassistant/components/ifttt.py +++ b/homeassistant/components/ifttt.py @@ -1,23 +1,10 @@ """ homeassistant.components.ifttt -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This component enable you to trigger Maker IFTTT recipes. -Check https://ifttt.com/maker for details. - -Configuration: - -To use Maker IFTTT you will need to add something like the following to your -config/configuration.yaml. - -ifttt: - key: xxxxx-x-xxxxxxxxxxxxx - -Variables: - -key -*Required -Your api key +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/ifttt.html """ import logging import requests From 55de563511987251e5fdda75605d180ef5486920 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:09:01 +0100 Subject: [PATCH 15/44] Add link to docs --- homeassistant/components/group.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/group.py b/homeassistant/components/group.py index 96fe2a67143..61e77247aa6 100644 --- a/homeassistant/components/group.py +++ b/homeassistant/components/group.py @@ -1,10 +1,11 @@ """ homeassistant.components.group ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to group devices that can be turned on or off. -""" +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/group.html +""" import homeassistant.core as ha from homeassistant.helpers import generate_entity_id from homeassistant.helpers.event import track_state_change From 0aaf280bf50cf00d15731e8cc5f324ed66ee0659 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:10:07 +0100 Subject: [PATCH 16/44] Add link to docs --- homeassistant/components/keyboard.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/keyboard.py b/homeassistant/components/keyboard.py index 3629fce31bf..6054547d685 100644 --- a/homeassistant/components/keyboard.py +++ b/homeassistant/components/keyboard.py @@ -1,8 +1,10 @@ """ homeassistant.components.keyboard ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to emulate keyboard presses on host machine. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/keyboard.html """ import logging From 5c79fc0ae3d14f2f30110a4383f3a83d301df437 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:10:51 +0100 Subject: [PATCH 17/44] Add link to docs --- homeassistant/components/logbook.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logbook.py b/homeassistant/components/logbook.py index 75a5cd83823..e81baf4a49c 100644 --- a/homeassistant/components/logbook.py +++ b/homeassistant/components/logbook.py @@ -1,8 +1,10 @@ """ homeassistant.components.logbook ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Parses events and generates a human log. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/logbook.html """ from datetime import timedelta from itertools import groupby From bf027fcd483c426cb8dcc376a0921c19a19293fb Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:12:09 +0100 Subject: [PATCH 18/44] Add link to docs --- homeassistant/components/recorder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/recorder.py b/homeassistant/components/recorder.py index 10f6576d23f..c6f7edd6ca8 100644 --- a/homeassistant/components/recorder.py +++ b/homeassistant/components/recorder.py @@ -1,9 +1,11 @@ """ homeassistant.components.recorder ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Component that records all events and state changes. Allows other components +to query this database. -Component that records all events and state changes. -Allows other components to query this database. +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/recorder.html """ import logging import threading From c3c248bc0a9225a9da7e28e6e36c4672198747e1 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:13:38 +0100 Subject: [PATCH 19/44] Add link to docs --- homeassistant/components/scene.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/scene.py b/homeassistant/components/scene.py index 66c15f8272f..48d7389b454 100644 --- a/homeassistant/components/scene.py +++ b/homeassistant/components/scene.py @@ -1,11 +1,10 @@ """ homeassistant.components.scene ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows users to set and activate scenes. -Allows users to set and activate scenes within Home Assistant. - -A scene is a set of states that describe how you want certain entities to be. -For example, light A should be red with 100 brightness. Light B should be on. +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/scene.html """ import logging from collections import namedtuple From 415d650860f0ba2c8918ba84bf9be175faaa91f4 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:14:56 +0100 Subject: [PATCH 20/44] Add link to docs --- homeassistant/components/script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index c5b1d4872de..3f892fdfa80 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -1,9 +1,11 @@ """ homeassistant.components.script ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -entity_id Scripts are a sequence of actions that can be triggered manually by the user or automatically based upon automation events, etc. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/script.html """ import logging from datetime import timedelta From 6a3316ed12651cedc058034c3062cb28cdbc6472 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:25:12 +0100 Subject: [PATCH 21/44] Add link to docs --- homeassistant/components/introduction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/introduction.py b/homeassistant/components/introduction.py index 3a1af572a30..4c367703903 100644 --- a/homeassistant/components/introduction.py +++ b/homeassistant/components/introduction.py @@ -1,8 +1,10 @@ """ homeassistant.components.introduction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Component that will help guide the user taking its first steps. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/introduction.html """ import logging From 77ba0c0393890108deebfe2a56f373f688bba256 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:30:56 +0100 Subject: [PATCH 22/44] Add link to docs --- homeassistant/components/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index e4c794df424..b11525170a4 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -1,8 +1,10 @@ """ homeassistant.components.api ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides a Rest API for Home Assistant. + +For more details about the RESTful API, please refer to the documentation at +https://home-assistant.io/developers/api.html """ import re import logging From 3c36d13e8d9bda5a81ea573335ffb145da87d1f3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:32:17 +0100 Subject: [PATCH 23/44] Add link to docs --- homeassistant/components/downloader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/downloader.py b/homeassistant/components/downloader.py index 6978dbd7fa9..6c1b53df059 100644 --- a/homeassistant/components/downloader.py +++ b/homeassistant/components/downloader.py @@ -1,8 +1,10 @@ """ homeassistant.components.downloader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to download files. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/downloader.html """ import os import logging From 78ad2686d4d5247b58d1c70e2d83ebf7490aa266 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:40:21 +0100 Subject: [PATCH 24/44] Add link to docs --- homeassistant/components/conversation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/conversation.py b/homeassistant/components/conversation.py index fd2ad60d211..f00a640232f 100644 --- a/homeassistant/components/conversation.py +++ b/homeassistant/components/conversation.py @@ -1,9 +1,10 @@ """ homeassistant.components.conversation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to have conversations with Home Assistant. -This is more a proof of concept. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/conversation.html """ import logging import re From f93282d636e2af6b6999c0abd20e439c8650719b Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:40:35 +0100 Subject: [PATCH 25/44] Add link to docs --- homeassistant/components/http.py | 73 ++------------------------------ 1 file changed, 4 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/http.py b/homeassistant/components/http.py index bae720db8dc..57e1875cd79 100644 --- a/homeassistant/components/http.py +++ b/homeassistant/components/http.py @@ -1,76 +1,11 @@ """ -homeassistant.components.httpinterface -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +homeassistant.components.http +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This module provides an API and a HTTP interface for debug purposes. -By default it will run on port 8123. - -All API calls have to be accompanied by an 'api_password' parameter and will -return JSON. If successful calls will return status code 200 or 201. - -Other status codes that can occur are: - - 400 (Bad Request) - - 401 (Unauthorized) - - 404 (Not Found) - - 405 (Method not allowed) - -The api supports the following actions: - -/api - GET -Returns message if API is up and running. -Example result: -{ - "message": "API running." -} - -/api/states - GET -Returns a list of entities for which a state is available -Example result: -[ - { .. state object .. }, - { .. state object .. } -] - -/api/states/ - GET -Returns the current state from an entity -Example result: -{ - "attributes": { - "next_rising": "07:04:15 29-10-2013", - "next_setting": "18:00:31 29-10-2013" - }, - "entity_id": "weather.sun", - "last_changed": "23:24:33 28-10-2013", - "state": "below_horizon" -} - -/api/states/ - POST -Updates the current state of an entity. Returns status code 201 if successful -with location header of updated resource and as body the new state. -parameter: new_state - string -optional parameter: attributes - JSON encoded object -Example result: -{ - "attributes": { - "next_rising": "07:04:15 29-10-2013", - "next_setting": "18:00:31 29-10-2013" - }, - "entity_id": "weather.sun", - "last_changed": "23:24:33 28-10-2013", - "state": "below_horizon" -} - -/api/events/ - POST -Fires an event with event_type -optional parameter: event_data - JSON encoded object -Example result: -{ - "message": "Event download_file fired." -} - +For more details about the RESTful API, please refer to the documentation at +https://home-assistant.io/developers/api.html """ - import json import threading import logging From 0d05930765d441088041d402b36c6314f03aa83e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 15:47:47 +0100 Subject: [PATCH 26/44] Update --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f646766a231..106b914eecb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,8 +20,8 @@ After you finish adding support for your device: - Update the supported devices in the `README.md` file. - Add any new dependencies to `requirements_all.txt`. There is no ordering right now, so just add it to the end. - Update the `.coveragerc` file. - - Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io). - - Make sure all your code passes Pylint and flake8 (PEP8 and some more) validation. To generate reports, run `pylint homeassistant > pylint.txt` and `flake8 homeassistant --exclude bower_components,external > flake8.txt`. + - Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io). It's OK to add a docstring with configuration details to the file header. + - Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `./script/lint`. - Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant. - Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/). From 5b25d9ccd6e1fa8d82ce3971390b2677ac12fea2 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Sun, 25 Oct 2015 17:00:54 +0000 Subject: [PATCH 27/44] flake8,pylint and other code cleanup --- homeassistant/components/discovery.py | 1 + homeassistant/components/media_player/plex.py | 57 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 1e04f20ea3e..b75abc32e4b 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -90,6 +90,7 @@ def setup(hass, config): ATTR_DISCOVERED: info }) + # pylint: disable=unused-argument def start_discovery(event): """ Start discovering. """ netdisco = DiscoveryService(SCAN_INTERVAL) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index dc9b7e82070..eeed715b337 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -6,7 +6,9 @@ Provides an interface to the Plex API. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/media_player.plex.html """ -import logging, json, os +import os +import json +import logging from datetime import timedelta from urllib.parse import urlparse @@ -16,7 +18,7 @@ from homeassistant.components.media_player import ( MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO) from homeassistant.const import ( - CONF_HOST, DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_PLAYING, + DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN) REQUIREMENTS = ['plexapi==1.1.0'] @@ -31,29 +33,30 @@ _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK + def config_from_file(filename, config=None): ''' Small configuration file management function''' if config: # We're writing configuration try: - with open(filename,'w') as f: - f.write(json.dumps(config)) - except IOError as e: - _LOGGER.error('Saving config file failed: %s'%e) + with open(filename, 'w') as fdesc: + fdesc.write(json.dumps(config)) + except IOError as error: + _LOGGER.error('Saving config file failed: %s', error) return False return True else: # We're reading config if os.path.isfile(filename): try: - with open(filename,'r') as f: - return json.loads(f.read()) - except IOError as e: - _LOGGER.error('Reading config file failed: %s'%e) + with open(filename, 'r') as fdesc: + return json.loads(fdesc.read()) + except IOError as error: + _LOGGER.error('Reading config file failed: %s', error) return False else: return {} - + # pylint: disable=abstract-method, unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): @@ -63,7 +66,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if discovery_info is not None: # Parse discovery data host = urlparse(discovery_info[1]).netloc - _LOGGER.info('Discovered PLEX server: %s'%host) + _LOGGER.info('Discovered PLEX server: %s', host) if host in _CONFIGURING: return @@ -73,7 +76,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # Setup a configured PlexServer config = config_from_file(hass.config.path(PLEX_CONFIG_FILE)) if len(config): - host,token = config.popitem() + host, token = config.popitem() token = token['token'] else: # Empty config file? @@ -82,25 +85,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): setup_plexserver(host, token, hass, add_devices_callback) +# pylint: disable=too-many-branches def setup_plexserver(host, token, hass, add_devices_callback): ''' Setup a plexserver based on host parameter''' - from plexapi.server import PlexServer - from plexapi.exceptions import BadRequest import plexapi - _LOGGER.info('Connecting to: htts://%s using token: %s' % - (host, token)) try: - plexserver = plexapi.server.PlexServer('http://%s'%host, token) + plexserver = plexapi.server.PlexServer('http://%s' % host, token) except (plexapi.exceptions.BadRequest, - plexapi.exceptions.Unauthorized, - plexapi.exceptions.NotFound) as e: - _LOGGER.info(e) + plexapi.exceptions.Unauthorized) as error: + _LOGGER.info(error) # No token or wrong token request_configuration(host, hass, add_devices_callback) return - except Exception as e: - _LOGGER.error('Misc Exception : %s'%e) + except plexapi.exceptions.NotFound: + # Host not found. Maybe it's off. Just log it and stop + _LOGGER.info(error) return # If we came here and configuring this host, mark as done @@ -116,8 +116,7 @@ def setup_plexserver(host, token, hass, add_devices_callback): {host: {'token': token}}): _LOGGER.error('failed to save config file') - _LOGGER.info('Connected to: htts://%s using token: %s' % - (host, token)) + _LOGGER.info('Connected to: htts://%s', host) plex_clients = {} plex_sessions = {} @@ -185,14 +184,14 @@ def request_configuration(host, hass, add_devices_callback): description=('Enter the X-Plex-Token'), description_image="/static/images/config_plex_mediaserver.png", submit_caption="Confirm", - fields=[{'id': 'token', 'name':'X-Plex-Token', 'type':''}] + fields=[{'id': 'token', 'name': 'X-Plex-Token', 'type': ''}] ) class PlexClient(MediaPlayerDevice): """ Represents a Plex device. """ - # pylint: disable=too-many-public-methods + # pylint: disable=too-many-public-methods, attribute-defined-outside-init def __init__(self, device, plex_sessions, update_devices, update_sessions): self.plex_sessions = plex_sessions self.update_devices = update_devices @@ -207,12 +206,12 @@ class PlexClient(MediaPlayerDevice): def unique_id(self): """ Returns the id of this plex client """ return "{}.{}".format( - self.__class__, self.device.machineIdentifier or self.device.name ) + self.__class__, self.device.machineIdentifier or self.device.name) @property def name(self): """ Returns the name of the device. """ - return self.device.name or self.device.product or self.device.deviceClass + return self.device.name or DEVICE_DEFAULT_NAME @property def session(self): From bc8c5766d4c320dd341ffc6b2169ae408ca06bb9 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Sun, 25 Oct 2015 17:54:48 +0000 Subject: [PATCH 28/44] Logic fixes --- homeassistant/components/media_player/plex.py | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index eeed715b337..b8267d286d3 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -53,6 +53,7 @@ def config_from_file(filename, config=None): return json.loads(fdesc.read()) except IOError as error: _LOGGER.error('Reading config file failed: %s', error) + # This won't work yet return False else: return {} @@ -62,8 +63,13 @@ def config_from_file(filename, config=None): def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Sets up the plex platform. """ + config = config_from_file(hass.config.path(PLEX_CONFIG_FILE)) + if len(config): + # Setup a configured PlexServer + host, token = config.popitem() + token = token['token'] # Via discovery - if discovery_info is not None: + elif discovery_info is not None: # Parse discovery data host = urlparse(discovery_info[1]).netloc _LOGGER.info('Discovered PLEX server: %s', host) @@ -71,16 +77,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if host in _CONFIGURING: return token = None - else: - # Setup a configured PlexServer - config = config_from_file(hass.config.path(PLEX_CONFIG_FILE)) - if len(config): - host, token = config.popitem() - token = token['token'] - else: - # Empty config file? - return + return setup_plexserver(host, token, hass, add_devices_callback) @@ -88,20 +86,18 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-branches def setup_plexserver(host, token, hass, add_devices_callback): ''' Setup a plexserver based on host parameter''' - import plexapi + import plexapi.server + import plexapi.exceptions try: plexserver = plexapi.server.PlexServer('http://%s' % host, token) except (plexapi.exceptions.BadRequest, - plexapi.exceptions.Unauthorized) as error: + plexapi.exceptions.Unauthorized, + plexapi.exceptions.NotFound) as error: _LOGGER.info(error) # No token or wrong token request_configuration(host, hass, add_devices_callback) return - except plexapi.exceptions.NotFound: - # Host not found. Maybe it's off. Just log it and stop - _LOGGER.info(error) - return # If we came here and configuring this host, mark as done if host in _CONFIGURING: From fb8edca94206dd000981d85b7833222cffc0c892 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 25 Oct 2015 22:21:25 +0100 Subject: [PATCH 29/44] Add link to docs, fix typo, and update log output --- homeassistant/components/switch/rest.py | 42 +++++-------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/switch/rest.py b/homeassistant/components/switch/rest.py index 0504f96bc4b..b81bfc246b4 100644 --- a/homeassistant/components/switch/rest.py +++ b/homeassistant/components/switch/rest.py @@ -1,37 +1,11 @@ -# -*- coding: utf-8 -*- """ homeassistant.components.switch.rest -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a REST switch. -Configuration: - -switch: - platform: rest - name: "Bedroom Switch" - resource: "http://IP_ADDRESS/ENDPOINT" - body_on: "ON" - body_off: "OFF" - -Variables: - -resource -*Required* - -name -*Optional -The name of the switch. Default is 'REST Switch'. - -body_on -*Optional -The body that represents enabled state. Default is "ON". - -body_off -*Optional -The body that represents disabled state. Default is "OFF". - +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/switch.rest.html """ - import logging import requests @@ -46,7 +20,7 @@ DEFAULT_BODY_OFF = "OFF" # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): - """ Add REST Switch """ + """ Get REST switch. """ resource = config.get('resource') @@ -61,7 +35,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): "Add http:// to your URL.") return False except requests.exceptions.ConnectionError: - _LOGGER.error("No route to device. " + _LOGGER.error("No route to resource/endpoint. " "Please check the IP address in the configuration file.") return False @@ -75,7 +49,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # pylint: disable=too-many-arguments class RestSwitch(SwitchDevice): - """ Represents a switch that can be togggled using REST """ + """ Represents a switch that can be toggled using REST. """ def __init__(self, hass, name, resource, body_on, body_off): self._state = None self._hass = hass @@ -102,7 +76,7 @@ class RestSwitch(SwitchDevice): if request.status_code == 200: self._state = True else: - _LOGGER.error("Can't turn on %s. Is device offline?", + _LOGGER.error("Can't turn on %s. Is resource/endpoint offline?", self._resource) def turn_off(self, **kwargs): @@ -113,7 +87,7 @@ class RestSwitch(SwitchDevice): if request.status_code == 200: self._state = False else: - _LOGGER.error("Can't turn off %s. Is device offline?", + _LOGGER.error("Can't turn off %s. Is resource/endpoint offline?", self._resource) def update(self): From a2256f6c97c4c1118a23e87610ac194586ac57f7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 15:39:50 -0700 Subject: [PATCH 30/44] Update version frontend --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 4456 +++++++++-------- .../www_static/home-assistant-polymer | 2 +- .../www_static/webcomponents-lite.min.js | 6 +- 4 files changed, 2317 insertions(+), 2149 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 98deab3f447..670fecb6620 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "90c41bfbaa56f9a1c88db27a54f7d36b" +VERSION = "461d1b36c376037f19d66e316e063543" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 18bb5c557f9..d4df6f5e0a5 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,584 @@ -

- - - - - \ No newline at end of file + } \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 055403f9d38..ac34b5fc269 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 055403f9d3892118243c8a64a341bce5b74ce8aa +Subproject commit ac34b5fc26928238343f34f334a6f6f1a19d5442 From f1aa685cf2b1c33ea79aa5289c7f7b30c9f92494 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 21:00:22 -0700 Subject: [PATCH 34/44] Add version to config API --- homeassistant/core.py | 3 ++- tests/test_core.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index b834efce406..fc1ff2e8cbe 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -17,7 +17,7 @@ import functools as ft from collections import namedtuple from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, + __version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED, @@ -741,6 +741,7 @@ class Config(object): 'location_name': self.location_name, 'time_zone': time_zone.zone, 'components': self.components, + 'version': __version__ } diff --git a/tests/test_core.py b/tests/test_core.py index 01ede9e138e..bb59aac03fa 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -21,7 +21,7 @@ from homeassistant.exceptions import ( import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_state_change from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, + __version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, ATTR_FRIENDLY_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -555,6 +555,7 @@ class TestConfig(unittest.TestCase): 'location_name': None, 'time_zone': 'UTC', 'components': [], + 'version': __version__, } self.assertEqual(expected, self.config.as_dict()) From 06c8c1b168d7c7a494de6ff3054e5d0fd65da87f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 23:11:50 -0700 Subject: [PATCH 35/44] Update to latest version frontend --- homeassistant/components/frontend/version.py | 2 +- .../components/frontend/www_static/frontend.html | 16 ++++++++-------- .../frontend/www_static/home-assistant-polymer | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 758c92dc3e9..1c753d1638e 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "231652c54795bcc542269ff03ec2e9b2" +VERSION = "beb922c55bb26ea576581b453f6d7c04" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index f17de5a2781..7343bd3afd0 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -5993,12 +5993,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN font-weight: 300; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - } \ No newline at end of file +console.group&&(console.groupCollapsed("Dispatch: %s",t),console.group("payload"),console.debug(e),console.groupEnd())},e.dispatchError=function(t){console.group&&(console.debug("Dispatch error: "+t),console.groupEnd())},e.storeHandled=function(t,e,n){console.group&&e!==n&&console.debug("Store "+t+" handled action")},e.dispatchEnd=function(t){console.group&&(console.debug("Dispatch done, new state: ",t.toJS()),console.groupEnd())}},function(t,e,n){function r(t,e){this.__prevState=t,this.__evaluator=e,this.__prevValues=i.Map(),this.__observers=[]}var i=n(2),o=n(7),u=n(8);Object.defineProperty(r.prototype,"notifyObservers",{writable:!0,configurable:!0,value:function(t){if(this.__observers.length>0){var e=i.Map();this.__observers.forEach(function(n){var r,i=n.getter,a=o(i),s=this.__prevState;this.__prevValues.has(a)?r=this.__prevValues.get(a):(r=this.__evaluator.evaluate(s,i),this.__prevValues=this.__prevValues.set(a,r));var c=this.__evaluator.evaluate(t,i);u(r,c)||(n.handler.call(null,c),e=e.set(a,c))}.bind(this)),this.__prevValues=e}this.__prevState=t}}),Object.defineProperty(r.prototype,"onChange",{writable:!0,configurable:!0,value:function(t,e){var n={getter:t,handler:e};return this.__observers.push(n),function(){var t=this.__observers.indexOf(n);t>-1&&this.__observers.splice(t,1)}.bind(this)}}),Object.defineProperty(r.prototype,"reset",{writable:!0,configurable:!0,value:function(t){this.__prevState=t,this.__prevValues=i.Map(),this.__observers=[]}}),t.exports=r},function(t,e,n){var r=n(2);t.exports=function(t,e){if(t.hasOwnProperty("__hashCode"))return t.__hashCode;var n=r.fromJS(t).hashCode();return e||(Object.defineProperty(t,"__hashCode",{enumerable:!1,configurable:!1,writable:!1,value:n}),Object.freeze(t)),n}},function(t,e,n){var r=n(2);t.exports=function(t,e){return r.is(t,e)}},function(t,e,n){function r(t){return s(t)&&a(t[t.length-1])}function i(t){return t[t.length-1]}function o(t){return t.slice(0,t.length-1)}function u(t){if(!c(t))throw new Error("Cannot create Getter from KeyPath: "+t);return[t,l]}var a=n(3).isFunction,s=n(3).isArray,c=n(10).isKeyPath,l=function(t){return t};t.exports={isGetter:r,getComputeFn:i,getDeps:o,fromKeyPath:u}},function(t,e,n){var r=n(3).isArray,i=n(3).isFunction;e.isKeyPath=function(t){return r(t)&&!i(t[t.length-1])}},function(t,e,n){function r(){this.__cachedGetters=i.Map({})}var i=n(2),o=n(1).toImmutable,u=n(7),a=n(8),s=n(9).getComputeFn,c=n(9).getDeps,l=n(10).isKeyPath,f=n(9).isGetter,d=!1;Object.defineProperty(r.prototype,"evaluate",{writable:!0,configurable:!0,value:function(t,e){if(l(e))return t.getIn(e);if(!f(e))throw new Error("evaluate must be passed a keyPath or Getter");var n=u(e);if(this.__isCached(t,e))return this.__cachedGetters.getIn([n,"value"]);var r=c(e).map(function(e){return this.evaluate(t,e)}.bind(this));if(this.__hasStaleValue(t,e)){var i=this.__cachedGetters.getIn([n,"args"]);if(a(i,o(r))){var p=this.__cachedGetters.getIn([n,"value"]);return this.__cacheValue(t,e,i,p),p}}if(d===!0)throw d=!1,new Error("Evaluate may not be called within a Getters computeFn");var h;d=!0;try{h=s(e).apply(null,r),d=!1}catch(_){throw d=!1,_}return this.__cacheValue(t,e,r,h),h}}),Object.defineProperty(r.prototype,"__hasStaleValue",{writable:!0,configurable:!0,value:function(t,e){var n=u(e),r=this.__cachedGetters;return r.has(n)&&r.getIn([n,"stateHashCode"])!==t.hashCode()}}),Object.defineProperty(r.prototype,"__cacheValue",{writable:!0,configurable:!0,value:function(t,e,n,r){var a=u(e);this.__cachedGetters=this.__cachedGetters.set(a,i.Map({value:r,args:o(n),stateHashCode:t.hashCode()}))}}),Object.defineProperty(r.prototype,"__isCached",{writable:!0,configurable:!0,value:function(t,e){var n=u(e);return this.__cachedGetters.hasIn([n,"value"])&&this.__cachedGetters.getIn([n,"stateHashCode"])===t.hashCode()}}),Object.defineProperty(r.prototype,"untrack",{writable:!0,configurable:!0,value:function(t){}}),Object.defineProperty(r.prototype,"reset",{writable:!0,configurable:!0,value:function(){this.__cachedGetters=i.Map({})}}),t.exports=r},function(t,e,n){function r(t,e){var n={};return i(e,function(e,r){n[r]=t.evaluate(e)}),n}var i=n(3).each;t.exports=function(t){return{getInitialState:function(){return r(t,this.getDataBindings())},componentDidMount:function(){var e=this;e.__unwatchFns=[],i(this.getDataBindings(),function(n,r){var i=t.observe(n,function(t){var n={};n[r]=t,e.setState(n)});e.__unwatchFns.push(i)})},componentWillUnmount:function(){for(;this.__unwatchFns.length;)this.__unwatchFns.shift()()}}}},function(t,e,n){function r(t){return this instanceof r?(this.__handlers=o({}),t&&u(this,t),void this.initialize()):new r(t)}function i(t){return t instanceof r}var o=n(2).Map,u=n(3).extend,a=n(1).toJS,s=n(1).toImmutable;Object.defineProperty(r.prototype,"initialize",{writable:!0,configurable:!0,value:function(){}}),Object.defineProperty(r.prototype,"getInitialState",{writable:!0,configurable:!0,value:function(){return o()}}),Object.defineProperty(r.prototype,"handle",{writable:!0,configurable:!0,value:function(t,e,n){var r=this.__handlers.get(e);return"function"==typeof r?r.call(this,t,n,e):t}}),Object.defineProperty(r.prototype,"handleReset",{writable:!0,configurable:!0,value:function(t){return this.getInitialState()}}),Object.defineProperty(r.prototype,"on",{writable:!0,configurable:!0,value:function(t,e){this.__handlers=this.__handlers.set(t,e)}}),Object.defineProperty(r.prototype,"serialize",{writable:!0,configurable:!0,value:function(t){return a(t)}}),Object.defineProperty(r.prototype,"deserialize",{writable:!0,configurable:!0,value:function(t){return s(t)}}),t.exports=r,t.exports.isStore=i}])})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(121),u=r(o);e["default"]=u["default"](i.reactor),t.exports=e["default"]},function(t,e){"use strict";var n=function(t){var e,n={};if(!(t instanceof Object)||Array.isArray(t))throw new Error("keyMirror(...): Argument must be an object.");for(e in t)t.hasOwnProperty(e)&&(n[e]=e);return n};t.exports=n},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(130),o=r(i);e.callApi=o["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(146),u=i(o),a=n(147),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){t.registerStores({restApiCache:l["default"]})}function o(t){return[["restApiCache",t.entity],function(t){return!!t}]}function u(t){return[["restApiCache",t.entity],function(t){return t||s.toImmutable({})}]}function a(t){return function(e){return["restApiCache",t.entity,e]}}Object.defineProperty(e,"__esModule",{value:!0}),e.register=i,e.createHasDataGetter=o,e.createEntityMapGetter=u,e.createByIdGetter=a;var s=n(3),c=n(170),l=r(c),f=n(169),d=r(f);e.createApiActions=d["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(80),n(37),e["default"]=new o["default"]({is:"state-info",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"partial-base",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1}},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({ENTITY_HISTORY_DATE_SELECTED:null,ENTITY_HISTORY_FETCH_START:null,ENTITY_HISTORY_FETCH_ERROR:null,ENTITY_HISTORY_FETCH_SUCCESS:null,RECENT_ENTITY_HISTORY_FETCH_START:null,RECENT_ENTITY_HISTORY_FETCH_ERROR:null,RECENT_ENTITY_HISTORY_FETCH_SUCCESS:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({LOGBOOK_DATE_SELECTED:null,LOGBOOK_ENTRIES_FETCH_START:null,LOGBOOK_ENTRIES_FETCH_ERROR:null,LOGBOOK_ENTRIES_FETCH_SUCCESS:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(171),u=i(o),a=n(57),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({VALIDATING_AUTH_TOKEN:null,VALID_AUTH_TOKEN:null,INVALID_AUTH_TOKEN:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({authAttempt:a["default"],authCurrent:c["default"],rememberAuth:f["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(133),a=i(u),s=n(134),c=i(s),l=n(135),f=i(l),d=n(131),p=r(d),h=n(132),_=r(h),v=p;e.actions=v;var y=_;e.getters=y},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var u=function(){function t(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var u,a=t[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(s){i=!0,o=s}finally{try{!r&&a["return"]&&a["return"]()}finally{if(i)throw o}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),a=function(){function t(t,e){for(var n=0;n5?"value big":"value"}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"loading-box"}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(122),a=r(u);n(39),n(120),n(119),n(116),n(118),n(117),e["default"]=new o["default"]({is:"state-card-content",properties:{stateObj:{type:Object,observer:"stateObjChanged"}},stateObjChanged:function(t,e){var n=o["default"].dom(this);if(!t)return void(n.lastChild&&n.removeChild(n.lastChild));var r=a["default"](t);if(e&&a["default"](e)===r)n.lastChild.stateObj=t;else{n.lastChild&&n.removeChild(n.lastChild);var i=document.createElement("state-card-"+r);i.stateObj=t,n.appendChild(i)}}}),t.exports=e["default"]},function(t,e){"use strict";function n(t,e){return t?e.map(function(e){return e in t.attributes?"has-"+e:""}).join(" "):""}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return i.reactor.evaluate(i.serviceGetters.canToggleEntity(t))}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r;var i=n(2);t.exports=e["default"]},function(t,e,n){"use strict";function r(t,e,n){function r(){y&&clearTimeout(y),p&&clearTimeout(p),g=0,p=y=m=void 0}function s(e,n){n&&clearTimeout(n),p=y=m=void 0,e&&(g=o(),h=t.apply(v,d),y||p||(d=v=void 0))}function c(){var t=e-(o()-_);0>=t||t>e?s(m,p):y=setTimeout(c,t)}function l(){s(O,y)}function f(){if(d=arguments,_=o(),v=this,m=O&&(y||!w),b===!1)var n=w&&!y;else{p||w||(g=_);var r=b-(_-g),i=0>=r||r>b;i?(p&&(p=clearTimeout(p)),g=_,h=t.apply(v,d)):p||(p=setTimeout(l,r))}return i&&y?y=clearTimeout(y):y||e===b||(y=setTimeout(c,e)),n&&(i=!0,h=t.apply(v,d)),!i||y||p||(d=v=void 0),h}var d,p,h,_,v,y,m,g=0,b=!1,O=!0;if("function"!=typeof t)throw new TypeError(u);if(e=0>e?0:+e||0,n===!0){var w=!0;O=!1}else i(n)&&(w=!!n.leading,b="maxWait"in n&&a(+n.maxWait||0,e),O="trailing"in n?!!n.trailing:O);return f.cancel=r,f}var i=n(46),o=n(126),u="Expected a function",a=Math.max;t.exports=r},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({SERVER_CONFIG_LOADED:null,COMPONENT_LOADED:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({serverComponent:a["default"],serverConfig:c["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(138),a=i(u),s=n(139),c=i(s),l=n(136),f=r(l),d=n(137),p=r(d),h=f;e.actions=h;var _=p;e.getters=_},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(148),u=i(o),a=n(149),s=r(a),c=u["default"];e.actions=c;var l=s;e.getters=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({NAVIGATE:null,SHOW_SIDEBAR:null,LOG_OUT:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({notifications:a["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(166),a=i(u),s=n(164),c=r(s),l=n(165),f=r(l),d=c;e.actions=d;var p=f;e.getters=p},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({streamStatus:a["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(178),a=i(u),s=n(174),c=r(s),l=n(175),f=r(l),d=c;e.actions=d;var p=f;e.getters=p},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),o=r(i);e["default"]=o["default"]({API_FETCH_ALL_START:null,API_FETCH_ALL_SUCCESS:null,API_FETCH_ALL_FAIL:null,SYNC_SCHEDULED:null,SYNC_SCHEDULE_CANCELLED:null}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({isFetchingData:a["default"],isSyncScheduled:c["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(180),a=i(u),s=n(181),c=i(s),l=n(179),f=r(l),d=n(60),p=r(d),h=f;e.actions=h;var _=p;e.getters=_},function(t,e){"use strict";function n(t){return t.getFullYear()+"-"+(t.getMonth()+1)+"-"+t.getDate()}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e){"use strict";function n(t){var e=t.split(" "),n=r(e,2),i=n[0],o=n[1],u=i.split(":"),a=r(u,3),s=a[0],c=a[1],l=a[2],f=o.split("-"),d=r(f,3),p=d[0],h=d[1],_=d[2];return new Date(Date.UTC(_,parseInt(h,10)-1,p,s,c,l))}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var u,a=t[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(s){i=!0,o=s}finally{try{!r&&a["return"]&&a["return"]()}finally{if(i)throw o}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();e["default"]=n,t.exports=e["default"]},function(t,e,n){(function(t){"use strict";!function(e,n){t.exports=n()}(void 0,function(){function e(){return Ln.apply(null,arguments)}function n(t){Ln=t}function r(t){return"[object Array]"===Object.prototype.toString.call(t)}function i(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function o(t,e){var n,r=[];for(n=0;n0)for(n in zn)r=zn[n],i=e[r],"undefined"!=typeof i&&(t[r]=i);return t}function h(t){p(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),Rn===!1&&(Rn=!0,e.updateOffset(this),Rn=!1)}function _(t){return t instanceof h||null!=t&&null!=t._isAMomentObject}function v(t){return 0>t?Math.ceil(t):Math.floor(t)}function y(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=v(e)),n}function m(t,e,n){var r,i=Math.min(t.length,e.length),o=Math.abs(t.length-e.length),u=0;for(r=0;i>r;r++)(n&&t[r]!==e[r]||!n&&y(t[r])!==y(e[r]))&&u++;return u+o}function g(){}function b(t){return t?t.toLowerCase().replace("_","-"):t}function O(t){for(var e,n,r,i,o=0;o0;){if(r=w(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&m(i,n,!0)>=e-1)break;e--}o++}return null}function w(e){var n=null;if(!Hn[e]&&"undefined"!=typeof t&&t&&t.exports)try{n=Nn._abbr,!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),S(n)}catch(r){}return Hn[e]}function S(t,e){var n;return t&&(n="undefined"==typeof e?T(t):M(t,e),n&&(Nn=n)),Nn._abbr}function M(t,e){return null!==e?(e.abbr=t,Hn[t]=Hn[t]||new g,Hn[t].set(e),S(t),Hn[t]):(delete Hn[t],null)}function T(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Nn;if(!r(t)){if(e=w(t))return e;t=[t]}return O(t)}function E(t,e){var n=t.toLowerCase();Yn[n]=Yn[n+"s"]=Yn[e]=t}function j(t){return"string"==typeof t?Yn[t]||Yn[t.toLowerCase()]:void 0}function I(t){var e,n,r={};for(n in t)u(t,n)&&(e=j(n),e&&(r[e]=t[n]));return r}function P(t,n){return function(r){return null!=r?(C(this,t,r),e.updateOffset(this,n),this):D(this,t)}}function D(t,e){return t._d["get"+(t._isUTC?"UTC":"")+e]()}function C(t,e,n){return t._d["set"+(t._isUTC?"UTC":"")+e](n)}function A(t,e){var n;if("object"==typeof t)for(n in t)this.set(n,t[n]);else if(t=j(t),"function"==typeof this[t])return this[t](e);return this}function k(t,e,n){var r=""+Math.abs(t),i=e-r.length,o=t>=0;return(o?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}function x(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&(Bn[t]=i),e&&(Bn[e[0]]=function(){return k(i.apply(this,arguments),e[1],e[2])}),n&&(Bn[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function L(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function N(t){var e,n,r=t.match(Gn);for(e=0,n=r.length;n>e;e++)Bn[r[e]]?r[e]=Bn[r[e]]:r[e]=L(r[e]);return function(i){var o="";for(e=0;n>e;e++)o+=r[e]instanceof Function?r[e].call(i,t):r[e];return o}}function z(t,e){return t.isValid()?(e=R(e,t.localeData()),Un[e]=Un[e]||N(e),Un[e](t)):t.localeData().invalidDate()}function R(t,e){function n(t){return e.longDateFormat(t)||t}var r=5;for(Fn.lastIndex=0;r>=0&&Fn.test(t);)t=t.replace(Fn,n),Fn.lastIndex=0,r-=1;return t}function H(t){return"function"==typeof t&&"[object Function]"===Object.prototype.toString.call(t)}function Y(t,e,n){or[t]=H(e)?e:function(t){return t&&n?n:e}}function G(t,e){return u(or,t)?or[t](e._strict,e._locale):new RegExp(F(t))}function F(t){return t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,r,i){return e||n||r||i}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function U(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),"number"==typeof e&&(r=function(t,n){n[e]=y(t)}),n=0;nr;r++){if(i=s([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(o="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(o.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}}function $(t,e){var n;return"string"==typeof e&&(e=t.localeData().monthsParse(e),"number"!=typeof e)?t:(n=Math.min(t.date(),q(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t)}function Z(t){return null!=t?($(this,t),e.updateOffset(this,!0),this):D(this,"Month")}function X(){return q(this.year(),this.month())}function Q(t){var e,n=t._a;return n&&-2===l(t).overflow&&(e=n[sr]<0||n[sr]>11?sr:n[cr]<1||n[cr]>q(n[ar],n[sr])?cr:n[lr]<0||n[lr]>24||24===n[lr]&&(0!==n[fr]||0!==n[dr]||0!==n[pr])?lr:n[fr]<0||n[fr]>59?fr:n[dr]<0||n[dr]>59?dr:n[pr]<0||n[pr]>999?pr:-1,l(t)._overflowDayOfYear&&(ar>e||e>cr)&&(e=cr),l(t).overflow=e),t}function tt(t){e.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function et(t,e){var n=!0;return a(function(){return n&&(tt(t+"\n"+(new Error).stack),n=!1),e.apply(this,arguments)},e)}function nt(t,e){vr[t]||(tt(e),vr[t]=!0)}function rt(t){var e,n,r=t._i,i=yr.exec(r);if(i){for(l(t).iso=!0,e=0,n=mr.length;n>e;e++)if(mr[e][1].exec(r)){t._f=mr[e][0];break}for(e=0,n=gr.length;n>e;e++)if(gr[e][1].exec(r)){t._f+=(i[6]||" ")+gr[e][0];break}r.match(nr)&&(t._f+="Z"),wt(t)}else t._isValid=!1}function it(t){var n=br.exec(t._i);return null!==n?void(t._d=new Date(+n[1])):(rt(t),void(t._isValid===!1&&(delete t._isValid,e.createFromInputFallback(t))))}function ot(t,e,n,r,i,o,u){var a=new Date(t,e,n,r,i,o,u);return 1970>t&&a.setFullYear(t),a}function ut(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function at(t){return st(t)?366:365}function st(t){return t%4===0&&t%100!==0||t%400===0}function ct(){return st(this.year())}function lt(t,e,n){var r,i=n-e,o=n-t.day();return o>i&&(o-=7),i-7>o&&(o+=7),r=Dt(t).add(o,"d"),{week:Math.ceil(r.dayOfYear()/7),year:r.year()}}function ft(t){return lt(t,this._week.dow,this._week.doy).week}function dt(){return this._week.dow}function pt(){return this._week.doy}function ht(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")}function _t(t){var e=lt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")}function vt(t,e,n,r,i){var o,u=6+i-r,a=ut(t,0,1+u),s=a.getUTCDay();return i>s&&(s+=7),n=null!=n?1*n:i,o=1+u+7*(e-1)-s+n,{year:o>0?t:t-1,dayOfYear:o>0?o:at(t-1)+o}}function yt(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")}function mt(t,e,n){return null!=t?t:null!=e?e:n}function gt(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function bt(t){var e,n,r,i,o=[];if(!t._d){for(r=gt(t),t._w&&null==t._a[cr]&&null==t._a[sr]&&Ot(t),t._dayOfYear&&(i=mt(t._a[ar],r[ar]),t._dayOfYear>at(i)&&(l(t)._overflowDayOfYear=!0),n=ut(i,0,t._dayOfYear),t._a[sr]=n.getUTCMonth(),t._a[cr]=n.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=o[e]=r[e];for(;7>e;e++)t._a[e]=o[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[lr]&&0===t._a[fr]&&0===t._a[dr]&&0===t._a[pr]&&(t._nextDay=!0,t._a[lr]=0),t._d=(t._useUTC?ut:ot).apply(null,o),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[lr]=24)}}function Ot(t){var e,n,r,i,o,u,a;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(o=1,u=4,n=mt(e.GG,t._a[ar],lt(Dt(),1,4).year),r=mt(e.W,1),i=mt(e.E,1)):(o=t._locale._week.dow,u=t._locale._week.doy,n=mt(e.gg,t._a[ar],lt(Dt(),o,u).year),r=mt(e.w,1),null!=e.d?(i=e.d,o>i&&++r):i=null!=e.e?e.e+o:o),a=vt(n,r,i,u,o),t._a[ar]=a.year,t._dayOfYear=a.dayOfYear}function wt(t){if(t._f===e.ISO_8601)return void rt(t);t._a=[],l(t).empty=!0;var n,r,i,o,u,a=""+t._i,s=a.length,c=0;for(i=R(t._f,t._locale).match(Gn)||[],n=0;n0&&l(t).unusedInput.push(u),a=a.slice(a.indexOf(r)+r.length),c+=r.length),Bn[o]?(r?l(t).empty=!1:l(t).unusedTokens.push(o),V(o,r,t)):t._strict&&!r&&l(t).unusedTokens.push(o);l(t).charsLeftOver=s-c,a.length>0&&l(t).unusedInput.push(a),l(t).bigHour===!0&&t._a[lr]<=12&&t._a[lr]>0&&(l(t).bigHour=void 0),t._a[lr]=St(t._locale,t._a[lr],t._meridiem),bt(t),Q(t)}function St(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?(r=t.isPM(n),r&&12>e&&(e+=12),r||12!==e||(e=0),e):e}function Mt(t){var e,n,r,i,o;if(0===t._f.length)return l(t).invalidFormat=!0,void(t._d=new Date(NaN));for(i=0;io)&&(r=o,n=e));a(t,n||e)}function Tt(t){if(!t._d){var e=I(t._i);t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],bt(t)}}function Et(t){var e=new h(Q(jt(t)));return e._nextDay&&(e.add(1,"d"),e._nextDay=void 0),e}function jt(t){var e=t._i,n=t._f;return t._locale=t._locale||T(t._l),null===e||void 0===n&&""===e?d({nullInput:!0}):("string"==typeof e&&(t._i=e=t._locale.preparse(e)),_(e)?new h(Q(e)):(r(n)?Mt(t):n?wt(t):i(e)?t._d=e:It(t),t))}function It(t){var n=t._i;void 0===n?t._d=new Date:i(n)?t._d=new Date(+n):"string"==typeof n?it(t):r(n)?(t._a=o(n.slice(0),function(t){return parseInt(t,10)}),bt(t)):"object"==typeof n?Tt(t):"number"==typeof n?t._d=new Date(n):e.createFromInputFallback(t)}function Pt(t,e,n,r,i){var o={};return"boolean"==typeof n&&(r=n,n=void 0),o._isAMomentObject=!0,o._useUTC=o._isUTC=i,o._l=n,o._i=t,o._f=e,o._strict=r,Et(o)}function Dt(t,e,n,r){return Pt(t,e,n,r,!1)}function Ct(t,e){var n,i;if(1===e.length&&r(e[0])&&(e=e[0]),!e.length)return Dt();for(n=e[0],i=1;it&&(t=-t,n="-"),n+k(~~(t/60),2)+e+k(~~t%60,2)})}function zt(t){var e=(t||"").match(nr)||[],n=e[e.length-1]||[],r=(n+"").match(Tr)||["-",0,0],i=+(60*r[1])+y(r[2]);return"+"===r[0]?i:-i}function Rt(t,n){var r,o;return n._isUTC?(r=n.clone(),o=(_(t)||i(t)?+t:+Dt(t))-+r,r._d.setTime(+r._d+o),e.updateOffset(r,!1),r):Dt(t).local()}function Ht(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Yt(t,n){var r,i=this._offset||0;return null!=t?("string"==typeof t&&(t=zt(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&n&&(r=Ht(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),i!==t&&(!n||this._changeInProgress?ne(this,Zt(t-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,e.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?i:Ht(this)}function Gt(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}function Ft(t){return this.utcOffset(0,t)}function Ut(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ht(this),"m")),this}function Bt(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(zt(this._i)),this}function Vt(t){return t=t?Dt(t).utcOffset():0,(this.utcOffset()-t)%60===0}function qt(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Wt(){if("undefined"!=typeof this._isDSTShifted)return this._isDSTShifted;var t={};if(p(t,this),t=jt(t),t._a){var e=t._isUTC?s(t._a):Dt(t._a);this._isDSTShifted=this.isValid()&&m(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Kt(){return!this._isUTC}function Jt(){return this._isUTC}function $t(){return this._isUTC&&0===this._offset}function Zt(t,e){var n,r,i,o=t,a=null;return Lt(t)?o={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(o={},e?o[e]=t:o.milliseconds=t):(a=Er.exec(t))?(n="-"===a[1]?-1:1,o={y:0,d:y(a[cr])*n,h:y(a[lr])*n,m:y(a[fr])*n,s:y(a[dr])*n,ms:y(a[pr])*n}):(a=jr.exec(t))?(n="-"===a[1]?-1:1,o={y:Xt(a[2],n),M:Xt(a[3],n),d:Xt(a[4],n),h:Xt(a[5],n),m:Xt(a[6],n),s:Xt(a[7],n),w:Xt(a[8],n)}):null==o?o={}:"object"==typeof o&&("from"in o||"to"in o)&&(i=te(Dt(o.from),Dt(o.to)),o={},o.ms=i.milliseconds,o.M=i.months),r=new xt(o),Lt(t)&&u(t,"_locale")&&(r._locale=t._locale),r}function Xt(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Qt(t,e){var n={milliseconds:0,months:0};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function te(t,e){var n;return e=Rt(e,t),t.isBefore(e)?n=Qt(t,e):(n=Qt(e,t),n.milliseconds=-n.milliseconds,n.months=-n.months),n}function ee(t,e){return function(n,r){var i,o;return null===r||isNaN(+r)||(nt(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period)."),o=n,n=r,r=o),n="string"==typeof n?+n:n,i=Zt(n,r),ne(this,i,t),this}}function ne(t,n,r,i){var o=n._milliseconds,u=n._days,a=n._months;i=null==i?!0:i,o&&t._d.setTime(+t._d+o*r),u&&C(t,"Date",D(t,"Date")+u*r),a&&$(t,D(t,"Month")+a*r),i&&e.updateOffset(t,u||a)}function re(t,e){var n=t||Dt(),r=Rt(n,this).startOf("day"),i=this.diff(r,"days",!0),o=-6>i?"sameElse":-1>i?"lastWeek":0>i?"lastDay":1>i?"sameDay":2>i?"nextDay":7>i?"nextWeek":"sameElse";return this.format(e&&e[o]||this.localeData().calendar(o,this,Dt(n)))}function ie(){return new h(this)}function oe(t,e){var n;return e=j("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=_(t)?t:Dt(t),+this>+t):(n=_(t)?+t:+Dt(t),n<+this.clone().startOf(e))}function ue(t,e){var n;return e=j("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=_(t)?t:Dt(t),+t>+this):(n=_(t)?+t:+Dt(t),+this.clone().endOf(e)e-o?(n=t.clone().add(i-1,"months"),r=(e-o)/(o-n)):(n=t.clone().add(i+1,"months"),r=(e-o)/(n-o)),-(i+r)}function fe(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function de(){var t=this.clone().utc();return 0e;e++)if(this._weekdaysParse[e]||(n=Dt([2e3,1]).day(e),r="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(r.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e}function Fe(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ze(t,this.localeData()),this.add(t-e,"d")):e}function Ue(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")}function Be(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)}function Ve(t,e){x(t,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)})}function qe(t,e){return e._meridiemParse}function We(t){return"p"===(t+"").toLowerCase().charAt(0)}function Ke(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"}function Je(t,e){e[pr]=y(1e3*("0."+t))}function $e(){return this._isUTC?"UTC":""}function Ze(){return this._isUTC?"Coordinated Universal Time":""}function Xe(t){return Dt(1e3*t)}function Qe(){return Dt.apply(null,arguments).parseZone()}function tn(t,e,n){var r=this._calendar[t];return"function"==typeof r?r.call(e,n):r}function en(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t])}function nn(){return this._invalidDate}function rn(t){return this._ordinal.replace("%d",t)}function on(t){return t}function un(t,e,n,r){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,r):i.replace(/%d/i,t)}function an(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)}function sn(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function cn(t,e,n,r){var i=T(),o=s().set(r,e);return i[n](o,t)}function ln(t,e,n,r,i){if("number"==typeof t&&(e=t,t=void 0),t=t||"",null!=e)return cn(t,e,n,i);var o,u=[];for(o=0;r>o;o++)u[o]=cn(t,o,n,i);return u}function fn(t,e){return ln(t,e,"months",12,"month")}function dn(t,e){return ln(t,e,"monthsShort",12,"month")}function pn(t,e){return ln(t,e,"weekdays",7,"day")}function hn(t,e){return ln(t,e,"weekdaysShort",7,"day")}function _n(t,e){return ln(t,e,"weekdaysMin",7,"day")}function vn(){var t=this._data;return this._milliseconds=$r(this._milliseconds),this._days=$r(this._days),this._months=$r(this._months),t.milliseconds=$r(t.milliseconds),t.seconds=$r(t.seconds),t.minutes=$r(t.minutes),t.hours=$r(t.hours),t.months=$r(t.months),t.years=$r(t.years),this}function yn(t,e,n,r){var i=Zt(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function mn(t,e){return yn(this,t,e,1)}function gn(t,e){return yn(this,t,e,-1)}function bn(t){return 0>t?Math.floor(t):Math.ceil(t)}function On(){var t,e,n,r,i,o=this._milliseconds,u=this._days,a=this._months,s=this._data;return o>=0&&u>=0&&a>=0||0>=o&&0>=u&&0>=a||(o+=864e5*bn(Sn(a)+u),u=0,a=0),s.milliseconds=o%1e3,t=v(o/1e3),s.seconds=t%60,e=v(t/60),s.minutes=e%60,n=v(e/60),s.hours=n%24,u+=v(n/24),i=v(wn(u)),a+=i,u-=bn(Sn(i)),r=v(a/12),a%=12,s.days=u,s.months=a,s.years=r,this}function wn(t){return 4800*t/146097}function Sn(t){return 146097*t/4800}function Mn(t){var e,n,r=this._milliseconds;if(t=j(t),"month"===t||"year"===t)return e=this._days+r/864e5,n=this._months+wn(e),"month"===t?n:n/12;switch(e=this._days+Math.round(Sn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}}function Tn(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*y(this._months/12)}function En(t){return function(){return this.as(t)}}function jn(t){return t=j(t),this[t+"s"]()}function In(t){return function(){return this._data[t]}}function Pn(){return v(this.days()/7)}function Dn(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}function Cn(t,e,n){var r=Zt(t).abs(),i=di(r.as("s")),o=di(r.as("m")),u=di(r.as("h")),a=di(r.as("d")),s=di(r.as("M")),c=di(r.as("y")),l=i0,l[4]=n,Dn.apply(null,l)}function An(t,e){return void 0===pi[t]?!1:void 0===e?pi[t]:(pi[t]=e,!0)}function kn(t){var e=this.localeData(),n=Cn(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)}function xn(){var t,e,n,r=hi(this._milliseconds)/1e3,i=hi(this._days),o=hi(this._months);t=v(r/60),e=v(t/60),r%=60,t%=60,n=v(o/12),o%=12;var u=n,a=o,s=i,c=e,l=t,f=r,d=this.asSeconds();return d?(0>d?"-":"")+"P"+(u?u+"Y":"")+(a?a+"M":"")+(s?s+"D":"")+(c||l||f?"T":"")+(c?c+"H":"")+(l?l+"M":"")+(f?f+"S":""):"P0D"}var Ln,Nn,zn=e.momentProperties=[],Rn=!1,Hn={},Yn={},Gn=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Fn=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Un={},Bn={},Vn=/\d/,qn=/\d\d/,Wn=/\d{3}/,Kn=/\d{4}/,Jn=/[+-]?\d{6}/,$n=/\d\d?/,Zn=/\d{1,3}/,Xn=/\d{1,4}/,Qn=/[+-]?\d{1,6}/,tr=/\d+/,er=/[+-]?\d+/,nr=/Z|[+-]\d\d:?\d\d/gi,rr=/[+-]?\d+(\.\d{1,3})?/,ir=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,or={},ur={},ar=0,sr=1,cr=2,lr=3,fr=4,dr=5,pr=6;x("M",["MM",2],"Mo",function(){return this.month()+1}),x("MMM",0,0,function(t){return this.localeData().monthsShort(this,t)}),x("MMMM",0,0,function(t){return this.localeData().months(this,t)}),E("month","M"),Y("M",$n),Y("MM",$n,qn),Y("MMM",ir),Y("MMMM",ir),U(["M","MM"],function(t,e){e[sr]=y(t)-1}),U(["MMM","MMMM"],function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[sr]=i:l(n).invalidMonth=t});var hr="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),_r="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),vr={};e.suppressDeprecationWarnings=!1;var yr=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mr=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],gr=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],br=/^\/?Date\((\-?\d+)/i;e.createFromInputFallback=et("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),x(0,["YY",2],0,function(){return this.year()%100}),x(0,["YYYY",4],0,"year"),x(0,["YYYYY",5],0,"year"),x(0,["YYYYYY",6,!0],0,"year"),E("year","y"),Y("Y",er),Y("YY",$n,qn),Y("YYYY",Xn,Kn),Y("YYYYY",Qn,Jn),Y("YYYYYY",Qn,Jn),U(["YYYYY","YYYYYY"],ar),U("YYYY",function(t,n){n[ar]=2===t.length?e.parseTwoDigitYear(t):y(t)}),U("YY",function(t,n){n[ar]=e.parseTwoDigitYear(t)}),e.parseTwoDigitYear=function(t){return y(t)+(y(t)>68?1900:2e3)};var Or=P("FullYear",!1);x("w",["ww",2],"wo","week"),x("W",["WW",2],"Wo","isoWeek"),E("week","w"),E("isoWeek","W"),Y("w",$n),Y("ww",$n,qn),Y("W",$n),Y("WW",$n,qn),B(["w","ww","W","WW"],function(t,e,n,r){e[r.substr(0,1)]=y(t)});var wr={dow:0,doy:6};x("DDD",["DDDD",3],"DDDo","dayOfYear"),E("dayOfYear","DDD"),Y("DDD",Zn),Y("DDDD",Wn),U(["DDD","DDDD"],function(t,e,n){n._dayOfYear=y(t)}),e.ISO_8601=function(){};var Sr=et("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var t=Dt.apply(null,arguments);return this>t?this:t}),Mr=et("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var t=Dt.apply(null,arguments);return t>this?this:t});Nt("Z",":"),Nt("ZZ",""),Y("Z",nr),Y("ZZ",nr),U(["Z","ZZ"],function(t,e,n){n._useUTC=!0,n._tzm=zt(t)});var Tr=/([\+\-]|\d\d)/gi;e.updateOffset=function(){};var Er=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,jr=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Zt.fn=xt.prototype;var Ir=ee(1,"add"),Pr=ee(-1,"subtract");e.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var Dr=et("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return void 0===t?this.localeData():this.locale(t)});x(0,["gg",2],0,function(){return this.weekYear()%100}),x(0,["GG",2],0,function(){return this.isoWeekYear()%100}),De("gggg","weekYear"),De("ggggg","weekYear"),De("GGGG","isoWeekYear"),De("GGGGG","isoWeekYear"),E("weekYear","gg"),E("isoWeekYear","GG"),Y("G",er),Y("g",er),Y("GG",$n,qn),Y("gg",$n,qn),Y("GGGG",Xn,Kn),Y("gggg",Xn,Kn),Y("GGGGG",Qn,Jn),Y("ggggg",Qn,Jn),B(["gggg","ggggg","GGGG","GGGGG"],function(t,e,n,r){e[r.substr(0,2)]=y(t)}),B(["gg","GG"],function(t,n,r,i){n[i]=e.parseTwoDigitYear(t)}),x("Q",0,0,"quarter"),E("quarter","Q"),Y("Q",Vn),U("Q",function(t,e){e[sr]=3*(y(t)-1)}),x("D",["DD",2],"Do","date"),E("date","D"),Y("D",$n),Y("DD",$n,qn),Y("Do",function(t,e){return t?e._ordinalParse:e._ordinalParseLenient}),U(["D","DD"],cr),U("Do",function(t,e){e[cr]=y(t.match($n)[0],10)});var Cr=P("Date",!0);x("d",0,"do","day"),x("dd",0,0,function(t){return this.localeData().weekdaysMin(this,t)}),x("ddd",0,0,function(t){return this.localeData().weekdaysShort(this,t)}),x("dddd",0,0,function(t){return this.localeData().weekdays(this,t)}),x("e",0,0,"weekday"),x("E",0,0,"isoWeekday"),E("day","d"),E("weekday","e"),E("isoWeekday","E"),Y("d",$n),Y("e",$n),Y("E",$n),Y("dd",ir),Y("ddd",ir),Y("dddd",ir),B(["dd","ddd","dddd"],function(t,e,n){var r=n._locale.weekdaysParse(t);null!=r?e.d=r:l(n).invalidWeekday=t}),B(["d","e","E"],function(t,e,n,r){e[r]=y(t)});var Ar="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),kr="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),xr="Su_Mo_Tu_We_Th_Fr_Sa".split("_");x("H",["HH",2],0,"hour"),x("h",["hh",2],0,function(){return this.hours()%12||12}),Ve("a",!0),Ve("A",!1),E("hour","h"),Y("a",qe),Y("A",qe),Y("H",$n),Y("h",$n),Y("HH",$n,qn),Y("hh",$n,qn),U(["H","HH"],lr),U(["a","A"],function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t}),U(["h","hh"],function(t,e,n){e[lr]=y(t),l(n).bigHour=!0});var Lr=/[ap]\.?m?\.?/i,Nr=P("Hours",!0);x("m",["mm",2],0,"minute"),E("minute","m"),Y("m",$n),Y("mm",$n,qn),U(["m","mm"],fr);var zr=P("Minutes",!1);x("s",["ss",2],0,"second"),E("second","s"),Y("s",$n),Y("ss",$n,qn),U(["s","ss"],dr);var Rr=P("Seconds",!1);x("S",0,0,function(){return~~(this.millisecond()/100)}),x(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),x(0,["SSS",3],0,"millisecond"),x(0,["SSSS",4],0,function(){return 10*this.millisecond()}),x(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),x(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),x(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),x(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),x(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),E("millisecond","ms"),Y("S",Zn,Vn),Y("SS",Zn,qn),Y("SSS",Zn,Wn);var Hr;for(Hr="SSSS";Hr.length<=9;Hr+="S")Y(Hr,tr);for(Hr="S";Hr.length<=9;Hr+="S")U(Hr,Je);var Yr=P("Milliseconds",!1);x("z",0,0,"zoneAbbr"),x("zz",0,0,"zoneName");var Gr=h.prototype;Gr.add=Ir,Gr.calendar=re,Gr.clone=ie,Gr.diff=ce,Gr.endOf=Oe,Gr.format=pe,Gr.from=he,Gr.fromNow=_e,Gr.to=ve,Gr.toNow=ye,Gr.get=A,Gr.invalidAt=Pe,Gr.isAfter=oe,Gr.isBefore=ue,Gr.isBetween=ae,Gr.isSame=se,Gr.isValid=je,Gr.lang=Dr,Gr.locale=me,Gr.localeData=ge,Gr.max=Mr,Gr.min=Sr,Gr.parsingFlags=Ie,Gr.set=A,Gr.startOf=be,Gr.subtract=Pr,Gr.toArray=Te,Gr.toObject=Ee,Gr.toDate=Me,Gr.toISOString=de,Gr.toJSON=de,Gr.toString=fe,Gr.unix=Se,Gr.valueOf=we,Gr.year=Or,Gr.isLeapYear=ct,Gr.weekYear=Ae,Gr.isoWeekYear=ke,Gr.quarter=Gr.quarters=Ne,Gr.month=Z,Gr.daysInMonth=X,Gr.week=Gr.weeks=ht,Gr.isoWeek=Gr.isoWeeks=_t,Gr.weeksInYear=Le,Gr.isoWeeksInYear=xe,Gr.date=Cr,Gr.day=Gr.days=Fe,Gr.weekday=Ue,Gr.isoWeekday=Be,Gr.dayOfYear=yt,Gr.hour=Gr.hours=Nr,Gr.minute=Gr.minutes=zr,Gr.second=Gr.seconds=Rr,Gr.millisecond=Gr.milliseconds=Yr,Gr.utcOffset=Yt,Gr.utc=Ft,Gr.local=Ut,Gr.parseZone=Bt,Gr.hasAlignedHourOffset=Vt,Gr.isDST=qt,Gr.isDSTShifted=Wt,Gr.isLocal=Kt,Gr.isUtcOffset=Jt,Gr.isUtc=$t,Gr.isUTC=$t,Gr.zoneAbbr=$e,Gr.zoneName=Ze,Gr.dates=et("dates accessor is deprecated. Use date instead.",Cr),Gr.months=et("months accessor is deprecated. Use month instead",Z),Gr.years=et("years accessor is deprecated. Use year instead",Or),Gr.zone=et("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Gt);var Fr=Gr,Ur={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Br={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Vr="Invalid date",qr="%d",Wr=/\d{1,2}/,Kr={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Jr=g.prototype;Jr._calendar=Ur,Jr.calendar=tn,Jr._longDateFormat=Br,Jr.longDateFormat=en,Jr._invalidDate=Vr,Jr.invalidDate=nn,Jr._ordinal=qr,Jr.ordinal=rn,Jr._ordinalParse=Wr,Jr.preparse=on,Jr.postformat=on,Jr._relativeTime=Kr,Jr.relativeTime=un,Jr.pastFuture=an,Jr.set=sn,Jr.months=W,Jr._months=hr,Jr.monthsShort=K,Jr._monthsShort=_r,Jr.monthsParse=J,Jr.week=ft,Jr._week=wr,Jr.firstDayOfYear=pt,Jr.firstDayOfWeek=dt,Jr.weekdays=Re,Jr._weekdays=Ar,Jr.weekdaysMin=Ye,Jr._weekdaysMin=xr,Jr.weekdaysShort=He,Jr._weekdaysShort=kr,Jr.weekdaysParse=Ge,Jr.isPM=We,Jr._meridiemParse=Lr,Jr.meridiem=Ke,S("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,n=1===y(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+n}}),e.lang=et("moment.lang is deprecated. Use moment.locale instead.",S),e.langData=et("moment.langData is deprecated. Use moment.localeData instead.",T);var $r=Math.abs,Zr=En("ms"),Xr=En("s"),Qr=En("m"),ti=En("h"),ei=En("d"),ni=En("w"),ri=En("M"),ii=En("y"),oi=In("milliseconds"),ui=In("seconds"),ai=In("minutes"),si=In("hours"),ci=In("days"),li=In("months"),fi=In("years"),di=Math.round,pi={s:45,m:45,h:22,d:26,M:11},hi=Math.abs,_i=xt.prototype;_i.abs=vn,_i.add=mn,_i.subtract=gn,_i.as=Mn,_i.asMilliseconds=Zr,_i.asSeconds=Xr,_i.asMinutes=Qr,_i.asHours=ti,_i.asDays=ei,_i.asWeeks=ni,_i.asMonths=ri,_i.asYears=ii,_i.valueOf=Tn,_i._bubble=On,_i.get=jn,_i.milliseconds=oi,_i.seconds=ui,_i.minutes=ai,_i.hours=si,_i.days=ci,_i.weeks=Pn,_i.months=li,_i.years=fi,_i.humanize=kn,_i.toISOString=xn,_i.toString=xn,_i.toJSON=xn,_i.locale=me,_i.localeData=ge,_i.toIsoString=et("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",xn),_i.lang=Dr,x("X",0,0,"unix"),x("x",0,0,"valueOf"),Y("x",er),Y("X",rr),U("X",function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))}),U("x",function(t,e,n){n._d=new Date(y(t))}),e.version="2.10.6",n(Dt),e.fn=Fr,e.min=At,e.max=kt,e.utc=s,e.unix=Xe,e.months=fn,e.isDate=i,e.locale=S,e.invalid=d,e.duration=Zt,e.isMoment=_,e.weekdays=pn,e.parseZone=Qe,e.localeData=T,e.isDuration=Lt,e.monthsShort=dn,e.weekdaysMin=_n,e.defineLocale=M,e.weekdaysShort=hn,e.normalizeUnits=j,e.relativeTimeThreshold=An;var vi=e;return vi})}).call(e,n(72)(t))},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);e["default"]=new u["default"]({is:"ha-entity-toggle",properties:{stateObj:{type:Object,observer:"stateObjChanged"},toggleChecked:{type:Boolean,value:!1}},ready:function(){this.forceStateChange()},toggleChanged:function(t){var e=t.target.checked;e&&"off"===this.stateObj.state?this.turn_on():e||"off"===this.stateObj.state||this.turn_off()},stateObjChanged:function(t){t&&this.updateToggle(t)},updateToggle:function(t){this.toggleChecked=t&&"off"!==t.state},forceStateChange:function(){this.updateToggle(this.stateObj)},turn_on:function(){var t=this;i.serviceActions.callTurnOn(this.stateObj.entityId).then(function(){return t.forceStateChange()})},turn_off:function(){var t=this;i.serviceActions.callTurnOff(this.stateObj.entityId).then(function(){return t.forceStateChange()})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"ha-card",properties:{title:{type:String},header:{type:String}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(34),o=r(i),u=n(2),a=n(1),s=r(a),c=6e4,l=u.util.parseDateTime;e["default"]=new s["default"]({is:"relative-ha-datetime",properties:{datetime:{type:String,observer:"datetimeChanged"},datetimeObj:{type:Object,observer:"datetimeObjChanged"},parsedDateTime:{type:Object},relativeTime:{type:String,value:"not set"}},created:function(){this.updateRelative=this.updateRelative.bind(this)},attached:function(){this._interval=setInterval(this.updateRelative,c)},detached:function(){clearInterval(this._interval)},datetimeChanged:function(t){this.parsedDateTime=t?l(t):null,this.updateRelative()},datetimeObjChanged:function(t){this.parsedDateTime=t,this.updateRelative()},updateRelative:function(){this.relativeTime=this.parsedDateTime?o["default"](this.parsedDateTime).fromNow():""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(19),n(91),n(90),e["default"]=new o["default"]({is:"state-history-charts",properties:{stateHistory:{type:Object},isLoadingData:{type:Boolean,value:!1},apiLoaded:{type:Boolean,value:!1},isLoading:{type:Boolean,computed:"computeIsLoading(isLoadingData, apiLoaded)"},groupedStateHistory:{type:Object,computed:"computeGroupedStateHistory(isLoading, stateHistory)"},isSingleDevice:{type:Boolean,computed:"computeIsSingleDevice(stateHistory)"}},computeIsSingleDevice:function(t){return t&&1===t.size},computeGroupedStateHistory:function(t,e){if(t||!e)return{line:[],timeline:[]};var n={},r=[];e.forEach(function(t){if(t&&0!==t.size){var e=t.find(function(t){return"unit_of_measurement"in t.attributes}),i=e?e.attributes.unit_of_measurement:!1;i?i in n?n[i].push(t.toArray()):n[i]=[t.toArray()]:r.push(t.toArray())}}),r=r.length>0&&r;var i=Object.keys(n).map(function(t){return[t,n[t]]});return{line:i,timeline:r}},googleApiLoaded:function(){var t=this;window.google.load("visualization","1",{packages:["timeline","corechart"],callback:function(){return t.apiLoaded=!0}})},computeContentClasses:function(t){return t?"loading":""},computeIsLoading:function(t,e){return t||!e},computeIsEmpty:function(t){return t&&0===t.size},extractUnit:function(t){return t[0]},extractData:function(t){return t[1]}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),e["default"]=new o["default"]({is:"state-card-display",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e){"use strict";function n(t,e){switch(t){case"homeassistant":return"home";case"group":return"homeassistant-24:group";case"device_tracker":return"social:person";case"switch":return"image:flash-on";case"alarm_control_panel":return e&&"disarmed"===e?"icons:lock-open":"icons:lock";case"media_player":var n="hardware:cast";return e&&"off"!==e&&"idle"!==e&&(n+="-connected"),n;case"sun":return"image:wb-sunny";case"light":return"image:wb-incandescent";case"simple_alarm":return"social:notifications";case"notify":return"announcement";case"thermostat":return"homeassistant-100:thermostat";case"sensor":return"visibility";case"configurator":return"settings";case"conversation":return"av:hearing";case"script":return"description";case"scene":return"social:pages";case"updater":return"update_available"===e?"icons:cloud-download":"icons:cloud-done";default:return"bookmark"}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return u["default"](t).format("LT")}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(34),u=r(o);t.exports=e["default"]},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2);e["default"]=function(t,e){r.authActions.validate(t,{rememberAuth:e,useStreaming:r.localStoragePreferences.useStreaming})},t.exports=e["default"]},function(t,e,n){"use strict";function r(t,e){var n=null==t?void 0:t[e];return i(n)?n:void 0}var i=n(129);t.exports=r},function(t,e){"use strict";function n(t){return!!t&&"object"==typeof t}t.exports=n},function(t,e,n){"use strict";function r(t){return i(t)&&a.call(t)==o}var i=n(46),o="[object Function]",u=Object.prototype,a=u.toString;t.exports=r},function(t,e){"use strict";function n(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=["isLoadingEntityHistory"];e.isLoadingEntityHistory=i;var o=["currentEntityHistoryDate"];e.currentDate=o;var u=["entityHistory"];e.entityHistoryMap=u;var a=[o,u,function(t,e){return e.get(t)||r.toImmutable({})}];e.entityHistoryForCurrentDate=a;var s=[o,u,function(t,e){return!!e.get(t)}];e.hasDataForCurrentDate=s;var c=["recentEntityHistory"];e.recentEntityHistoryMap=c;var l=["recentEntityHistory"];e.recentEntityHistoryUpdatedMap=l},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({currentEntityHistoryDate:a["default"],entityHistory:c["default"],isLoadingEntityHistory:f["default"],recentEntityHistory:p["default"],recentEntityHistoryUpdated:_["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(141),a=i(u),s=n(142),c=i(s),l=n(143),f=i(l),d=n(144),p=i(d),h=n(145),_=i(h),v=n(140),y=r(v),m=n(47),g=r(m),b=y;e.actions=b;var O=g;e.getters=O},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function t(t,e){for(var n=0;n6e4}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var u=n(167),a=n(187),s=i(a),c=n(189),l=i(c),f=n(191),d=i(f),p=n(15),h=r(p),_=n(25),v=r(_),y=n(7),m=r(y),g=n(48),b=r(g),O=n(26),w=r(O),S=n(152),M=r(S),T=n(51),E=r(T),j=n(54),I=r(j),P=n(28),D=r(P),C=n(13),A=r(C),k=n(29),x=r(k),L=n(31),N=r(L),z=n(184),R=r(z),H=n(8),Y=r(H),G=function F(){o(this,F);var t=s["default"]();Object.defineProperties(this,{demo:{value:!1,enumerable:!0},localStoragePreferences:{value:u.localStoragePreferences,enumerable:!0},reactor:{value:t,enumerable:!0},util:{value:d["default"],enumerable:!0},startLocalStoragePreferencesSync:{value:u.localStoragePreferences.startSync.bind(u.localStoragePreferences,t)},startUrlSync:{value:I.urlSync.startSync.bind(null,t)},stopUrlSync:{value:I.urlSync.stopSync.bind(null,t)}}),l["default"](this,t,{auth:h,config:v,entity:m,entityHistory:b,event:w,logbook:M,moreInfo:E,navigation:I,notification:D,service:A,stream:x,sync:N,voice:R,restApi:Y})};e["default"]=G,t.exports=e["default"]},function(t,e){"use strict";function n(t){return function(e){return null==e?void 0:e[t]}}t.exports=n},function(t,e,n){"use strict";var r=n(64),i=r("length");t.exports=i},function(t,e,n){"use strict";function r(t){return null!=t&&o(i(t))}var i=n(65),o=n(69);t.exports=r},function(t,e){"use strict";function n(t,e){return t="number"==typeof t||r.test(t)?+t:-1,e=null==e?i:e,t>-1&&t%1==0&&e>t}var r=/^\d+$/,i=9007199254740991;t.exports=n},function(t,e,n){"use strict";function r(t,e,n){if(!u(n))return!1;var r=typeof e;if("number"==r?i(n)&&o(e,n.length):"string"==r&&e in n){var a=n[e];return t===t?t===a:a!==a}return!1}var i=n(66),o=n(67),u=n(70);t.exports=r},function(t,e){"use strict";function n(t){return"number"==typeof t&&t>-1&&t%1==0&&r>=t}var r=9007199254740991;t.exports=n},function(t,e){"use strict";function n(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){"use strict";function r(t,e,n){n&&i(t,e,n)&&(e=n=void 0),t=+t||0,n=null==n?1:+n||0,null==e?(e=t,t=0):e=+e||0;for(var r=-1,a=u(o((e-t)/(n||1)),0),s=Array(a);++r1}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(36),e["default"]=new o["default"]({is:"ha-introduction-card",properties:{showInstallInstruction:{type:Boolean,value:!1},showHideInstruction:{type:Boolean,value:!0}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(41),a=r(u);e["default"]=new o["default"]({is:"display-time",properties:{dateObj:{type:Object}},computeTime:function(t){return t?a["default"](t):""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"entity-list",behaviors:[s["default"]],properties:{entities:{type:Array,bindNuclear:[i.entityGetters.entityMap,function(t){return t.valueSeq().sortBy(function(t){return t.entityId}).toArray()}]}},entitySelected:function(t){t.preventDefault(),this.fire("entity-selected",{entityId:t.model.entity.entityId})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);n(18),e["default"]=new o["default"]({is:"ha-entity-marker",properties:{entityId:{type:String,value:""},state:{type:Object,computed:"computeState(entityId)"},icon:{type:Object,computed:"computeIcon(state)"},image:{type:Object,computed:"computeImage(state)"},value:{type:String,computed:"computeValue(state)"}},listeners:{click:"badgeTap"},badgeTap:function(t){var e=this;t.stopPropagation(),this.entityId&&this.async(function(){return u.moreInfoActions.selectEntity(e.entityId)},1)},computeState:function(t){return t&&u.reactor.evaluate(u.entityGetters.byId(t))},computeIcon:function(t){return!t&&"home"},computeImage:function(t){return t&&t.attributes.entity_picture},computeValue:function(t){return t&&t.entityDisplay.split(" ").map(function(t){return t.substr(0,1)}).join("")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(40),s=r(a),c=n(22),l=r(c);n(18),e["default"]=new o["default"]({is:"ha-state-label-badge",properties:{state:{type:Object,observer:"stateChanged"}},listeners:{click:"badgeTap"},badgeTap:function(t){var e=this;return t.stopPropagation(),l["default"](this.state.entityId)?void("scene"===this.state.domain?u.serviceActions.callTurnOn(this.state.entityId):"off"===this.state.state?u.serviceActions.callTurnOn(this.state.entityId):u.serviceActions.callTurnOff(this.state.entityId)):void this.async(function(){return u.moreInfoActions.selectEntity(e.state.entityId)},1)},computeClasses:function(t){switch(t.domain){case"scene":return"green";case"script":return"on"===t.state?"blue":"grey";default:return""}},computeGlow:function(t){switch(t.domain){case"scene":case"script":return"on"===t.state;default:return!1}},computeValue:function(t){switch(t.domain){case"device_tracker":case"sun":case"scene":case"script":case"alarm_control_panel":return void 0;case"sensor":return t.attributes.unit_of_measurement&&t.state;default:return t.state}},computeIcon:function(t){switch(t.domain){case"device_tracker":case"alarm_control_panel":case"scene":case"script":return s["default"](t.domain,t.state);case"sensor":return!t.attributes.unit_of_measurement&&s["default"](t.domain);case"sun":return"above_horizon"===t.state?"image:wb-sunny":"image:brightness-3";default:return void 0}},computeImage:function(t){return t.attributes.entity_picture},computeLabel:function(t){switch(t.domain){case"scene":case"script":return t.domain;case"sensor":return t.attributes.unit_of_measurement||t.state;case"device_tracker":return"not_home"===t.state?"Away":t.state;case"alarm_control_panel":return t.state;default:return t.attributes.unit_of_measurement}},computeDescription:function(t){return t.entityDisplay},stateChanged:function(){this.updateStyles()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(124),a=r(u);n(17),e["default"]=new o["default"]({is:"state-badge",properties:{stateObj:{type:Object,observer:"updateIconColor"}},updateIconColor:function(t){if("light"===t.domain&&"on"===t.state&&t.attributes.brightness&&t.attributes.xy_color){var e=a["default"](t.attributes.xy_color[0],t.attributes.xy_color[1],t.attributes.brightness);this.$.icon.style.color="rgb("+e.map(Math.floor).join(",")+")"}else this.$.icon.style.color=null}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"events-list",behaviors:[s["default"]],properties:{events:{type:Array,bindNuclear:[i.eventGetters.entityMap,function(t){return t.valueSeq().sortBy(function(t){return t.event}).toArray()}]}},eventSelected:function(t){t.preventDefault(),this.fire("event-selected",{eventType:t.model.event.event})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){var e=t.toString(16);return 1===e.length?"0"+e:e}function o(t){return"#"+i(t.r)+i(t.g)+i(t.b)}Object.defineProperty(e,"__esModule",{value:!0});var u=n(1),a=r(u);e["default"]=new a["default"]({is:"ha-color-picker",properties:{width:{type:Number,value:300},height:{type:Number,value:300},color:{type:Object}},listeners:{mousedown:"onMouseDown",mouseup:"onMouseUp",touchstart:"onTouchStart",touchend:"onTouchEnd",tap:"onTap"},onMouseDown:function(t){this.onMouseMove(t),this.addEventListener("mousemove",this.onMouseMove)},onMouseUp:function(){this.removeEventListener("mousemove",this.onMouseMove)},onTouchStart:function(t){this.onTouchMove(t),this.addEventListener("touchmove",this.onTouchMove)},onTouchEnd:function(){this.removeEventListener("touchmove",this.onTouchMove)},onTap:function(t){t.stopPropagation()},onTouchMove:function(t){var e=t.touches[0];this.onColorSelect(t,{x:e.clientX,y:e.clientY})},onMouseMove:function(t){var e=this;t.preventDefault(),this.mouseMoveIsThrottled&&(this.mouseMoveIsThrottled=!1,this.onColorSelect(t),this.async(function(){return e.mouseMoveIsThrottled=!0},100))},onColorSelect:function(t,e){if(this.context){var n=e||this.relativeMouseCoordinates(t),r=this.context.getImageData(n.x,n.y,1,1).data;this.setColor({r:r[0],g:r[1],b:r[2]})}},setColor:function(t){this.color={hex:o(t),rgb:t},this.fire("colorselected",{rgb:this.color.rgb,hex:this.color.hex})},relativeMouseCoordinates:function(t){var e=0,n=0;if(this.canvas){var r=this.canvas.getBoundingClientRect();e=t.clientX-r.left,n=t.clientY-r.top}return{x:e,y:n}},ready:function(){this.setColor=this.setColor.bind(this),this.mouseMoveIsThrottled=!0,this.canvas=this.children[0],this.context=this.canvas.getContext("2d");var t=this.context.createLinearGradient(0,0,this.width,0);t.addColorStop(0,"rgb(255,0,0)"),t.addColorStop(.16,"rgb(255,0,255)"),t.addColorStop(.32,"rgb(0,0,255)"),t.addColorStop(.48,"rgb(0,255,255)"),t.addColorStop(.64,"rgb(0,255,0)"),t.addColorStop(.8,"rgb(255,255,0)"),t.addColorStop(1,"rgb(255,0,0)"),this.context.fillStyle=t,this.context.fillRect(0,0,this.width,this.height);var e=this.context.createLinearGradient(0,0,0,this.height);e.addColorStop(0,"rgba(255,255,255,1)"),e.addColorStop(.5,"rgba(255,255,255,0)"),e.addColorStop(.5,"rgba(0,0,0,0)"),e.addColorStop(1,"rgba(0,0,0,1)"),this.context.fillStyle=e,this.context.fillRect(0,0,this.width,this.height)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(18),e["default"]=new o["default"]({is:"ha-demo-badge"}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(88),e["default"]=new o["default"]({is:"ha-logbook",properties:{entries:{type:Object,value:[]}},noEntries:function(t){return!t.length}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(92),e["default"]=new u["default"]({is:"ha-sidebar",behaviors:[s["default"]],properties:{menuShown:{type:Boolean},menuSelected:{type:String},selected:{type:String,bindNuclear:i.navigationGetters.activePane,observer:"selectedChanged"},hasHistoryComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("history")},hasLogbookComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("logbook")}},selectedChanged:function(t){for(var e=this.querySelectorAll(".menu [data-panel]"),n=0;nd;d++)f._columns[d]=[];var p=0;return n&&u(),c.keySeq().sortBy(function(t){return i(t)}).forEach(function(t){if("a"===t)return void(f._demo=!0);var n=i(t);n>=0&&10>n?f._badges.push.apply(f._badges,r(c.get(t)).sortBy(o).toArray()):"group"===t?c.get(t).filter(function(t){return!t.attributes.auto}).sortBy(o).forEach(function(t){var n=s.util.expandGroup(t,e);n.forEach(function(t){return l[t.entityId]=!0}),a(t.entityDisplay,n.toArray(),t)}):a(t,r(c.get(t)).sortBy(o).toArray())}),f},computeShouldRenderColumn:function(t,e){return 0===t||e.length},computeShowIntroduction:function(t,e,n){return 0===t&&(e||n._demo)},computeShowHideInstruction:function(t,e){return t.size>0&&!0&&!e._demo},computeGroupEntityOfCard:function(t,e){return t[e].groupEntity},computeStatesOfCard:function(t,e){return t[e].entities}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(17),n(76),n(37),e["default"]=new u["default"]({is:"logbook-entry",entityClicked:function(t){t.preventDefault(),i.moreInfoActions.selectEntity(this.entryObj.entityId)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(17),e["default"]=new u["default"]({is:"services-list",behaviors:[s["default"]],properties:{serviceDomains:{type:Array,bindNuclear:i.serviceGetters.entityMap}},computeDomains:function(t){return t.valueSeq().map(function(t){return t.domain}).sort().toJS()},computeServices:function(t,e){return t.get(e).get("services").keySeq().toArray()},serviceClicked:function(t){t.preventDefault(),this.fire("service-selected",{domain:t.model.domain,service:t.model.service})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){var e=parseFloat(t);return!isNaN(e)&&isFinite(e)?e:null}Object.defineProperty(e,"__esModule",{value:!0});var o=n(71),u=r(o),a=n(1),s=r(a);e["default"]=new s["default"]({is:"state-history-chart-line",properties:{data:{type:Object,observer:"dataChanged"},unit:{type:String},isSingleDevice:{type:Boolean,value:!1},isAttached:{type:Boolean,value:!1,observer:"dataChanged"},chartEngine:{type:Object}},created:function(){this.style.display="block"},attached:function(){this.isAttached=!0},dataChanged:function(){this.drawChart()},drawChart:function(){if(this.isAttached){this.chartEngine||(this.chartEngine=new window.google.visualization.LineChart(this));var t=this.unit,e=this.data;if(0!==e.length){var n={legend:{position:"top"},interpolateNulls:!0,titlePosition:"none",vAxes:{0:{title:t}},hAxis:{format:"H:mm"},chartArea:{left:"60",width:"95%"},explorer:{actions:["dragToZoom","rightClickToReset","dragToPan"],keepInBounds:!0,axis:"horizontal",maxZoomIn:.1}};this.isSingleDevice&&(n.legend.position="none",n.vAxes[0].title=null,n.chartArea.left=40,n.chartArea.height="80%",n.chartArea.top=5,n.enableInteractivity=!1);var r=new Date(Math.min.apply(null,e.map(function(t){return t[0].lastChangedAsDate}))),o=new Date(r);o.setDate(o.getDate()+1),o>new Date&&(o=new Date);var a=e.map(function(t){function e(t,e){c&&e&&s.push([t[0]].concat(c.slice(1).map(function(t,n){return e[n]?t:null}))),s.push(t),c=t}var n=t[t.length-1],r=n.domain,u=n.entityDisplay,a=new window.google.visualization.DataTable;a.addColumn({type:"datetime",id:"Time"});var s=[],c=void 0;if("thermostat"===r){var l=t.reduce(function(t,e){return t||e.attributes.target_temp_high!==e.attributes.target_temp_low},!1);a.addColumn("number",u+" current temperature");var f=void 0;l?!function(){a.addColumn("number",u+" target temperature high"),a.addColumn("number",u+" target temperature low");var t=[!1,!0,!0];f=function(n){var r=i(n.attributes.current_temperature),o=i(n.attributes.target_temp_high),u=i(n.attributes.target_temp_low);e([n.lastChangedAsDate,r,o,u],t)}}():!function(){a.addColumn("number",u+" target temperature");var t=[!1,!0];f=function(n){var r=i(n.attributes.current_temperature),o=i(n.attributes.temperature);e([n.lastChangedAsDate,r,o],t)}}(),t.forEach(f)}else!function(){a.addColumn("number",u);var n="sensor"!==r&&[!0];t.forEach(function(t){var r=i(t.state);e([t.lastChangedAsDate,r],n)})}();return e([o].concat(c.slice(1)),!1),a.addRows(s),a}),s=void 0;s=1===a.length?a[0]:a.slice(1).reduce(function(t,e){return window.google.visualization.data.join(t,e,"full",[[0,0]],u["default"](1,t.getNumberOfColumns()),u["default"](1,e.getNumberOfColumns()))},a[0]),this.chartEngine.draw(s,n)}}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"state-history-chart-timeline",properties:{data:{type:Object,observer:"dataChanged"},isAttached:{type:Boolean,value:!1,observer:"dataChanged"}},attached:function(){this.isAttached=!0},dataChanged:function(){this.drawChart()},drawChart:function(){function t(t,e,n,r){var o=e.replace(/_/g," ");i.addRow([t,o,n,r])}if(this.isAttached){for(var e=o["default"].dom(this),n=this.data;e.node.lastChild;)e.node.removeChild(e.node.lastChild);if(n&&0!==n.length){var r=new window.google.visualization.Timeline(this),i=new window.google.visualization.DataTable;i.addColumn({type:"string",id:"Entity"}),i.addColumn({type:"string",id:"State"}),i.addColumn({type:"date",id:"Start"}),i.addColumn({type:"date",id:"End"});var u=new Date(n.reduce(function(t,e){return Math.min(t,e[0].lastChangedAsDate)},new Date)),a=new Date(u);a.setDate(a.getDate()+1),a>new Date&&(a=new Date);var s=0;n.forEach(function(e){if(0!==e.length){var n=e[0].entityDisplay,r=void 0,i=null,o=null;e.forEach(function(e){null!==i&&e.state!==i?(r=e.lastChangedAsDate,t(n,i,o,r),i=e.state,o=r):null===i&&(i=e.state,o=e.lastChangedAsDate)}),t(n,i,o,a),s++}}),r.draw(i,{height:55+42*s,timeline:{showRowLabels:n.length>1},hAxis:{format:"H:mm"}})}}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"stream-status",behaviors:[s["default"]],properties:{isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},hasError:{type:Boolean,bindNuclear:i.streamGetters.hasStreamingEventsError}},toggleChanged:function(){this.isStreaming?i.streamActions.stop():i.streamActions.start()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(20),n(38),n(107);var c=["camera","configurator","scene"];e["default"]=new u["default"]({is:"more-info-dialog",behaviors:[s["default"]],properties:{stateObj:{type:Object,bindNuclear:i.moreInfoGetters.currentEntity,observer:"stateObjChanged"},stateHistory:{type:Object,bindNuclear:[i.moreInfoGetters.currentEntityHistory,function(t){return t?[t]:!1}]},isLoadingHistoryData:{type:Boolean,computed:"computeIsLoadingHistoryData(_delayedDialogOpen, _isLoadingHistoryData)"},_isLoadingHistoryData:{type:Boolean,bindNuclear:i.entityHistoryGetters.isLoadingEntityHistory},hasHistoryComponent:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("history"),observer:"fetchHistoryData"},shouldFetchHistory:{type:Boolean,bindNuclear:i.moreInfoGetters.isCurrentEntityHistoryStale,observer:"fetchHistoryData"},showHistoryComponent:{type:Boolean,value:!1},dialogOpen:{type:Boolean,value:!1,observer:"dialogOpenChanged"},_delayedDialogOpen:{type:Boolean,value:!1},_boundOnBackdropTap:{type:Function,value:function(){return this._onBackdropTap.bind(this)}}},computeIsLoadingHistoryData:function(t,e){return!t||e},fetchHistoryData:function(){this.stateObj&&this.hasHistoryComponent&&this.shouldFetchHistory&&i.entityHistoryActions.fetchRecent(this.stateObj.entityId)},stateObjChanged:function(t){var e=this;return t?(this.showHistoryComponent=this.hasHistoryComponent&&-1===c.indexOf(this.stateObj.domain),void this.async(function(){e.fetchHistoryData(),e.dialogOpen=!0},10)):void(this.dialogOpen=!1)},dialogOpenChanged:function(t){var e=this;t?(this.$.dialog.backdropElement.addEventListener("click",this._boundOnBackdropTap),this.async(function(){return e._delayedDialogOpen=!0},10)):!t&&this.stateObj&&(i.moreInfoActions.deselectEntity(),this._delayedDialogOpen=!1)},_onBackdropTap:function(){this.$.dialog.backdropElement.removeEventListener("click",this._boundOnBackdropTap),this.dialogOpen=!1}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(4),s=r(a);n(85),n(102),n(100),n(99),n(101),n(96),n(97),n(98),n(103),n(93),e["default"]=new o["default"]({is:"home-assistant-main",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},activePane:{type:String,bindNuclear:u.navigationGetters.activePane,observer:"activePaneChanged"},isSelectedStates:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("states")},isSelectedHistory:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("history")},isSelectedMap:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("map")},isSelectedLogbook:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("logbook")},isSelectedDevEvent:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devEvent")},isSelectedDevState:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devState")},isSelectedDevService:{type:Boolean,bindNuclear:u.navigationGetters.isActivePane("devService")},showSidebar:{type:Boolean,bindNuclear:u.navigationGetters.showSidebar}},listeners:{"open-menu":"openMenu","close-menu":"closeMenu"},openMenu:function(){this.narrow?this.$.drawer.openDrawer():u.navigationActions.showSidebar(!0)},closeMenu:function(){this.$.drawer.closeDrawer(),this.showSidebar&&u.navigationActions.showSidebar(!1)},activePaneChanged:function(){this.narrow&&this.$.drawer.closeDrawer()},attached:function(){u.startUrlSync()},computeForceNarrow:function(t,e){return t||!e},detached:function(){u.stopUrlSync()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(4),s=r(a),c=n(42),l=r(c);e["default"]=new o["default"]({is:"login-form",behaviors:[s["default"]],properties:{isValidating:{type:Boolean,observer:"isValidatingChanged",bindNuclear:u.authGetters.isValidating},isInvalid:{type:Boolean,bindNuclear:u.authGetters.isInvalidAttempt},errorMessage:{type:String,bindNuclear:u.authGetters.attemptErrorMessage}},listeners:{keydown:"passwordKeyDown","loginButton.click":"validatePassword"},observers:["validatingChanged(isValidating, isInvalid)"],validatingChanged:function(t,e){t||e||(this.$.passwordInput.value="")},isValidatingChanged:function(t){var e=this;t||this.async(function(){return e.$.passwordInput.focus()},10)},passwordKeyDown:function(t){13===t.keyCode?(this.validatePassword(),t.preventDefault()):this.isInvalid&&(this.isInvalid=!1)},validatePassword:function(){this.$.hideKeyboardOnFocus.focus(),l["default"](this.$.passwordInput.value,this.$.rememberLogin.checked)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(89),e["default"]=new u["default"]({is:"partial-dev-call-service",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},domain:{type:String,value:""},service:{type:String,value:""},serviceData:{type:String,value:""},description:{type:String,computed:"computeDescription(domain, service)"}},computeDescription:function(t,e){return i.reactor.evaluate([i.serviceGetters.entityMap,function(n){return n.has(t)&&n.get(t).get("services").has(e)?JSON.stringify(n.get(t).get("services").get(e).toJS(),null,2):"No description available"; +}])},serviceSelected:function(t){this.domain=t.detail.domain,this.service=t.detail.service},callService:function(){var t=void 0;try{t=this.serviceData?JSON.parse(this.serviceData):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.serviceActions.callService(this.domain,this.service,t)},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(81),e["default"]=new u["default"]({is:"partial-dev-fire-event",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},eventType:{type:String,value:""},eventData:{type:String,value:""}},eventSelected:function(t){this.eventType=t.detail.eventType},fireEvent:function(){var t=void 0;try{t=this.eventData?JSON.parse(this.eventData):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.eventActions.fireEvent(this.eventType,t)},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);n(10),n(77),e["default"]=new u["default"]({is:"partial-dev-set-state",properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},entityId:{type:String,value:""},state:{type:String,value:""},stateAttributes:{type:String,value:""}},setStateData:function(t){var e=t?JSON.stringify(t,null," "):"";this.$.inputData.value=e,this.$.inputDataWrapper.update(this.$.inputData)},entitySelected:function(t){var e=i.reactor.evaluate(i.entityGetters.byId(t.detail.entityId));this.entityId=e.entityId,this.state=e.state,this.stateAttributes=JSON.stringify(e.attributes,null," ")},handleSetState:function(){var t=void 0;try{t=this.stateAttributes?JSON.parse(this.stateAttributes):{}}catch(e){return void alert("Error parsing JSON: "+e)}i.entityActions.save({entityId:this.entityId,state:this.state,attributes:t})},computeFormClasses:function(t){return"layout "+(t?"vertical":"horizontal")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(38),e["default"]=new u["default"]({is:"partial-history",behaviors:[s["default"]],properties:{narrow:{type:Boolean},showMenu:{type:Boolean,value:!1},isDataLoaded:{type:Boolean,bindNuclear:i.entityHistoryGetters.hasDataForCurrentDate,observer:"isDataLoadedChanged"},stateHistory:{type:Object,bindNuclear:i.entityHistoryGetters.entityHistoryForCurrentDate},isLoadingData:{type:Boolean,bindNuclear:i.entityHistoryGetters.isLoadingEntityHistory},selectedDate:{type:String,value:null,bindNuclear:i.entityHistoryGetters.currentDate}},isDataLoadedChanged:function(t){t||this.async(function(){return i.entityHistoryActions.fetchSelectedDate()},1)},handleRefreshClick:function(){i.entityHistoryActions.fetchSelectedDate()},datepickerFocus:function(){this.datePicker.adjustPosition()},attached:function(){this.datePicker=new window.Pikaday({field:this.$.datePicker.inputElement,onSelect:i.entityHistoryActions.changeCurrentDate})},detached:function(){this.datePicker.destroy()},computeContentClasses:function(t){return"flex content "+(t?"narrow":"wide")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(84),n(19),e["default"]=new u["default"]({is:"partial-logbook",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},showMenu:{type:Boolean,value:!1},selectedDate:{type:String,bindNuclear:i.logbookGetters.currentDate},isLoading:{type:Boolean,bindNuclear:i.logbookGetters.isLoadingEntries},isStale:{type:Boolean,bindNuclear:i.logbookGetters.isCurrentStale,observer:"isStaleChanged"},entries:{type:Array,bindNuclear:[i.logbookGetters.currentEntries,function(t){return t.reverse().toArray()}]},datePicker:{type:Object}},isStaleChanged:function(t){var e=this;t&&this.async(function(){return i.logbookActions.fetchDate(e.selectedDate)},1)},handleRefresh:function(){i.logbookActions.fetchDate(this.selectedDate)},datepickerFocus:function(){this.datePicker.adjustPosition()},attached:function(){this.datePicker=new window.Pikaday({field:this.$.datePicker.inputElement,onSelect:i.logbookActions.changeCurrentDate})},detached:function(){this.datePicker.destroy()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(78),window.L.Icon.Default.imagePath="/static/images/leaflet",e["default"]=new u["default"]({is:"partial-map",behaviors:[s["default"]],properties:{locationGPS:{type:Number,bindNuclear:i.configGetters.locationGPS},locationName:{type:String,bindNuclear:i.configGetters.locationName},locationEntities:{type:Array,bindNuclear:[i.entityGetters.visibleEntityMap,function(t){return t.valueSeq().filter(function(t){return t.attributes.latitude&&"home"!==t.state}).toArray()}]},zoneEntities:{type:Array,bindNuclear:[i.entityGetters.entityMap,function(t){return t.valueSeq().filter(function(t){return"zone"===t.domain}).toArray()}]},narrow:{type:Boolean},showMenu:{type:Boolean,value:!1}},attached:function(){var t=this;window.L.Browser.mobileWebkit&&this.async(function(){var e=t.$.map,n=e.style.display;e.style.display="none",t.async(function(){e.style.display=n},1)},1)},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(10),n(86),n(87),e["default"]=new u["default"]({is:"partial-zone",behaviors:[s["default"]],properties:{narrow:{type:Boolean,value:!1},isFetching:{type:Boolean,bindNuclear:i.syncGetters.isFetching},isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},canListen:{type:Boolean,bindNuclear:[i.voiceGetters.isVoiceSupported,i.configGetters.isComponentLoaded("conversation"),function(t,e){return t&&e}]},isListening:{type:Boolean,bindNuclear:i.voiceGetters.isListening},showListenInterface:{type:Boolean,bindNuclear:[i.voiceGetters.isListening,i.voiceGetters.isTransmitting,function(t,e){return t||e}]},introductionLoaded:{type:Boolean,bindNuclear:i.configGetters.isComponentLoaded("introduction")},locationName:{type:String,bindNuclear:i.configGetters.locationName},showMenu:{type:Boolean,value:!1,observer:"windowChange"},states:{type:Object,bindNuclear:i.entityGetters.visibleEntityMap},columns:{type:Number}},created:function(){var t=this;this.windowChange=this.windowChange.bind(this);for(var e=[],n=0;5>n;n++)e.push(278+278*n);this.mqls=e.map(function(e){var n=window.matchMedia("(min-width: "+e+"px)");return n.addListener(t.windowChange),n})},detached:function(){var t=this;this.mqls.forEach(function(e){return e.removeListener(t.windowChange)})},windowChange:function(){var t=this.mqls.reduce(function(t,e){return t+e.matches},0);this.columns=Math.max(1,t-this.showMenu)},handleRefresh:function(){i.syncActions.fetchAll()},handleListenClick:function(){this.isListening?i.voiceActions.stop():i.voiceActions.listen()},computeDomains:function(t){return t.keySeq().toArray()},computeMenuButtonClass:function(t,e){return!t&&e?"invisible":""},computeStatesOfDomain:function(t,e){return t.get(e).toArray()},computeListenButtonIcon:function(t){return t?"av:mic-off":"av:mic"},computeRefreshButtonClass:function(t){return t?"ha-spin":void 0},computeShowIntroduction:function(t,e){return t||0===e.size},toggleMenu:function(){this.fire("open-menu")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);e["default"]=new u["default"]({is:"notification-manager",behaviors:[s["default"]],properties:{text:{type:String,bindNuclear:i.notificationGetters.lastNotificationMessage,observer:"showNotification"}},showNotification:function(t){t&&this.$.toast.show()}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o);e["default"]=new u["default"]({is:"more-info-alarm_control_panel",handleDisarmTap:function(){this.callService("alarm_disarm",{code:this.enteredCode})},handleHomeTap:function(){this.callService("alarm_arm_home",{code:this.enteredCode})},handleAwayTap:function(){this.callService("alarm_arm_away",{code:this.enteredCode})},properties:{stateObj:{type:Object,observer:"stateObjChanged"},enteredCode:{type:String,value:""},disarmButtonVisible:{type:Boolean,value:!1},armHomeButtonVisible:{type:Boolean,value:!1},armAwayButtonVisible:{type:Boolean,value:!1},codeInputVisible:{type:Boolean,value:!1},codeInputEnabled:{type:Boolean,value:!1},codeFormat:{type:String,value:""},codeValid:{type:Boolean,computed:"validateCode(enteredCode, codeFormat)"}},validateCode:function(t,e){var n=new RegExp(e);return null===e?!0:n.test(t)},stateObjChanged:function(t){var e=this;t&&(this.codeFormat=t.attributes.code_format,this.codeInputVisible=null!==this.codeFormat,this.codeInputEnabled="armed_home"===t.state||"armed_away"===t.state||"disarmed"===t.state||"pending"===t.state||"triggered"===t.state,this.disarmButtonVisible="armed_home"===t.state||"armed_away"===t.state||"pending"===t.state||"triggered"===t.state,this.armHomeButtonVisible="disarmed"===t.state,this.armAwayButtonVisible="disarmed"===t.state),this.async(function(){return e.fire("iron-resize")},500)},callService:function(t,e){var n=e||{};n.entity_id=this.stateObj.entityId,i.serviceActions.callService("alarm_control_panel",t,n)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"more-info-camera",properties:{stateObj:{type:Object},dialogOpen:{type:Boolean}},imageLoaded:function(){this.fire("iron-resize")},computeCameraImageUrl:function(t){return t?"/api/camera_proxy_stream/"+this.stateObj.entityId:"data:image/gif;base64,R0lGODlhAQABAAAAACw="}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(19),e["default"]=new u["default"]({is:"more-info-configurator",behaviors:[s["default"]],properties:{stateObj:{type:Object},action:{type:String,value:"display"},isStreaming:{type:Boolean,bindNuclear:i.streamGetters.isStreamingEvents},isConfigurable:{type:Boolean,computed:"computeIsConfigurable(stateObj)"},isConfiguring:{type:Boolean,value:!1},submitCaption:{type:String,computed:"computeSubmitCaption(stateObj)"},fieldInput:{type:Object,value:{}}},computeIsConfigurable:function(t){return"configure"===t.state},computeSubmitCaption:function(t){return t.attributes.submit_caption||"Set configuration"},fieldChanged:function(t){var e=t.target;this.fieldInput[e.id]=e.value},submitClicked:function(){var t=this;this.isConfiguring=!0;var e={configure_id:this.stateObj.attributes.configure_id,fields:this.fieldInput};i.serviceActions.callService("configurator","configure",e).then(function(){t.isConfiguring=!1,t.isStreaming||i.syncActions.fetchAll()},function(){t.isConfiguring=!1})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(123),a=r(u);n(108),n(109),n(113),n(106),n(114),n(112),n(110),n(111),n(105),n(115),n(104),e["default"]=new o["default"]({is:"more-info-content",properties:{stateObj:{type:Object,observer:"stateObjChanged"},dialogOpen:{type:Boolean,value:!1,observer:"dialogOpenChanged"}},dialogOpenChanged:function(t){var e=o["default"].dom(this);e.lastChild&&(e.lastChild.dialogOpen=t)},stateObjChanged:function(t,e){var n=o["default"].dom(this);if(!t)return void(n.lastChild&&n.removeChild(n.lastChild));var r=a["default"](t);if(e&&a["default"](e)===r)n.lastChild.dialogOpen=this.dialogOpen,n.lastChild.stateObj=t;else{n.lastChild&&n.removeChild(n.lastChild);var i=document.createElement("more-info-"+r);i.stateObj=t,i.dialogOpen=this.dialogOpen,n.appendChild(i)}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=["entity_picture","friendly_name","unit_of_measurement"];e["default"]=new o["default"]({is:"more-info-default",properties:{stateObj:{type:Object}},computeDisplayAttributes:function(t){return t?Object.keys(t.attributes).filter(function(t){return-1===u.indexOf(t)}):[]},getAttributeValue:function(t,e){return t.attributes[e]}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(4),s=r(a);n(20),e["default"]=new u["default"]({is:"more-info-group",behaviors:[s["default"]],properties:{stateObj:{type:Object},states:{type:Array,bindNuclear:[i.moreInfoGetters.currentEntity,i.entityGetters.entityMap,function(t,e){return t?t.attributes.entity_id.map(e.get.bind(e)):[]}]}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a);n(82);var c=["brightness","xy_color"];e["default"]=new u["default"]({is:"more-info-light",properties:{stateObj:{type:Object,observer:"stateObjChanged"},brightnessSliderValue:{type:Number,value:0}},stateObjChanged:function(t){var e=this;t&&"on"===t.state&&(this.brightnessSliderValue=t.attributes.brightness),this.async(function(){return e.fire("iron-resize")},500)},computeClassNames:function(t){return s["default"](t,c)},brightnessSliderChanged:function(t){var e=parseInt(t.target.value,10);isNaN(e)||(0===e?i.serviceActions.callTurnOff(this.stateObj.entityId):i.serviceActions.callService("light","turn_on",{entity_id:this.stateObj.entityId,brightness:e}))},colorPicked:function(t){var e=t.detail.rgb;i.serviceActions.callService("light","turn_on",{entity_id:this.stateObj.entityId,rgb_color:[e.r,e.g,e.b]})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a),c=["volume_level"];e["default"]=new u["default"]({is:"more-info-media_player",properties:{stateObj:{type:Object,observer:"stateObjChanged"},isOff:{type:Boolean,value:!1},isPlaying:{type:Boolean,value:!1},isMuted:{type:Boolean,value:!1},volumeSliderValue:{type:Number,value:0},supportsPause:{type:Boolean,value:!1},supportsVolumeSet:{type:Boolean,value:!1},supportsVolumeMute:{type:Boolean,value:!1},supportsPreviousTrack:{type:Boolean,value:!1},supportsNextTrack:{type:Boolean,value:!1},supportsTurnOn:{type:Boolean,value:!1},supportsTurnOff:{type:Boolean,value:!1}},stateObjChanged:function(t){var e=this;t&&(this.isOff="off"===t.state,this.isPlaying="playing"===t.state,this.volumeSliderValue=100*t.attributes.volume_level,this.isMuted=t.attributes.is_volume_muted,this.supportsPause=0!==(1&t.attributes.supported_media_commands),this.supportsVolumeSet=0!==(4&t.attributes.supported_media_commands),this.supportsVolumeMute=0!==(8&t.attributes.supported_media_commands),this.supportsPreviousTrack=0!==(16&t.attributes.supported_media_commands),this.supportsNextTrack=0!==(32&t.attributes.supported_media_commands),this.supportsTurnOn=0!==(128&t.attributes.supported_media_commands),this.supportsTurnOff=0!==(256&t.attributes.supported_media_commands)),this.async(function(){return e.fire("iron-resize")},500)},computeClassNames:function(t){return s["default"](t,c)},computeIsOff:function(t){return"off"===t.state},computeMuteVolumeIcon:function(t){return t?"av:volume-off":"av:volume-up"},computePlaybackControlIcon:function(){return this.isPlaying?this.supportsPause?"av:pause":"av:stop":"av:play-arrow"},computeHidePowerButton:function(t,e,n){return t?!e:!n},handleTogglePower:function(){this.callService(this.isOff?"turn_on":"turn_off")},handlePrevious:function(){this.callService("media_previous_track")},handlePlaybackControl:function(){this.callService("media_play_pause")},handleNext:function(){this.callService("media_next_track")},handleVolumeTap:function(){this.supportsVolumeMute&&this.callService("volume_mute",{is_volume_muted:!this.isMuted})},volumeSliderChanged:function(t){var e=parseFloat(t.target.value),n=e>0?e/100:0;this.callService("volume_set",{volume_level:n})},callService:function(t,e){var n=e||{};n.entity_id=this.stateObj.entityId,i.serviceActions.callService("media_player",t,n)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);e["default"]=new o["default"]({is:"more-info-script",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2),a=n(41),s=r(a),c=u.util.parseDateTime;e["default"]=new o["default"]({is:"more-info-sun",properties:{stateObj:{type:Object},risingDate:{type:Object,computed:"computeRising(stateObj)"},settingDate:{type:Object,computed:"computeSetting(stateObj)"}},computeRising:function(t){return c(t.attributes.next_rising)},computeSetting:function(t){return c(t.attributes.next_setting)},computeOrder:function(t,e){return t>e?["set","ris"]:["ris","set"]},itemCaption:function(t){return"ris"===t?"Rising ":"Setting "},itemDate:function(t){return"ris"===t?this.risingDate:this.settingDate},itemValue:function(t){return s["default"](this.itemDate(t))}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(1),u=r(o),a=n(21),s=r(a),c=["away_mode"];e["default"]=new u["default"]({is:"more-info-thermostat",properties:{stateObj:{type:Object,observer:"stateObjChanged"},tempMin:{type:Number},tempMax:{type:Number},targetTemperatureSliderValue:{type:Number},awayToggleChecked:{type:Boolean}},stateObjChanged:function(t){this.targetTemperatureSliderValue=t.attributes.temperature,this.awayToggleChecked="on"===t.attributes.away_mode,this.tempMin=t.attributes.min_temp,this.tempMax=t.attributes.max_temp},computeClassNames:function(t){return s["default"](t,c)},targetTemperatureSliderChanged:function(t){var e=parseInt(t.target.value,10);isNaN(e)||i.serviceActions.callService("thermostat","set_temperature",{entity_id:this.stateObj.entityId,temperature:e})},toggleChanged:function(t){var e=t.target.checked;e&&"off"===this.stateObj.attributes.away_mode?this.service_set_away(!0):e||"on"!==this.stateObj.attributes.away_mode||this.service_set_away(!1)},service_set_away:function(t){var e=this;i.serviceActions.callService("thermostat","set_away_mode",{away_mode:t,entity_id:this.stateObj.entityId}).then(function(){return e.stateObjChanged(e.stateObj)})}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);e["default"]=new o["default"]({is:"more-info-updater",properties:{stateObj:{type:Object}},updateTapped:function(){u.serviceActions.callService("updater","update",{})},linkTapped:function(){window.open(this.stateObj.attributes.link,"_blank")}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),n(39),e["default"]=new o["default"]({is:"state-card-configurator",properties:{stateObj:{type:Object}}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9);var u=["playing","paused"];e["default"]=new o["default"]({is:"state-card-media_player",properties:{stateObj:{type:Object},isPlaying:{type:Boolean,computed:"computeIsPlaying(stateObj)"}},computeIsPlaying:function(t){return-1!==u.indexOf(t.state)},computePrimaryText:function(t,e){return e?t.attributes.media_title:t.stateDisplay},computeSecondaryText:function(t){var e=void 0;return"music"===t.attributes.media_content_type?t.attributes.media_artist:"tvshow"===t.attributes.media_content_type?(e=t.attributes.media_series_title,t.attributes.media_season&&t.attributes.media_episode&&(e+=" S"+t.attributes.media_season+"E"+t.attributes.media_episode),e):t.attributes.app_name?t.attributes.app_name:""}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i),u=n(2);n(9),e["default"]=new o["default"]({is:"state-card-scene",properties:{stateObj:{type:Object}},activateScene:function(){u.serviceActions.callTurnOn(this.stateObj.entityId)}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),e["default"]=new o["default"]({is:"state-card-thermostat",properties:{stateObj:{type:Object}},computeTargetTemperature:function(t){return t.attributes.temperature+" "+t.attributes.unit_of_measurement}}),t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(1),o=r(i);n(9),n(35),e["default"]=new o["default"]({is:"state-card-toggle"}),t.exports=e["default"]},function(t,e){"use strict";function n(t){return{attached:function(){var e=this;this.__unwatchFns=Object.keys(this.properties).reduce(function(n,r){if(!("bindNuclear"in e.properties[r]))return n;var i=e.properties[r].bindNuclear;if(!i)throw new Error("Undefined getter specified for key "+r);return e[r]=t.evaluate(i),n.concat(t.observe(i,function(t){e[r]=t}))},[])},detached:function(){for(;this.__unwatchFns.length;)this.__unwatchFns.shift()()}}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){return-1!==a.indexOf(t.domain)?t.domain:u["default"](t.entityId)?"toggle":"display"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=i;var o=n(22),u=r(o),a=["thermostat","configurator","scene","media_player"];t.exports=e["default"]},function(t,e){"use strict";function n(t){return-1!==r.indexOf(t.domain)?t.domain:"default"}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n;var r=["light","group","sun","configurator","thermostat","script","media_player","camera","updater","alarm_control_panel"];t.exports=e["default"]},function(t,e){"use strict";function n(t,e,n){var r=1-t-e,i=n/255,o=i/e*t,u=i/e*r,a=1.612*o-.203*i-.302*u,s=.509*-o+1.412*i+.066*u,c=.026*o-.072*i+.962*u;a=.0031308>=a?12.92*a:1.055*Math.pow(a,1/2.4)-.055,s=.0031308>=s?12.92*s:1.055*Math.pow(s,1/2.4)-.055,c=.0031308>=c?12.92*c:1.055*Math.pow(c,1/2.4)-.055;var l=Math.max(a,s,c);return a/=l,s/=l,c/=l,a=255*a,0>a&&(a=255),s=255*s,0>s&&(s=255),c=255*c,0>c&&(c=255),[a,s,c]}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=n,t.exports=e["default"]},function(t,e,n){var r;(function(t,i,o){"use strict";(function(){function u(t){return"function"==typeof t||"object"==typeof t&&null!==t}function a(t){return"function"==typeof t}function s(t){return"object"==typeof t&&null!==t}function c(t){W=t}function l(t){Z=t}function f(){return function(){t.nextTick(v)}}function d(){return function(){q(v)}}function p(){var t=0,e=new tt(v),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function h(){var t=new MessageChannel;return t.port1.onmessage=v,function(){t.port2.postMessage(0)}}function _(){return function(){setTimeout(v,1)}}function v(){for(var t=0;$>t;t+=2){var e=rt[t],n=rt[t+1];e(n),rt[t]=void 0,rt[t+1]=void 0}$=0}function y(){try{var t=n(196);return q=t.runOnLoop||t.runOnContext,d()}catch(e){return _()}}function m(){}function g(){return new TypeError("You cannot resolve a promise with itself")}function b(){return new TypeError("A promises callback cannot return that same promise.")}function O(t){try{return t.then}catch(e){return at.error=e,at}}function w(t,e,n,r){try{t.call(e,n,r)}catch(i){return i}}function S(t,e,n){Z(function(t){var r=!1,i=w(n,e,function(n){r||(r=!0,e!==n?E(t,n):I(t,n))},function(e){r||(r=!0,P(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&i&&(r=!0,P(t,i))},t)}function M(t,e){e._state===ot?I(t,e._result):e._state===ut?P(t,e._result):D(e,void 0,function(e){E(t,e)},function(e){P(t,e)})}function T(t,e){if(e.constructor===t.constructor)M(t,e);else{var n=O(e);n===at?P(t,at.error):void 0===n?I(t,e):a(n)?S(t,e,n):I(t,e)}}function E(t,e){t===e?P(t,g()):u(e)?T(t,e):I(t,e)}function j(t){t._onerror&&t._onerror(t._result),C(t)}function I(t,e){t._state===it&&(t._result=e,t._state=ot,0!==t._subscribers.length&&Z(C,t))}function P(t,e){t._state===it&&(t._state=ut,t._result=e,Z(j,t))}function D(t,e,n,r){var i=t._subscribers,o=i.length;t._onerror=null,i[o]=e,i[o+ot]=n,i[o+ut]=r,0===o&&t._state&&Z(C,t)}function C(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,i,o=t._result,u=0;uu;u++)D(r.resolve(t[u]),void 0,e,n);return i}function H(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(m);return E(n,t),n}function Y(t){var e=this,n=new e(m);return P(n,t),n}function G(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function U(t){this._id=ht++,this._state=void 0,this._result=void 0,this._subscribers=[],m!==t&&(a(t)||G(),this instanceof U||F(),L(this,t))}function B(){var t;if("undefined"!=typeof i)t=i;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=_t)}var V;V=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var q,W,K,J=V,$=0,Z=({}.toString,function(t,e){rt[$]=t,rt[$+1]=e,$+=2,2===$&&(W?W(v):K())}),X="undefined"!=typeof window?window:void 0,Q=X||{},tt=Q.MutationObserver||Q.WebKitMutationObserver,et="undefined"!=typeof t&&"[object process]"==={}.toString.call(t),nt="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,rt=new Array(1e3);K=et?f():tt?p():nt?h():void 0===X?y():_();var it=void 0,ot=1,ut=2,at=new A,st=new A;N.prototype._validateInput=function(t){return J(t)},N.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},N.prototype._init=function(){this._result=new Array(this.length)};var ct=N;N.prototype._enumerate=function(){for(var t=this,e=t.length,n=t.promise,r=t._input,i=0;n._state===it&&e>i;i++)t._eachEntry(r[i],i)},N.prototype._eachEntry=function(t,e){var n=this,r=n._instanceConstructor;s(t)?t.constructor===r&&t._state!==it?(t._onerror=null,n._settledAt(t._state,e,t._result)):n._willSettleAt(r.resolve(t),e):(n._remaining--,n._result[e]=t)},N.prototype._settledAt=function(t,e,n){var r=this,i=r.promise;i._state===it&&(r._remaining--,t===ut?P(i,n):r._result[e]=n),0===r._remaining&&I(i,r._result)},N.prototype._willSettleAt=function(t,e){var n=this;D(t,void 0,function(t){n._settledAt(ot,e,t)},function(t){n._settledAt(ut,e,t)})};var lt=z,ft=R,dt=H,pt=Y,ht=0,_t=U;U.all=lt,U.race=ft,U.resolve=dt,U.reject=pt,U._setScheduler=c,U._setAsap=l,U._asap=Z,U.prototype={constructor:U,then:function(t,e){var n=this,r=n._state;if(r===ot&&!t||r===ut&&!e)return this;var i=new this.constructor(m),o=n._result;if(r){var u=arguments[r-1];Z(function(){x(r,i,u,o)})}else D(n,i,t,e);return i},"catch":function(t){return this.then(null,t)}};var vt=B,yt={Promise:_t,polyfill:vt};n(195).amd?(r=function(){return yt}.call(e,n,e,o),!(void 0!==r&&(o.exports=r))):"undefined"!=typeof o&&o.exports?o.exports=yt:"undefined"!=typeof this&&(this.ES6Promise=yt),vt()}).call(void 0)}).call(e,n(193),function(){return this}(),n(192)(t))},function(t,e,n){"use strict";var r=n(43),i=r(Date,"now"),o=i||function(){return(new Date).getTime()};t.exports=o},function(t,e){"use strict";function n(t){return"number"==typeof t&&t>-1&&t%1==0&&r>=t}var r=9007199254740991;t.exports=n},function(t,e,n){"use strict";var r=n(43),i=n(127),o=n(44),u="[object Array]",a=Object.prototype,s=a.toString,c=r(Array,"isArray"),l=c||function(t){return o(t)&&i(t.length)&&s.call(t)==u};t.exports=l},function(t,e,n){"use strict";function r(t){return null==t?!1:i(t)?l.test(s.call(t)):o(t)&&u.test(t)}var i=n(45),o=n(44),u=/^\[object .+?Constructor\]$/,a=Object.prototype,s=Function.prototype.toString,c=a.hasOwnProperty,l=RegExp("^"+s.call(c).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(125),i=n(15),o=function(t,e,n){var o=arguments.length<=3||void 0===arguments[3]?null:arguments[3],u=t.evaluate(i.getters.authInfo),a=u.host+"/api/"+n;return new r.Promise(function(t,n){var r=new XMLHttpRequest;r.open(e,a,!0),r.setRequestHeader("X-HA-access",u.authToken),r.onload=function(){if(r.status>199&&r.status<300)t(JSON.parse(r.responseText));else try{n(JSON.parse(r.responseText))}catch(e){n({})}},r.onerror=function(){return n({})},o?r.send(JSON.stringify(o)):r.send()})};e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2],r=n.useStreaming,i=void 0===r?t.evaluate(s.getters.isSupported):r,o=n.rememberAuth,u=void 0===o?!1:o,f=n.host,d=void 0===f?"":f;t.dispatch(a["default"].VALIDATING_AUTH_TOKEN,{authToken:e,host:d}),c.actions.fetchAll(t).then(function(){t.dispatch(a["default"].VALID_AUTH_TOKEN,{authToken:e,host:d,rememberAuth:u}),i?s.actions.start(t,{syncOnInitialConnect:!1}):c.actions.start(t,{skipInitialSync:!0})},function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=e.message,r=void 0===n?l:n;t.dispatch(a["default"].INVALID_AUTH_TOKEN,{errorMessage:r +})})}function o(t){t.dispatch(a["default"].LOG_OUT,{})}Object.defineProperty(e,"__esModule",{value:!0}),e.validate=i,e.logOut=o;var u=n(14),a=r(u),s=n(29),c=n(31),l="Unexpected result from API"},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=[["authAttempt","isValidating"],function(t){return!!t}];e.isValidating=n;var r=[["authAttempt","isInvalid"],function(t){return!!t}];e.isInvalidAttempt=r;var i=["authAttempt","errorMessage"];e.attemptErrorMessage=i;var o=["rememberAuth"];e.rememberAuth=o;var u=[["authAttempt","authToken"],["authAttempt","host"],function(t,e){return{authToken:t,host:e}}];e.attemptAuthInfo=u;var a=["authCurrent","authToken"];e.currentAuthToken=a;var s=[a,["authCurrent","host"],function(t,e){return{authToken:t,host:e}}];e.currentAuthInfo=s;var c=[n,["authAttempt","authToken"],["authCurrent","authToken"],function(t,e,n){return t?e:n}];e.authToken=c;var l=[n,u,s,function(t,e,n){return t?e:n}];e.authInfo=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t){if(null==t)throw new TypeError("Cannot destructure undefined")}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t,e){var n=e.authToken,r=e.host;return d.toImmutable({authToken:n,host:r,isValidating:"true",isInvalid:!1,errorMessage:""})}function s(t,e){return i(e),v.getInitialState()}function c(t,e){var n=e.errorMessage;return t.withMutations(function(t){return t.set("isValidating",!1).set("isInvalid","true").set("errorMessage",n)})}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n1&&t.set(p,r)})}function a(){return _.getInitialState()}Object.defineProperty(e,"__esModule",{value:!0});var s=function(){function t(t,e){for(var n=0;no}Object.defineProperty(e,"__esModule",{value:!0});var i=n(3),o=6e4,u=["currentLogbookDate"];e.currentDate=u;var a=[u,["logbookEntriesUpdated"],function(t,e){return r(e.get(t))}];e.isCurrentStale=a;var s=[u,["logbookEntries"],function(t,e){return e.get(t)||i.toImmutable([])}];e.currentEntries=s;var c=["isLoadingLogbookEntries"];e.isLoadingEntries=c},function(t,e,n){"use strict";function r(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e["default"]=t,e}function i(t){return t&&t.__esModule?t:{"default":t}}function o(t){t.registerStores({currentLogbookDate:a["default"],isLoadingLogbookEntries:c["default"],logbookEntries:f["default"],logbookEntriesUpdated:p["default"]})}Object.defineProperty(e,"__esModule",{value:!0}),e.register=o;var u=n(154),a=i(u),s=n(155),c=i(s),l=n(156),f=i(l),d=n(157),p=i(d),h=n(150),_=r(h),v=n(151),y=r(v),m=_;e.actions=m;var g=y;e.getters=g},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var u=function(){function t(t,e){for(var n=0;n1)for(var n=1;n \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index ac34b5fc269..24623ff26ab 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit ac34b5fc26928238343f34f334a6f6f1a19d5442 +Subproject commit 24623ff26ab8cbf7b39f0a25c26d9d991063b61a From 18747f8ae1ef3958c0c25b0b7b19b7d17e31d798 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Oct 2015 23:12:00 -0700 Subject: [PATCH 36/44] Update some docs --- homeassistant/components/light/hue.py | 2 ++ homeassistant/components/light/hyperion.py | 12 +----------- homeassistant/components/light/limitlessled.py | 17 +---------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/light/hue.py b/homeassistant/components/light/hue.py index b438d7b92b1..eff9aef4f36 100644 --- a/homeassistant/components/light/hue.py +++ b/homeassistant/components/light/hue.py @@ -2,6 +2,8 @@ homeassistant.components.light.hue ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Hue lights. + +https://home-assistant.io/components/light.hue.html """ import logging import socket diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index fe48d58f945..d5fa3f9f2ce 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -3,17 +3,7 @@ homeassistant.components.light.hyperion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Hyperion remotes. -Configuration: - -To connect to [a Hyperion server](https://github.com/tvdzwan/hyperion) you -will need to add something like the following to your configuration.yaml file: - -light: - platform: hyperion - host: 192.168.1.98 - port: 19444 - -The JSON server port is 19444 by default. +https://home-assistant.io/components/light.hyperion.html """ import logging import socket diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index ba8b8235260..b35ed379047 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -12,22 +12,7 @@ Support for LimitlessLED bulbs, also known as... - dekolight - iLight -Configuration: - -To use limitlessled you will need to add the following to your -configuration.yaml file. - -light: - platform: limitlessled - bridges: - - host: 192.168.1.10 - group_1_name: Living Room - group_2_name: Bedroom - group_3_name: Office - group_3_type: white - group_4_name: Kitchen - - host: 192.168.1.11 - group_2_name: Basement +https://home-assistant.io/components/light.limitlessled.html """ import logging From 0826ae2742f08faac8fb879b01ea7369dc782228 Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 08:37:13 +0000 Subject: [PATCH 37/44] Revise pywemo version, update discovery.device_from_description parameters --- homeassistant/components/switch/wemo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index b598af04946..5cc3be0dbe9 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -11,7 +11,7 @@ import logging from homeassistant.components.switch import SwitchDevice from homeassistant.const import STATE_ON, STATE_OFF, STATE_STANDBY -REQUIREMENTS = ['pywemo==0.3.1'] +REQUIREMENTS = ['pywemo==0.3.2'] _LOGGER = logging.getLogger(__name__) @@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): import pywemo.discovery as discovery if discovery_info is not None: - device = discovery.device_from_description(discovery_info[2]) + device = discovery.device_from_description(discovery_info[2], discovery_info[3]) if device: add_devices_callback([WemoSwitch(device)]) From ef6c209c6f28a753b660de80a4a136d16e933d6a Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 09:03:57 +0000 Subject: [PATCH 38/44] Revise for clarity, disable pylink check --- homeassistant/components/switch/wemo.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 5cc3be0dbe9..820c0736c87 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -22,7 +22,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): import pywemo.discovery as discovery if discovery_info is not None: - device = discovery.device_from_description(discovery_info[2], discovery_info[3]) + location = discovery_info[2] + mac = discovery_info[3] + # pylint: disable=too-many-function-args + device = discovery.device_from_description(location, mac) if device: add_devices_callback([WemoSwitch(device)]) From c9f1dce6a2e6b0669f9775fd6973d1b886f2b6b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:32:00 +0100 Subject: [PATCH 39/44] Coding style fixes --- homeassistant/components/device_tracker/ubus.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index a231af02be7..3135993e91b 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -124,8 +124,7 @@ class UbusDeviceScanner(object): if not self.hostapd: hostapd = _req_json_rpc(self.url, self.session_id, 'list', 'hostapd.*', '') - for key in hostapd.keys(): - self.hostapd.append(key) + self.hostapd.extend(hostapd.keys()) self.last_results = [] results = 0 @@ -135,14 +134,9 @@ class UbusDeviceScanner(object): if result: results = results + 1 - for key in result["clients"].keys(): - self.last_results.append(key) - - if results: - return True - else: - return False + self.last_results.extend(result['clients'].keys()) + return bool(results) def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): """ Perform one JSON RPC operation. """ From fbb73dd5da7ade81683fc404c8e66ae78211b4c3 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:50:09 +0100 Subject: [PATCH 40/44] fixed pylint warnings --- homeassistant/components/device_tracker/ubus.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index 3135993e91b..195ed33e77b 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -89,8 +89,8 @@ class UbusDeviceScanner(object): 'call', 'uci', 'get', config="dhcp", type="dnsmasq") if result: - self.leasefile = next(iter(result["values"]. - values()))["leasefile"] + values = result["values"].values() + self.leasefile = next(iter(values))["leasefile"] else: return @@ -101,8 +101,8 @@ class UbusDeviceScanner(object): if result: self.mac2name = dict() for line in result["data"].splitlines(): - [time, mac, ip, name, lid] = line.split(" ") - self.mac2name[mac.upper()] = name + hosts = line.split(" ") + self.mac2name[hosts[1].upper()] = hosts[3] else: # Error, handled in the _req_json_rpc return @@ -138,6 +138,7 @@ class UbusDeviceScanner(object): return bool(results) + def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): """ Perform one JSON RPC operation. """ @@ -147,8 +148,7 @@ def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): "params": [session_id, subsystem, method, - params] - }) + params]}) try: res = requests.post(url, data=data, timeout=5) @@ -159,7 +159,7 @@ def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params): if res.status_code == 200: response = res.json() - if (rpcmethod == "call"): + if rpcmethod == "call": return response["result"][1] else: return response["result"] From 649124d16217a744e53d6e7dfaf82933169fa3e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Koziarek Date: Mon, 26 Oct 2015 11:55:20 +0100 Subject: [PATCH 41/44] Added ubus.py to .coveragerc --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 7e474d287c2..b1ec9681fef 100644 --- a/.coveragerc +++ b/.coveragerc @@ -37,6 +37,7 @@ omit = homeassistant/components/device_tracker/asuswrt.py homeassistant/components/device_tracker/ddwrt.py homeassistant/components/device_tracker/luci.py + homeassistant/components/device_tracker/ubus.py homeassistant/components/device_tracker/netgear.py homeassistant/components/device_tracker/nmap_tracker.py homeassistant/components/device_tracker/thomson.py From dbc05450a0a2cbc57ea4eb90c89340114ec9f3c9 Mon Sep 17 00:00:00 2001 From: pavoni Date: Mon, 26 Oct 2015 16:32:12 +0000 Subject: [PATCH 42/44] Bump requirements_all.txt version, remove pylint disable --- homeassistant/components/switch/wemo.py | 1 - requirements_all.txt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/switch/wemo.py b/homeassistant/components/switch/wemo.py index 820c0736c87..c710a914d80 100644 --- a/homeassistant/components/switch/wemo.py +++ b/homeassistant/components/switch/wemo.py @@ -24,7 +24,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if discovery_info is not None: location = discovery_info[2] mac = discovery_info[3] - # pylint: disable=too-many-function-args device = discovery.device_from_description(location, mac) if device: diff --git a/requirements_all.txt b/requirements_all.txt index 11d91043d12..2ec0fb339bb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -90,7 +90,7 @@ pynetgear==0.3 netdisco==0.5 # Wemo (switch.wemo) -pywemo==0.3.1 +pywemo==0.3.2 # Wink (*.wink) https://github.com/balloob/python-wink/archive/c2b700e8ca866159566ecf5e644d9c297f69f257.zip#python-wink==0.1 From 9ab9d0e383b61c301e2ae59da8dab4e6570ac387 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2015 20:49:20 -0700 Subject: [PATCH 43/44] Update netdisco requirement --- homeassistant/components/discovery.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index b75abc32e4b..f61d792bc60 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -19,7 +19,7 @@ from homeassistant.const import ( DOMAIN = "discovery" DEPENDENCIES = [] -REQUIREMENTS = ['netdisco==0.5'] +REQUIREMENTS = ['netdisco==0.5.1'] SCAN_INTERVAL = 300 # seconds diff --git a/requirements_all.txt b/requirements_all.txt index 2ec0fb339bb..0a26e15ecc6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -87,7 +87,7 @@ https://github.com/theolind/pymysensors/archive/d4b809c2167650691058d1e29bfd2c4b pynetgear==0.3 # Netdisco (discovery) -netdisco==0.5 +netdisco==0.5.1 # Wemo (switch.wemo) pywemo==0.3.2 From 55c0ee6b32fd8e797b3c64518d175e10ee37118d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 26 Oct 2015 21:27:42 -0700 Subject: [PATCH 44/44] Version bump to 0.7.6 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4d5f68e24c5..07eee5c793b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.6.dev0" +__version__ = "0.7.6" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*'