diff --git a/.coveragerc b/.coveragerc index 28b4b926eab..f19e37d00a1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -36,10 +36,12 @@ omit = homeassistant/components/device_tracker/aruba.py homeassistant/components/device_tracker/asuswrt.py homeassistant/components/device_tracker/ddwrt.py + homeassistant/components/device_tracker/geofancy.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/owntracks.py homeassistant/components/device_tracker/thomson.py homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py @@ -48,6 +50,7 @@ omit = homeassistant/components/downloader.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py + homeassistant/components/light/mqtt.py homeassistant/components/light/limitlessled.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/hyperion.py @@ -64,6 +67,7 @@ omit = homeassistant/components/notify/instapush.py homeassistant/components/notify/nma.py homeassistant/components/notify/pushbullet.py + homeassistant/components/notify/pushetta.py homeassistant/components/notify/pushover.py homeassistant/components/notify/slack.py homeassistant/components/notify/smtp.py @@ -80,6 +84,7 @@ omit = homeassistant/components/sensor/glances.py homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/openweathermap.py + homeassistant/components/switch/orvibo.py homeassistant/components/sensor/rest.py homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py @@ -97,6 +102,7 @@ omit = homeassistant/components/switch/rpi_gpio.py homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py + homeassistant/components/thermostat/honeywell.py homeassistant/components/thermostat/nest.py homeassistant/components/thermostat/radiotherm.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 106b914eecb..03e8e88a35f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,19 +17,19 @@ For help on building your component, please see the [developer documentation](ht 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). It's OK to add a docstring with configuration details to the file header. + - Add a link to the website of your device/service/component in the "examples" listing of the `README.md` file. + - Add any new dependencies to `requirements_all.txt` if needed. There is no ordering right now, so just add it to the end of the file. + - Update the `.coveragerc` file to exclude your platform if there are no tests available. + - Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io). - 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/). -If you've added a component: +If you add a platform for an existing component, there is usually no need for updating the frontend. Only if you've added a new component that should show up in the frontend, there are more steps needed: - Update the file [`home-assistant-icons.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/frontend/www_static/polymer/resources/home-assistant-icons.html) with an icon for your domain ([pick one from this list](https://www.polymer-project.org/1.0/components/core-elements/demo.html#core-icon)). - - Update the demo component with two states that it provides - - Add your component to home-assistant.conf.example + - Update the demo component with two states that it provides. + - Add your component to `home-assistant.conf.example`. Since you've updated `home-assistant-icons.html`, you've made changes to the frontend: diff --git a/README.md b/README.md index 2fed012402c..e1528e393bd 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat Examples of devices it can interface it: * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT - * - * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors + * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv)) * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) * Interaction with [IFTTT](https://ifttt.com/) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index daee13914fd..4d68227d4d0 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -9,11 +9,12 @@ After bootstrapping you can add your own components or start by calling homeassistant.start_home_assistant(bus) """ -import os -import sys +from collections import defaultdict import logging import logging.handlers -from collections import defaultdict +import os +import shutil +import sys import homeassistant.core as core import homeassistant.util.dt as date_util @@ -25,7 +26,7 @@ import homeassistant.components as core_components import homeassistant.components.group as group from homeassistant.helpers.entity import Entity from homeassistant.const import ( - EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, + __version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -34,6 +35,7 @@ _LOGGER = logging.getLogger(__name__) ATTR_COMPONENT = 'component' PLATFORM_FORMAT = '{}.{}' +ERROR_LOG_FILENAME = 'home-assistant.log' def setup_component(hass, domain, config=None): @@ -167,6 +169,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True, hass.config.config_dir = config_dir mount_local_lib_path(config_dir) + process_ha_config_upgrade(hass) process_ha_core_config(hass, config.get(core.DOMAIN, {})) if enable_log: @@ -252,7 +255,7 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None): "Colorlog package not found, console coloring disabled") # Log errors to a file if we have write access to file or config dir - err_log_path = hass.config.path('home-assistant.log') + err_log_path = hass.config.path(ERROR_LOG_FILENAME) err_path_exists = os.path.isfile(err_log_path) # Check if we can write to the error log if it exists or that @@ -280,6 +283,31 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None): 'Unable to setup error log %s (access denied)', err_log_path) +def process_ha_config_upgrade(hass): + """ Upgrade config if necessary. """ + version_path = hass.config.path('.HA_VERSION') + + try: + with open(version_path, 'rt') as inp: + conf_version = inp.readline().strip() + except FileNotFoundError: + # Last version to not have this file + conf_version = '0.7.7' + + if conf_version == __version__: + return + + _LOGGER.info('Upgrading config directory from %s to %s', conf_version, + __version__) + + lib_path = hass.config.path('lib') + if os.path.isdir(lib_path): + shutil.rmtree(lib_path) + + with open(version_path, 'wt') as outp: + outp.write(__version__) + + def process_ha_core_config(hass, config): """ Processes the [homeassistant] section from the config. """ hac = hass.config diff --git a/homeassistant/components/__init__.py b/homeassistant/components/__init__.py index e5e917c5250..e0b008cab5e 100644 --- a/homeassistant/components/__init__.py +++ b/homeassistant/components/__init__.py @@ -1,7 +1,6 @@ """ homeassistant.components ~~~~~~~~~~~~~~~~~~~~~~~~ - This package contains components that can be plugged into Home Assistant. Component design guidelines: @@ -12,7 +11,6 @@ Each component that tracks states should create state entity names in the format ".". Each component should publish services only under its own domain. - """ import itertools as it import logging diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/alarm_control_panel/manual.py index 8c98eec50e1..ca1816db9e6 100644 --- a/homeassistant/components/alarm_control_panel/manual.py +++ b/homeassistant/components/alarm_control_panel/manual.py @@ -4,7 +4,7 @@ homeassistant.components.alarm_control_panel.manual Support for manual alarms. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.manual.html +https://home-assistant.io/components/alarm_control_panel.manual/ """ import logging import datetime diff --git a/homeassistant/components/alarm_control_panel/mqtt.py b/homeassistant/components/alarm_control_panel/mqtt.py index e070babd080..168b220db1a 100644 --- a/homeassistant/components/alarm_control_panel/mqtt.py +++ b/homeassistant/components/alarm_control_panel/mqtt.py @@ -4,7 +4,7 @@ homeassistant.components.alarm_control_panel.mqtt This platform enables the possibility to control a MQTT alarm. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/alarm_control_panel.mqtt.html +https://home-assistant.io/components/alarm_control_panel.mqtt/ """ import logging import homeassistant.components.mqtt as mqtt diff --git a/homeassistant/components/alarm_control_panel/verisure.py b/homeassistant/components/alarm_control_panel/verisure.py index 9e0475592bd..e4c498a5044 100644 --- a/homeassistant/components/alarm_control_panel/verisure.py +++ b/homeassistant/components/alarm_control_panel/verisure.py @@ -2,6 +2,9 @@ homeassistant.components.alarm_control_panel.verisure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Interfaces with Verisure alarm control panel. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/verisure/ """ import logging diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index b11525170a4..7ccc1f745e9 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -4,7 +4,7 @@ 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 +https://home-assistant.io/developers/api/ """ import re import logging @@ -14,13 +14,14 @@ import json import homeassistant.core as ha from homeassistant.helpers.state import TrackStates import homeassistant.remote as rem +from homeassistant.bootstrap import ERROR_LOG_FILENAME from homeassistant.const import ( URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM, URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, URL_API_COMPONENTS, - URL_API_CONFIG, URL_API_BOOTSTRAP, + URL_API_CONFIG, URL_API_BOOTSTRAP, URL_API_ERROR_LOG, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL, HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, - HTTP_UNPROCESSABLE_ENTITY) + HTTP_UNPROCESSABLE_ENTITY, CONTENT_TYPE_TEXT_PLAIN) DOMAIN = 'api' @@ -89,6 +90,9 @@ def setup(hass, config): hass.http.register_path( 'GET', URL_API_COMPONENTS, _handle_get_api_components) + hass.http.register_path('GET', URL_API_ERROR_LOG, + _handle_get_api_error_log) + return True @@ -341,6 +345,13 @@ def _handle_get_api_components(handler, path_match, data): handler.write_json(handler.server.hass.config.components) +def _handle_get_api_error_log(handler, path_match, data): + """ Returns the logged errors for this session. """ + error_path = handler.server.hass.config.path(ERROR_LOG_FILENAME) + with open(error_path, 'rb') as error_log: + handler.write_file_pointer(CONTENT_TYPE_TEXT_PLAIN, error_log) + + def _services_json(hass): """ Generate services data to JSONify. """ return [{"domain": key, "services": value} diff --git a/homeassistant/components/arduino.py b/homeassistant/components/arduino.py index 12ceafbd44b..0c278ceee63 100644 --- a/homeassistant/components/arduino.py +++ b/homeassistant/components/arduino.py @@ -5,7 +5,7 @@ Arduino component that connects to a directly attached Arduino board which runs with the Firmata firmware. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/arduino.html +https://home-assistant.io/components/arduino/ """ import logging diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index b734728e59b..d3ef80d7192 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -1,8 +1,10 @@ """ homeassistant.components.automation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Allows to setup simple automation rules via the config file. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/automation/ """ import logging diff --git a/homeassistant/components/automation/event.py b/homeassistant/components/automation/event.py index c172b8e0e11..7fc33df0031 100644 --- a/homeassistant/components/automation/event.py +++ b/homeassistant/components/automation/event.py @@ -4,7 +4,7 @@ homeassistant.components.automation.event Offers event listening automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#event-trigger +at https://home-assistant.io/components/automation/#event-trigger """ import logging diff --git a/homeassistant/components/automation/mqtt.py b/homeassistant/components/automation/mqtt.py index 706d97824b4..8ea5f1bc6e5 100644 --- a/homeassistant/components/automation/mqtt.py +++ b/homeassistant/components/automation/mqtt.py @@ -4,7 +4,7 @@ homeassistant.components.automation.mqtt Offers MQTT listening automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#mqtt-trigger +at https://home-assistant.io/components/automation/#mqtt-trigger """ import logging diff --git a/homeassistant/components/automation/numeric_state.py b/homeassistant/components/automation/numeric_state.py index 1ddfb91a334..ab3529235d6 100644 --- a/homeassistant/components/automation/numeric_state.py +++ b/homeassistant/components/automation/numeric_state.py @@ -1,10 +1,10 @@ """ homeassistant.components.automation.numeric_state -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Offers numeric state listening automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#numeric-state-trigger +at https://home-assistant.io/components/automation/#numeric-state-trigger """ import logging diff --git a/homeassistant/components/automation/state.py b/homeassistant/components/automation/state.py index 52379355d6b..bcf498f509a 100644 --- a/homeassistant/components/automation/state.py +++ b/homeassistant/components/automation/state.py @@ -4,7 +4,7 @@ homeassistant.components.automation.state Offers state listening automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#state-trigger +at https://home-assistant.io/components/automation/#state-trigger """ import logging diff --git a/homeassistant/components/automation/sun.py b/homeassistant/components/automation/sun.py index c72474ae4dd..84334493d0f 100644 --- a/homeassistant/components/automation/sun.py +++ b/homeassistant/components/automation/sun.py @@ -4,7 +4,7 @@ homeassistant.components.automation.sun Offers sun based automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#sun-trigger +at https://home-assistant.io/components/automation/#sun-trigger """ import logging from datetime import timedelta diff --git a/homeassistant/components/automation/time.py b/homeassistant/components/automation/time.py index 2f05c6f4390..7fc2c0d40e2 100644 --- a/homeassistant/components/automation/time.py +++ b/homeassistant/components/automation/time.py @@ -4,7 +4,7 @@ homeassistant.components.automation.time Offers time listening automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#time-trigger +at https://home-assistant.io/components/automation/#time-trigger """ import logging diff --git a/homeassistant/components/automation/zone.py b/homeassistant/components/automation/zone.py index 28d1c8456f0..4bf7eccf41e 100644 --- a/homeassistant/components/automation/zone.py +++ b/homeassistant/components/automation/zone.py @@ -4,7 +4,7 @@ homeassistant.components.automation.zone Offers zone automation rules. For more details about this automation rule, please refer to the documentation -at https://home-assistant.io/components/automation.html#zone-trigger +at https://home-assistant.io/components/automation/#zone-trigger """ import logging diff --git a/homeassistant/components/browser.py b/homeassistant/components/browser.py index c5a55afad40..db0f3710158 100644 --- a/homeassistant/components/browser.py +++ b/homeassistant/components/browser.py @@ -1,8 +1,10 @@ """ homeassistant.components.browser ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to launch a webbrowser on the host machine. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/browser/ """ DOMAIN = "browser" diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index e34d4169fa2..ff5198b7ab1 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -4,24 +4,8 @@ homeassistant.components.camera ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Component to interface with various cameras. -The following features are supported: - - Returning recorded camera images and streams - - Proxying image requests via HA for external access - - Converting a still image url into a live video stream - -Upcoming features - - Recording - - Snapshot - - Motion Detection Recording(for supported cameras) - - Automatic Configuration(for supported cameras) - - Creation of child entities for supported functions - - Collating motion event images passed via FTP into time based events - - A service for calling camera functions - - Camera movement(panning) - - Zoom - - Light/Nightvision toggling - - Support for more devices - - Expanded documentation +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/camera/ """ import requests import logging @@ -103,7 +87,10 @@ def setup(hass, config): if camera: response = camera.camera_image() - handler.wfile.write(response) + if response is not None: + handler.wfile.write(response) + else: + handler.send_response(HTTP_NOT_FOUND) else: handler.send_response(HTTP_NOT_FOUND) @@ -145,7 +132,8 @@ def setup(hass, config): while True: img_bytes = camera.camera_image() - + if img_bytes is None: + continue headers_str = '\r\n'.join(( 'Content-length: {}'.format(len(img_bytes)), 'Content-type: image/jpeg', diff --git a/homeassistant/components/camera/foscam.py b/homeassistant/components/camera/foscam.py index 24a42cbf883..d4d707c790f 100644 --- a/homeassistant/components/camera/foscam.py +++ b/homeassistant/components/camera/foscam.py @@ -4,14 +4,13 @@ homeassistant.components.camera.foscam This component provides basic support for Foscam IP cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.foscam.html +https://home-assistant.io/components/camera.foscam/ """ import logging from homeassistant.helpers import validate_config from homeassistant.components.camera import DOMAIN from homeassistant.components.camera import Camera import requests -import re _LOGGER = logging.getLogger(__name__) @@ -40,7 +39,7 @@ class FoscamCamera(Camera): self._username = device_info.get('username') self._password = device_info.get('password') self._snap_picture_url = self._base_url \ - + 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture&usr=' \ + + 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=' \ + self._username + '&pwd=' + self._password self._name = device_info.get('name', 'Foscam Camera') @@ -50,17 +49,9 @@ class FoscamCamera(Camera): def camera_image(self): """ Return a still image reponse from the camera. """ - # send the request to snap a picture + # Send the request to snap a picture and return raw jpg data response = requests.get(self._snap_picture_url) - # parse the response to find the image file name - - pattern = re.compile('src="[.][.]/(.*[.]jpg)"') - filename = pattern.search(response.content.decode("utf-8")).group(1) - - # send request for the image - response = requests.get(self._base_url + filename) - return response.content @property diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index 74d2d0102d3..b8be51292bf 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -4,7 +4,7 @@ homeassistant.components.camera.generic Support for IP Cameras. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/camera.generic.html +https://home-assistant.io/components/camera.generic/ """ import logging from requests.auth import HTTPBasicAuth @@ -42,11 +42,19 @@ class GenericCamera(Camera): def camera_image(self): """ Return a still image reponse from the camera. """ if self._username and self._password: - response = requests.get( - self._still_image_url, - auth=HTTPBasicAuth(self._username, self._password)) + try: + response = requests.get( + self._still_image_url, + auth=HTTPBasicAuth(self._username, self._password)) + except requests.exceptions.RequestException as error: + _LOGGER.error('Error getting camera image: %s', error) + return None else: - response = requests.get(self._still_image_url) + try: + response = requests.get(self._still_image_url) + except requests.exceptions.RequestException as error: + _LOGGER.error('Error getting camera image: %s', error) + return None return response.content diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py new file mode 100644 index 00000000000..1e643304add --- /dev/null +++ b/homeassistant/components/camera/mjpeg.py @@ -0,0 +1,71 @@ +""" +homeassistant.components.camera.mjpeg +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for IP Cameras. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/camera.mjpeg/ +""" +import logging +from requests.auth import HTTPBasicAuth +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN +from homeassistant.components.camera import Camera +import requests +from contextlib import closing + +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Adds a mjpeg IP Camera. """ + if not validate_config({DOMAIN: config}, {DOMAIN: ['mjpeg_url']}, + _LOGGER): + return None + + add_devices_callback([MjpegCamera(config)]) + + +# pylint: disable=too-many-instance-attributes +class MjpegCamera(Camera): + """ + A generic implementation of an IP camera that is reachable over a URL. + """ + + def __init__(self, device_info): + super().__init__() + self._name = device_info.get('name', 'Mjpeg Camera') + self._username = device_info.get('username') + self._password = device_info.get('password') + self._mjpeg_url = device_info['mjpeg_url'] + + def camera_image(self): + """ Return a still image response from the camera. """ + + def process_response(response): + """ Take in a response object, return the jpg from it. """ + data = b'' + for chunk in response.iter_content(1024): + data += chunk + jpg_start = data.find(b'\xff\xd8') + jpg_end = data.find(b'\xff\xd9') + if jpg_start != -1 and jpg_end != -1: + jpg = data[jpg_start:jpg_end + 2] + return jpg + + if self._username and self._password: + with closing(requests.get(self._mjpeg_url, + auth=HTTPBasicAuth(self._username, + self._password), + stream=True)) as response: + return process_response(response) + else: + with closing(requests.get(self._mjpeg_url, + stream=True)) as response: + return process_response(response) + + @property + def name(self): + """ Return the name of this device. """ + return self._name diff --git a/homeassistant/components/conversation.py b/homeassistant/components/conversation.py index f00a640232f..d9cba832df7 100644 --- a/homeassistant/components/conversation.py +++ b/homeassistant/components/conversation.py @@ -4,7 +4,7 @@ homeassistant.components.conversation Provides functionality to have conversations with Home Assistant. For more details about this component, please refer to the documentation at -https://home-assistant.io/components/conversation.html +https://home-assistant.io/components/conversation/ """ import logging import re diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 388a869ae0c..7c873e834bd 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -10,14 +10,15 @@ import homeassistant.core as ha import homeassistant.bootstrap as bootstrap import homeassistant.loader as loader from homeassistant.const import ( - CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME) + CONF_PLATFORM, ATTR_ENTITY_ID) DOMAIN = "demo" -DEPENDENCIES = ['introduction', 'conversation'] +DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ - 'switch', 'light', 'sensor', 'thermostat', 'media_player', 'notify'] + 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', + 'thermostat'] def setup(hass, config): @@ -110,25 +111,6 @@ def setup(hass, config): }}, ]}) - # Setup fake device tracker - hass.states.set("device_tracker.paulus", "home", - {ATTR_ENTITY_PICTURE: - "http://graph.facebook.com/297400035/picture", - ATTR_FRIENDLY_NAME: 'Paulus'}) - hass.states.set("device_tracker.anne_therese", "not_home", - {ATTR_FRIENDLY_NAME: 'Anne Therese', - 'latitude': hass.config.latitude + 0.002, - 'longitude': hass.config.longitude + 0.002}) - - hass.states.set("group.all_devices", "home", - { - "auto": True, - ATTR_ENTITY_ID: [ - "device_tracker.paulus", - "device_tracker.anne_therese" - ] - }) - # Setup configurator configurator_ids = [] diff --git a/homeassistant/components/device_sun_light_trigger.py b/homeassistant/components/device_sun_light_trigger.py index 67da9e26a82..4acf60bc0a2 100644 --- a/homeassistant/components/device_sun_light_trigger.py +++ b/homeassistant/components/device_sun_light_trigger.py @@ -1,9 +1,11 @@ """ homeassistant.components.device_sun_light_trigger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Provides functionality to turn on lights based on the state of the sun and +devices. -Provides functionality to turn on lights based on -the state of the sun and devices. +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/device_sun_light_trigger/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 9fe18585418..204d845084c 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -1,25 +1,10 @@ """ homeassistant.components.device_tracker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Provides functionality to keep track of devices. -device_tracker: - platform: netgear - - # Optional - - # How many seconds to wait after not seeing device to consider it not home - consider_home: 180 - - # Seconds between each scan - interval_seconds: 12 - - # New found devices auto found - track_new_devices: yes - - # Maximum distance from home we consider people home - range_home: 100 +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/device_tracker/ """ # pylint: disable=too-many-instance-attributes, too-many-arguments # pylint: disable=too-many-locals diff --git a/homeassistant/components/device_tracker/actiontec.py b/homeassistant/components/device_tracker/actiontec.py index a2f94f34c3a..f363acf1902 100644 --- a/homeassistant/components/device_tracker/actiontec.py +++ b/homeassistant/components/device_tracker/actiontec.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning an Actiontec MI424WR (Verizon FIOS) router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.actiontec.html +https://home-assistant.io/components/device_tracker.actiontec/ """ import logging from datetime import timedelta @@ -17,20 +17,19 @@ import telnetlib import homeassistant.util.dt as dt_util from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD from homeassistant.helpers import validate_config -from homeassistant.util import Throttle, convert +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) -# Interval in minutes to exclude devices from a scan while they are home -CONF_HOME_INTERVAL = "home_interval" - _LOGGER = logging.getLogger(__name__) _LEASES_REGEX = re.compile( r'(?P([0-9]{1,3}[\.]){3}[0-9]{1,3})' + - r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))') + r'\smac:\s(?P([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))' + + r'\svalid\sfor:\s(?P(-?\d+))' + + r'\ssec') # pylint: disable=unused-argument @@ -40,9 +39,7 @@ def get_scanner(hass, config): {DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]}, _LOGGER): return None - scanner = ActiontecDeviceScanner(config[DOMAIN]) - return scanner if scanner.success_init else None Device = namedtuple("Device", ["mac", "ip", "last_update"]) @@ -58,19 +55,11 @@ class ActiontecDeviceScanner(object): self.host = config[CONF_HOST] self.username = config[CONF_USERNAME] self.password = config[CONF_PASSWORD] - minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) - self.home_interval = timedelta(minutes=minutes) - self.lock = threading.Lock() - self.last_results = [] - - # Test the router is accessible data = self.get_actiontec_data() self.success_init = data is not None _LOGGER.info("actiontec scanner initialized") - if self.home_interval: - _LOGGER.info("home_interval set to: %s", self.home_interval) def scan_devices(self): """ @@ -100,27 +89,13 @@ class ActiontecDeviceScanner(object): return False with self.lock: - exclude_targets = set() - exclude_target_list = [] now = dt_util.now() - if self.home_interval: - for host in self.last_results: - if host.last_update + self.home_interval > now: - exclude_targets.add(host) - if len(exclude_targets) > 0: - exclude_target_list = [t.ip for t in exclude_targets] - actiontec_data = self.get_actiontec_data() if not actiontec_data: return False - self.last_results = [] - for client in exclude_target_list: - if client in actiontec_data: - actiontec_data.pop(client) - for name, data in actiontec_data.items(): - device = Device(data['mac'], name, now) - self.last_results.append(device) - self.last_results.extend(exclude_targets) + self.last_results = [Device(data['mac'], name, now) + for name, data in actiontec_data.items() + if data['timevalid'] > -60] _LOGGER.info("actiontec scan successful") return True @@ -153,6 +128,7 @@ class ActiontecDeviceScanner(object): if match is not None: devices[match.group('ip')] = { 'ip': match.group('ip'), - 'mac': match.group('mac').upper() + 'mac': match.group('mac').upper(), + 'timevalid': int(match.group('timevalid')) } return devices diff --git a/homeassistant/components/device_tracker/aruba.py b/homeassistant/components/device_tracker/aruba.py index d46264fa264..82183e1495c 100644 --- a/homeassistant/components/device_tracker/aruba.py +++ b/homeassistant/components/device_tracker/aruba.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a Aruba Access Point for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.aruba.html +https://home-assistant.io/components/device_tracker.aruba/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 5284d45835b..b90e1ee4448 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -4,32 +4,8 @@ homeassistant.components.device_tracker.asuswrt Device tracker platform that supports scanning a ASUSWRT router for device presence. -This device tracker needs telnet to be enabled on the router. - -Configuration: - -To use the ASUSWRT tracker you will need to add something like the following -to your configuration.yaml file. - -device_tracker: - platform: asuswrt - host: YOUR_ROUTER_IP - username: YOUR_ADMIN_USERNAME - password: YOUR_ADMIN_PASSWORD - -Variables: - -host -*Required -The IP address of your router, e.g. 192.168.1.1. - -username -*Required -The username of an user with administrative privileges, usually 'admin'. - -password -*Required -The password for your given admin account. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.asuswrt/ """ import logging from datetime import timedelta @@ -158,13 +134,16 @@ class AsusWrtDeviceScanner(object): for lease in leases_result: match = _LEASES_REGEX.search(lease.decode('utf-8')) + if not match: + _LOGGER.warning("Could not parse lease row: %s", lease) + continue + # For leases where the client doesn't set a hostname, ensure # it is blank and not '*', which breaks the entity_id down # the line - if match: - host = match.group('host') - if host == '*': - host = '' + host = match.group('host') + if host == '*': + host = '' devices[match.group('ip')] = { 'host': host, @@ -175,6 +154,9 @@ class AsusWrtDeviceScanner(object): for neighbor in neighbors: match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8')) - if match and match.group('ip') in devices: + if not match: + _LOGGER.warning("Could not parse neighbor row: %s", neighbor) + continue + if match.group('ip') in devices: devices[match.group('ip')]['status'] = match.group('status') return devices diff --git a/homeassistant/components/device_tracker/ddwrt.py b/homeassistant/components/device_tracker/ddwrt.py index d8734a55a17..268c4e5a22f 100644 --- a/homeassistant/components/device_tracker/ddwrt.py +++ b/homeassistant/components/device_tracker/ddwrt.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a DD-WRT router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.ddwrt.html +https://home-assistant.io/components/device_tracker.ddwrt/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/demo.py b/homeassistant/components/device_tracker/demo.py index e8cf906be9e..43b7915ee3c 100644 --- a/homeassistant/components/device_tracker/demo.py +++ b/homeassistant/components/device_tracker/demo.py @@ -1,7 +1,6 @@ """ homeassistant.components.device_tracker.demo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Demo platform for the device tracker. device_tracker: diff --git a/homeassistant/components/device_tracker/geofancy.py b/homeassistant/components/device_tracker/geofancy.py index 91d3978326b..a5e6edee71a 100644 --- a/homeassistant/components/device_tracker/geofancy.py +++ b/homeassistant/components/device_tracker/geofancy.py @@ -4,9 +4,8 @@ homeassistant.components.device_tracker.geofancy Geofancy platform for the device tracker. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.geofancy.html +https://home-assistant.io/components/device_tracker.geofancy/ """ - from homeassistant.const import ( HTTP_UNPROCESSABLE_ENTITY, HTTP_INTERNAL_SERVER_ERROR) diff --git a/homeassistant/components/device_tracker/luci.py b/homeassistant/components/device_tracker/luci.py index 2ce032f90fd..8b3e4eeb3c8 100644 --- a/homeassistant/components/device_tracker/luci.py +++ b/homeassistant/components/device_tracker/luci.py @@ -5,7 +5,7 @@ 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.luci.html +https://home-assistant.io/components/device_tracker.luci/ """ import logging import json diff --git a/homeassistant/components/device_tracker/mqtt.py b/homeassistant/components/device_tracker/mqtt.py index f78cb3420f5..929deaae669 100644 --- a/homeassistant/components/device_tracker/mqtt.py +++ b/homeassistant/components/device_tracker/mqtt.py @@ -4,7 +4,7 @@ homeassistant.components.device_tracker.mqtt MQTT platform for the device tracker. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.mqtt.html +https://home-assistant.io/components/device_tracker.mqtt/ """ import logging from homeassistant import util diff --git a/homeassistant/components/device_tracker/netgear.py b/homeassistant/components/device_tracker/netgear.py index 2d138cf5c70..5d20e98e992 100644 --- a/homeassistant/components/device_tracker/netgear.py +++ b/homeassistant/components/device_tracker/netgear.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a Netgear router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.netgear.html +https://home-assistant.io/components/device_tracker.netgear/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/device_tracker/nmap_tracker.py index fe6b814b96f..1e45444c74e 100644 --- a/homeassistant/components/device_tracker/nmap_tracker.py +++ b/homeassistant/components/device_tracker/nmap_tracker.py @@ -4,7 +4,7 @@ homeassistant.components.device_tracker.nmap Device tracker platform that supports scanning a network with nmap. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.nmap_scanner.html +https://home-assistant.io/components/device_tracker.nmap_scanner/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index 78fd42f1566..b98c3a1636c 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -4,7 +4,7 @@ homeassistant.components.device_tracker.owntracks OwnTracks platform for the device tracker. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.owntracks.html +https://home-assistant.io/components/device_tracker.owntracks/ """ import json import logging diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 21bcdfb2a93..868f701673a 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -5,7 +5,7 @@ Device tracker platform that supports fetching WiFi associations through SNMP. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.snmp.html +https://home-assistant.io/components/device_tracker.snmp/ """ import logging from datetime import timedelta @@ -45,9 +45,12 @@ class SnmpScanner(object): This class queries any SNMP capable Acces Point for connected devices. """ def __init__(self, config): - self.host = config[CONF_HOST] - self.community = config[CONF_COMMUNITY] - self.baseoid = config[CONF_BASEOID] + from pysnmp.entity.rfc3413.oneliner import cmdgen + self.snmp = cmdgen.CommandGenerator() + + self.host = cmdgen.UdpTransportTarget((config[CONF_HOST], 161)) + self.community = cmdgen.CommunityData(config[CONF_COMMUNITY]) + self.baseoid = cmdgen.MibVariable(config[CONF_BASEOID]) self.lock = threading.Lock() @@ -91,16 +94,11 @@ class SnmpScanner(object): def get_snmp_data(self): """ Fetch mac addresses from WAP via SNMP. """ - from pysnmp.entity.rfc3413.oneliner import cmdgen devices = [] - snmp = cmdgen.CommandGenerator() - errindication, errstatus, errindex, restable = snmp.nextCmd( - cmdgen.CommunityData(self.community), - cmdgen.UdpTransportTarget((self.host, 161)), - cmdgen.MibVariable(self.baseoid) - ) + errindication, errstatus, errindex, restable = self.snmp.nextCmd( + self.community, self.host, self.baseoid) if errindication: _LOGGER.error("SNMPLIB error: %s", errindication) diff --git a/homeassistant/components/device_tracker/thomson.py b/homeassistant/components/device_tracker/thomson.py index c6679e6c320..657bb910da2 100644 --- a/homeassistant/components/device_tracker/thomson.py +++ b/homeassistant/components/device_tracker/thomson.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a THOMSON router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.thomson.html +https://home-assistant.io/components/device_tracker.thomson/ """ import logging from datetime import timedelta diff --git a/homeassistant/components/device_tracker/tomato.py b/homeassistant/components/device_tracker/tomato.py index df0c9c8d93d..c87a50f0981 100644 --- a/homeassistant/components/device_tracker/tomato.py +++ b/homeassistant/components/device_tracker/tomato.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a Tomato router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.tomato.html +https://home-assistant.io/components/device_tracker.tomato/ """ import logging import json diff --git a/homeassistant/components/device_tracker/tplink.py b/homeassistant/components/device_tracker/tplink.py index 3769229f101..46556b3eca4 100755 --- a/homeassistant/components/device_tracker/tplink.py +++ b/homeassistant/components/device_tracker/tplink.py @@ -5,7 +5,7 @@ Device tracker platform that supports scanning a TP-Link router for device presence. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/device_tracker.tplink.html +https://home-assistant.io/components/device_tracker.tplink/ """ import base64 import logging diff --git a/homeassistant/components/device_tracker/ubus.py b/homeassistant/components/device_tracker/ubus.py index 195ed33e77b..0355680a31d 100644 --- a/homeassistant/components/device_tracker/ubus.py +++ b/homeassistant/components/device_tracker/ubus.py @@ -5,7 +5,7 @@ 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 +https://home-assistant.io/components/device_tracker.ubus/ """ import logging import json diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index f61d792bc60..3a43c86f58a 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -1,7 +1,6 @@ """ homeassistant.components.discovery ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Starts a service to scan in intervals for new devices. Will emit EVENT_PLATFORM_DISCOVERED whenever a new service has been discovered. diff --git a/homeassistant/components/downloader.py b/homeassistant/components/downloader.py index f145fadfb71..a69a6ca1517 100644 --- a/homeassistant/components/downloader.py +++ b/homeassistant/components/downloader.py @@ -4,7 +4,7 @@ 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 +https://home-assistant.io/components/downloader/ """ import os import logging diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index b327e510cd8..5a8fbed34e9 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -8,7 +8,7 @@ import re import os import logging -from . import version +from . import version, mdi_version import homeassistant.util as util from homeassistant.const import URL_ROOT, HTTP_OK from homeassistant.config import get_default_config_dir @@ -22,9 +22,11 @@ _LOGGER = logging.getLogger(__name__) FRONTEND_URLS = [ URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState', - '/devEvent'] + '/devEvent', '/devInfo'] STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)') +_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE) + def setup(hass, config): """ Setup serving the frontend. """ @@ -72,6 +74,7 @@ def _handle_get_root(handler, path_match, data): template_html = template_html.replace('{{ app_url }}', app_url) template_html = template_html.replace('{{ auth }}', auth) + template_html = template_html.replace('{{ icons }}', mdi_version.VERSION) handler.wfile.write(template_html.encode("UTF-8")) @@ -80,9 +83,10 @@ def _handle_get_static(handler, path_match, data): """ Returns a static file for the frontend. """ req_file = util.sanitize_path(path_match.group('file')) - # Strip md5 hash out of frontend filename - if re.match(r'^frontend-[A-Za-z0-9]{32}\.html$', req_file): - req_file = "frontend.html" + # Strip md5 hash out + fingerprinted = _FINGERPRINT.match(req_file) + if fingerprinted: + req_file = "{}.{}".format(*fingerprinted.groups()) path = os.path.join(os.path.dirname(__file__), 'www_static', req_file) diff --git a/homeassistant/components/frontend/index.html.template b/homeassistant/components/frontend/index.html.template index 8906e8902a0..409ea6752db 100644 --- a/homeassistant/components/frontend/index.html.template +++ b/homeassistant/components/frontend/index.html.template @@ -46,6 +46,6 @@ - + diff --git a/homeassistant/components/frontend/mdi_version.py b/homeassistant/components/frontend/mdi_version.py new file mode 100644 index 00000000000..c9d06a4b300 --- /dev/null +++ b/homeassistant/components/frontend/mdi_version.py @@ -0,0 +1,2 @@ +""" DO NOT MODIFY. Auto-generated by update_mdi script """ +VERSION = "38EF63D0474411E4B3CF842B2B6CFE1B" diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1c753d1638e..1435fe16c5c 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 = "beb922c55bb26ea576581b453f6d7c04" +VERSION = "b75e3c9ebd3de2dae0912a89499127a9" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 7343bd3afd0..5157f4dd5a6 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -