diff --git a/.coveragerc b/.coveragerc index 2f6e7a9adf8..69755cb8a62 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,18 +5,18 @@ omit = homeassistant/__main__.py # omit pieces of code that rely on external devices being present - homeassistant/components/alarm_control_panel/alarmdotcom.py - homeassistant/components/alarm_control_panel/nx584.py + homeassistant/components/apcupsd.py + homeassistant/components/*/apcupsd.py homeassistant/components/arduino.py homeassistant/components/*/arduino.py - homeassistant/components/apcupsd.py - homeassistant/components/*/apcupsd.py - homeassistant/components/bloomsky.py homeassistant/components/*/bloomsky.py + homeassistant/components/ecobee.py + homeassistant/components/*/ecobee.py + homeassistant/components/insteon_hub.py homeassistant/components/*/insteon_hub.py @@ -26,33 +26,6 @@ omit = homeassistant/components/modbus.py homeassistant/components/*/modbus.py - homeassistant/components/tellstick.py - homeassistant/components/*/tellstick.py - - homeassistant/components/tellduslive.py - homeassistant/components/*/tellduslive.py - - homeassistant/components/vera.py - homeassistant/components/*/vera.py - - homeassistant/components/ecobee.py - homeassistant/components/*/ecobee.py - - homeassistant/components/verisure.py - homeassistant/components/*/verisure.py - - homeassistant/components/wemo.py - homeassistant/components/*/wemo.py - - homeassistant/components/wink.py - homeassistant/components/*/wink.py - - homeassistant/components/zigbee.py - homeassistant/components/*/zigbee.py - - homeassistant/components/zwave.py - homeassistant/components/*/zwave.py - homeassistant/components/mysensors.py homeassistant/components/*/mysensors.py @@ -65,6 +38,36 @@ omit = homeassistant/components/scsgate.py homeassistant/components/*/scsgate.py + homeassistant/components/tellduslive.py + homeassistant/components/*/tellduslive.py + + homeassistant/components/tellstick.py + homeassistant/components/*/tellstick.py + + homeassistant/components/*/thinkingcleaner.py + + homeassistant/components/vera.py + homeassistant/components/*/vera.py + + homeassistant/components/verisure.py + homeassistant/components/*/verisure.py + + homeassistant/components/*/webostv.py + + homeassistant/components/wemo.py + homeassistant/components/*/wemo.py + + homeassistant/components/wink.py + homeassistant/components/*/wink.py + + homeassistant/components/zigbee.py + homeassistant/components/*/zigbee.py + + homeassistant/components/zwave.py + homeassistant/components/*/zwave.py + + homeassistant/components/alarm_control_panel/alarmdotcom.py + homeassistant/components/alarm_control_panel/nx584.py homeassistant/components/binary_sensor/arest.py homeassistant/components/binary_sensor/rest.py homeassistant/components/browser.py @@ -76,6 +79,7 @@ omit = homeassistant/components/device_tracker/actiontec.py homeassistant/components/device_tracker/aruba.py homeassistant/components/device_tracker/asuswrt.py + homeassistant/components/device_tracker/bluetooth_tracker.py homeassistant/components/device_tracker/ddwrt.py homeassistant/components/device_tracker/fritz.py homeassistant/components/device_tracker/icloud.py @@ -89,6 +93,7 @@ omit = homeassistant/components/device_tracker/ubus.py homeassistant/components/discovery.py homeassistant/components/downloader.py + homeassistant/components/feedreader.py homeassistant/components/garage_door/wink.py homeassistant/components/ifttt.py homeassistant/components/keyboard.py @@ -103,17 +108,17 @@ omit = homeassistant/components/media_player/itunes.py homeassistant/components/media_player/kodi.py homeassistant/components/media_player/mpd.py + homeassistant/components/media_player/onkyo.py + homeassistant/components/media_player/panasonic_viera.py homeassistant/components/media_player/plex.py homeassistant/components/media_player/samsungtv.py homeassistant/components/media_player/snapcast.py homeassistant/components/media_player/sonos.py homeassistant/components/media_player/squeezebox.py - homeassistant/components/media_player/onkyo.py - homeassistant/components/media_player/panasonic_viera.py homeassistant/components/media_player/yamaha.py homeassistant/components/notify/free_mobile.py - homeassistant/components/notify/googlevoice.py homeassistant/components/notify/gntp.py + homeassistant/components/notify/googlevoice.py homeassistant/components/notify/instapush.py homeassistant/components/notify/message_bird.py homeassistant/components/notify/nma.py @@ -140,10 +145,10 @@ omit = homeassistant/components/sensor/forecast.py homeassistant/components/sensor/glances.py homeassistant/components/sensor/gtfs.py - homeassistant/components/sensor/netatmo.py - homeassistant/components/sensor/nzbget.py homeassistant/components/sensor/loopenergy.py + homeassistant/components/sensor/netatmo.py homeassistant/components/sensor/neurio_energy.py + homeassistant/components/sensor/nzbget.py homeassistant/components/sensor/onewire.py homeassistant/components/sensor/openweathermap.py homeassistant/components/sensor/rest.py @@ -169,10 +174,13 @@ omit = homeassistant/components/switch/rest.py homeassistant/components/switch/transmission.py homeassistant/components/switch/wake_on_lan.py + homeassistant/components/thermostat/eq3btsmart.py homeassistant/components/thermostat/heatmiser.py homeassistant/components/thermostat/homematic.py homeassistant/components/thermostat/proliphix.py homeassistant/components/thermostat/radiotherm.py + homeassistant/components/upnp.py + homeassistant/components/zeroconf.py [report] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3d87814ac8b..bb40754c97a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -23,6 +23,6 @@ If the code does not interact with devices: [fork]: http://stackoverflow.com/a/7244456 [squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit -[ex-requir]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16 -[ex-import]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51 +[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16 +[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51 diff --git a/.gitignore b/.gitignore index e7d64517e17..5a3fa9649c0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ config/custom_components/* !config/custom_components/hello_world.py !config/custom_components/mqtt_example.py +tests/config/deps tests/config/home-assistant.log # Hide sublime text stuff diff --git a/.gitmodules b/.gitmodules index ad28a4e2c8a..49d8dace9a4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "homeassistant/components/frontend/www_static/home-assistant-polymer"] path = homeassistant/components/frontend/www_static/home-assistant-polymer - url = https://github.com/balloob/home-assistant-polymer.git + url = https://github.com/home-assistant/home-assistant-polymer.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2028161458e..221b46c65ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,10 @@ Everybody is invited and welcome to contribute to Home Assistant. There is a lot The process is straight-forward. - - Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant). + - Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant). - Write the code for your device, notification service, sensor, or IoT thing. - Ensure tests work. - - Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant. + - Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant. Still interested? Then you should read the next sections and get more details. @@ -20,20 +20,20 @@ After you finish adding support for your device: - Check that all dependencies are included via the `REQUIREMENTS` variable in your platform/component and only imported inside functions that use them. - Add any new dependencies to `requirements_all.txt` if needed. Use `script/gen_requirements_all.py`. - Update the `.coveragerc` file to exclude your platform if there are no tests available or your new code uses a 3rd party library for communication with the device/service/sensor. - - 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). + - 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/home-assistant/home-assistant.io). - Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `tox` or `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 [CI output](https://travis-ci.org/balloob/home-assistant/). + - Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant. + - Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/home-assistant/home-assistant/). 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 file [`home-assistant-icons.html`](https://github.com/home-assistant/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`. Since you've updated `home-assistant-icons.html`, you've made changes to the frontend: - - Run `build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit. + - Run `script/build_frontend`. This will build a new version of the frontend. Make sure you add the changed files `frontend.py` and `frontend.html` to the commit. ### Setting states @@ -46,11 +46,11 @@ A state can have several attributes that will help the frontend in displaying yo - `unit_of_measurement`: this will be appended to the state in the interface - `hidden`: This is a suggestion to the frontend on if the state should be hidden -These attributes are defined in [homeassistant.components](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/__init__.py#L25). +These attributes are defined in [homeassistant.components](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/__init__.py#L25). ### Proper Visibility Handling -Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/balloob/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you. +Generally, when creating a new entity for Home Assistant you will want it to be a class that inherits the [homeassistant.helpers.entity.Entity](https://github.com/home-assistant/home-assistant/blob/master/homeassistant/helpers/entity.py) class. If this is done, visibility will be handled for you. You can set a suggestion for your entity's visibility by setting the hidden property by doing something similar to the following. ```python diff --git a/Dockerfile b/Dockerfile index 8970df523be..013afcca674 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,9 +8,9 @@ WORKDIR /usr/src/app RUN pip3 install --no-cache-dir colorlog cython -# For the nmap tracker +# For the nmap tracker, bluetooth tracker, Z-Wave RUN apt-get update && \ - apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo && \ + apt-get install -y --no-install-recommends nmap net-tools cython3 libudev-dev sudo libglib2.0-dev && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY script/build_python_openzwave script/build_python_openzwave diff --git a/README.rst b/README.rst index c66b3670f32..69856586be0 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/balloob/home-assistant| +Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/home-assistant/home-assistant| =========================================================================================================== Home Assistant is a home automation platform running on Python 3. The @@ -88,11 +88,11 @@ If you run into issues while using Home Assistant or during development of a component, check the `Home Assistant help section `__ how to reach us. -.. |Build Status| image:: https://travis-ci.org/balloob/home-assistant.svg?branch=master - :target: https://travis-ci.org/balloob/home-assistant -.. |Coverage Status| image:: https://img.shields.io/coveralls/balloob/home-assistant.svg - :target: https://coveralls.io/r/balloob/home-assistant?branch=master -.. |Join the chat at https://gitter.im/balloob/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg - :target: https://gitter.im/balloob/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge -.. |screenshot-states| image:: https://raw.github.com/balloob/home-assistant/master/docs/screenshots.png +.. |Build Status| image:: https://travis-ci.org/home-assistant/home-assistant.svg?branch=master + :target: https://travis-ci.org/home-assistant/home-assistant +.. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg + :target: https://coveralls.io/r/home-assistant/home-assistant?branch=master +.. |Join the chat at https://gitter.im/home-assistant/home-assistant| image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/home-assistant/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png :target: https://home-assistant.io/demo/ diff --git a/config/configuration.yaml.example b/config/configuration.yaml.example index 899134b71a7..0f6318a0638 100644 --- a/config/configuration.yaml.example +++ b/config/configuration.yaml.example @@ -7,7 +7,7 @@ homeassistant: latitude: 32.87336 longitude: 117.22743 - # C for Celcius, F for Fahrenheit + # C for Celsius, F for Fahrenheit temperature_unit: C # Pick yours from here: diff --git a/config/custom_components/mqtt_example.py b/config/custom_components/mqtt_example.py index b4e5e89a977..451a60deef4 100644 --- a/config/custom_components/mqtt_example.py +++ b/config/custom_components/mqtt_example.py @@ -14,7 +14,7 @@ To use the mqtt_example component you will need to add the following to your configuration.yaml file. mqtt_example: - topic: home-assistant/mqtt_example + topic: "home-assistant/mqtt_example" """ import homeassistant.loader as loader @@ -29,7 +29,7 @@ DEFAULT_TOPIC = 'home-assistant/mqtt_example' def setup(hass, config): - """Setup the MQTT example component.""" + """Setup the MQTT example component.""" mqtt = loader.get_component('mqtt') topic = config[DOMAIN].get('topic', DEFAULT_TOPIC) entity_id = 'mqtt_example.last_message' diff --git a/homeassistant/__main__.py b/homeassistant/__main__.py index 6dbb609ec05..f12758a354d 100644 --- a/homeassistant/__main__.py +++ b/homeassistant/__main__.py @@ -31,7 +31,7 @@ def validate_python(): def ensure_config_path(config_dir): """Validate the configuration directory.""" import homeassistant.config as config_util - lib_dir = os.path.join(config_dir, 'lib') + lib_dir = os.path.join(config_dir, 'deps') # Test if configuration directory exists if not os.path.isdir(config_dir): diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 2ba0681c2d6..a4b6550ee84 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -21,7 +21,7 @@ import homeassistant.util.package as pkg_util from homeassistant.const import ( CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED, - TEMP_CELCIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__) + TEMP_CELSIUS, TEMP_FAHRENHEIT, PLATFORM_FORMAT, __version__) from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import ( event_decorators, service, config_per_platform, extract_domain_configs) @@ -65,7 +65,7 @@ def _handle_requirements(hass, component, name): return True for req in component.REQUIREMENTS: - if not pkg_util.install_package(req, target=hass.config.path('lib')): + if not pkg_util.install_package(req, target=hass.config.path('deps')): _LOGGER.error('Not initializing %s because could not install ' 'dependency %s', name, req) return False @@ -211,7 +211,7 @@ def prepare_setup_platform(hass, config, domain, platform_name): def mount_local_lib_path(config_dir): """Add local library to Python Path.""" - sys.path.insert(0, os.path.join(config_dir, 'lib')) + sys.path.insert(0, os.path.join(config_dir, 'deps')) # pylint: disable=too-many-branches, too-many-statements, too-many-arguments @@ -372,10 +372,16 @@ def process_ha_config_upgrade(hass): _LOGGER.info('Upgrading config directory from %s to %s', conf_version, __version__) + # This was where dependencies were installed before v0.18 + # Probably should keep this around until ~v0.20. lib_path = hass.config.path('lib') if os.path.isdir(lib_path): shutil.rmtree(lib_path) + lib_path = hass.config.path('deps') + if os.path.isdir(lib_path): + shutil.rmtree(lib_path) + with open(version_path, 'wt') as outp: outp.write(__version__) @@ -434,7 +440,7 @@ def process_ha_core_config(hass, config): if info.use_fahrenheit: hac.temperature_unit = TEMP_FAHRENHEIT else: - hac.temperature_unit = TEMP_CELCIUS + hac.temperature_unit = TEMP_CELSIUS if hac.location_name is None: hac.location_name = info.city diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index e3bde441211..cf042abbe10 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -7,12 +7,15 @@ https://home-assistant.io/components/alarm_control_panel/ import logging import os +import voluptuous as vol + from homeassistant.components import verisure from homeassistant.const import ( ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER, SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY) from homeassistant.config import load_yaml_config_file from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent @@ -38,6 +41,11 @@ ATTR_TO_PROPERTY = [ ATTR_CODE_FORMAT ] +ALARM_SERVICE_SCHEMA = vol.Schema({ + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, + vol.Optional(ATTR_CODE): cv.string, +}) + def setup(hass, config): """Track states and offer events for sensors.""" @@ -51,10 +59,7 @@ def setup(hass, config): """Map services to methods on Alarm.""" target_alarms = component.extract_from_service(service) - if ATTR_CODE not in service.data: - code = None - else: - code = service.data[ATTR_CODE] + code = service.data.get(ATTR_CODE) method = SERVICE_TO_METHOD[service.service] @@ -68,8 +73,8 @@ def setup(hass, config): for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, alarm_service_handler, - descriptions.get(service)) - + descriptions.get(service), + schema=ALARM_SERVICE_SCHEMA) return True diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index ce5dde3609d..7e13ae3ed75 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -51,8 +51,6 @@ def _platform_validator(method, schema): if not hasattr(platform, schema): return config - print('validating config', method, config) - return getattr(platform, schema)(config) return validator diff --git a/homeassistant/components/automation/time.py b/homeassistant/components/automation/time.py index f28e95c6f7a..761ad73b826 100644 --- a/homeassistant/components/automation/time.py +++ b/homeassistant/components/automation/time.py @@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__) def trigger(hass, config, action): """Listen for state changes based on configuration.""" if CONF_AFTER in config: - after = dt_util.parse_time_str(config[CONF_AFTER]) + after = dt_util.parse_time(config[CONF_AFTER]) if after is None: _error_time(config[CONF_AFTER], CONF_AFTER) return False @@ -62,13 +62,13 @@ def if_action(hass, config): return None if before is not None: - before = dt_util.parse_time_str(before) + before = dt_util.parse_time(before) if before is None: _error_time(before, CONF_BEFORE) return None if after is not None: - after = dt_util.parse_time_str(after) + after = dt_util.parse_time(after) if after is None: _error_time(after, CONF_AFTER) return None diff --git a/homeassistant/components/binary_sensor/mysensors.py b/homeassistant/components/binary_sensor/mysensors.py index cada64bc78f..3cc9798f288 100644 --- a/homeassistant/components/binary_sensor/mysensors.py +++ b/homeassistant/components/binary_sensor/mysensors.py @@ -6,10 +6,9 @@ https://home-assistant.io/components/binary_sensor.mysensors/ """ import logging -from homeassistant.const import ( - ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON) -from homeassistant.components.binary_sensor import ( - BinarySensorDevice, SENSOR_CLASSES) +from homeassistant.components.binary_sensor import (SENSOR_CLASSES, + BinarySensorDevice) +from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON from homeassistant.loader import get_component _LOGGER = logging.getLogger(__name__) @@ -101,8 +100,13 @@ class MySensorsBinarySensor(BinarySensorDevice): @property def device_state_attributes(self): """Return device specific state attributes.""" + address = getattr(self.gateway, 'server_address', None) + if address: + device = '{}:{}'.format(address[0], address[1]) + else: + device = self.gateway.port attr = { - self.mysensors.ATTR_PORT: self.gateway.port, + self.mysensors.ATTR_DEVICE: device, self.mysensors.ATTR_NODE_ID: self.node_id, self.mysensors.ATTR_CHILD_ID: self.child_id, ATTR_BATTERY_LEVEL: self.battery_level, diff --git a/homeassistant/components/binary_sensor/nest.py b/homeassistant/components/binary_sensor/nest.py index 79530bad52f..9f963b730b5 100644 --- a/homeassistant/components/binary_sensor/nest.py +++ b/homeassistant/components/binary_sensor/nest.py @@ -4,12 +4,14 @@ Support for Nest Thermostat Binary Sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.nest/ """ -import logging -import socket +import voluptuous as vol import homeassistant.components.nest as nest from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.sensor.nest import NestSensor +from homeassistant.const import ( + CONF_PLATFORM, CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS +) DEPENDENCIES = ['nest'] BINARY_TYPES = ['fan', @@ -23,25 +25,19 @@ BINARY_TYPES = ['fan', 'hvac_emer_heat_state', 'online'] +PLATFORM_SCHEMA = vol.Schema({ + vol.Required(CONF_PLATFORM): nest.DOMAIN, + vol.Optional(CONF_SCAN_INTERVAL): + vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(BINARY_TYPES)], +}) + def setup_platform(hass, config, add_devices, discovery_info=None): """Setup Nest binary sensors.""" - logger = logging.getLogger(__name__) - try: - for structure in nest.NEST.structures: - for device in structure.devices: - for variable in config['monitored_conditions']: - if variable in BINARY_TYPES: - add_devices([NestBinarySensor(structure, - device, - variable)]) - else: - logger.error('Nest sensor type: "%s" does not exist', - variable) - except socket.error: - logger.error( - "Connection error logging into the nest web service." - ) + for structure, device in nest.devices(): + add_devices([NestBinarySensor(structure, device, variable) + for variable in config[CONF_MONITORED_CONDITIONS]]) class NestBinarySensor(NestSensor, BinarySensorDevice): diff --git a/homeassistant/components/binary_sensor/vera.py b/homeassistant/components/binary_sensor/vera.py index 3f92503dcbf..27d604805dc 100644 --- a/homeassistant/components/binary_sensor/vera.py +++ b/homeassistant/components/binary_sensor/vera.py @@ -49,8 +49,7 @@ class VeraBinarySensor(VeraDevice, BinarySensorDevice): last_tripped = self.vera_device.last_trip if last_tripped is not None: utc_time = dt_util.utc_from_timestamp(int(last_tripped)) - attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str( - utc_time) + attr[ATTR_LAST_TRIP_TIME] = utc_time.isoformat() else: attr[ATTR_LAST_TRIP_TIME] = None tripped = self.vera_device.is_tripped diff --git a/homeassistant/components/binary_sensor/wink.py b/homeassistant/components/binary_sensor/wink.py index c24253ce715..8c8c6736ba8 100644 --- a/homeassistant/components/binary_sensor/wink.py +++ b/homeassistant/components/binary_sensor/wink.py @@ -10,7 +10,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['python-wink==0.6.4'] +REQUIREMENTS = ['python-wink==0.7.4'] # These are the available sensors mapped to binary_sensor class SENSOR_TYPES = { diff --git a/homeassistant/components/binary_sensor/zwave.py b/homeassistant/components/binary_sensor/zwave.py index ac846cb8df2..14268e1c124 100644 --- a/homeassistant/components/binary_sensor/zwave.py +++ b/homeassistant/components/binary_sensor/zwave.py @@ -8,11 +8,7 @@ import logging import datetime import homeassistant.util.dt as dt_util from homeassistant.helpers.event import track_point_in_time - -from homeassistant.components.zwave import ( - ATTR_NODE_ID, ATTR_VALUE_ID, - COMMAND_CLASS_SENSOR_BINARY, NETWORK, - ZWaveDeviceEntity, get_config_value) +from homeassistant.components import zwave from homeassistant.components.binary_sensor import ( DOMAIN, BinarySensorDevice) @@ -36,11 +32,11 @@ DEVICE_MAPPINGS = { def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Z-Wave platform for sensors.""" - if discovery_info is None or NETWORK is None: + if discovery_info is None or zwave.NETWORK is None: return - node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]] - value = node.values[discovery_info[ATTR_VALUE_ID]] + node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]] + value = node.values[discovery_info[zwave.ATTR_VALUE_ID]] value.set_change_verified(False) # Make sure that we have values for the key before converting to int @@ -53,18 +49,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if specific_sensor_key in DEVICE_MAPPINGS: if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT: # Default the multiplier to 4 - re_arm_multiplier = (get_config_value(value.node, 9) or 4) + re_arm_multiplier = (zwave.get_config_value(value.node, + 9) or 4) add_devices([ ZWaveTriggerSensor(value, "motion", hass, re_arm_multiplier * 8) ]) return - if value.command_class == COMMAND_CLASS_SENSOR_BINARY: + if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: add_devices([ZWaveBinarySensor(value, None)]) -class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity): +class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity): """Representation of a binary sensor within Z-Wave.""" def __init__(self, value, sensor_class): @@ -74,7 +71,7 @@ class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity): from openzwave.network import ZWaveNetwork from pydispatch import dispatcher - ZWaveDeviceEntity.__init__(self, value, DOMAIN) + zwave.ZWaveDeviceEntity.__init__(self, value, DOMAIN) dispatcher.connect( self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED) diff --git a/homeassistant/components/browser.py b/homeassistant/components/browser.py index edfe1008c6e..fc78b83bd60 100644 --- a/homeassistant/components/browser.py +++ b/homeassistant/components/browser.py @@ -4,10 +4,18 @@ Provides functionality to launch a web browser on the host machine. For more details about this component, please refer to the documentation at https://home-assistant.io/components/browser/ """ +import voluptuous as vol DOMAIN = "browser" SERVICE_BROWSE_URL = "browse_url" +ATTR_URL = 'url' +ATTR_URL_DEFAULT = 'https://www.google.com' + +SERVICE_BROWSE_URL_SCHEMA = vol.Schema({ + vol.Required(ATTR_URL, default=ATTR_URL_DEFAULT): vol.Url, +}) + def setup(hass, config): """Listen for browse_url events.""" @@ -15,8 +23,7 @@ def setup(hass, config): hass.services.register(DOMAIN, SERVICE_BROWSE_URL, lambda service: - webbrowser.open( - service.data.get( - 'url', 'https://www.google.com'))) + webbrowser.open(service.data[ATTR_URL]), + schema=SERVICE_BROWSE_URL_SCHEMA) return True diff --git a/homeassistant/components/conversation.py b/homeassistant/components/conversation.py index 11e018846c2..dc8446412c4 100644 --- a/homeassistant/components/conversation.py +++ b/homeassistant/components/conversation.py @@ -8,9 +8,12 @@ import logging import re import warnings +import voluptuous as vol + from homeassistant import core from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON) +import homeassistant.helpers.config_validation as cv DOMAIN = "conversation" @@ -18,6 +21,10 @@ SERVICE_PROCESS = "process" ATTR_TEXT = "text" +SERVICE_PROCESS_SCHEMA = vol.Schema({ + vol.Required(ATTR_TEXT): vol.All(cv.string, vol.Lower), +}) + REGEX_TURN_COMMAND = re.compile(r'turn (?P(?: |\w)+) (?P\w+)') REQUIREMENTS = ['fuzzywuzzy==0.8.0'] @@ -32,11 +39,7 @@ def setup(hass, config): def process(service): """Parse text into commands.""" - if ATTR_TEXT not in service.data: - logger.error("Received process service call without a text") - return - - text = service.data[ATTR_TEXT].lower() + text = service.data[ATTR_TEXT] match = REGEX_TURN_COMMAND.match(text) if not match: @@ -67,6 +70,6 @@ def setup(hass, config): logger.error( 'Got unsupported command %s from text %s', command, text) - hass.services.register(DOMAIN, SERVICE_PROCESS, process) - + hass.services.register(DOMAIN, SERVICE_PROCESS, process, + schema=SERVICE_PROCESS_SCHEMA) return True diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 1866e972e28..beb9e4a7214 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -94,7 +94,7 @@ def setup(hass, config): yaml_path = hass.config.path(YAML_DEVICES) conf = config.get(DOMAIN, {}) - if isinstance(conf, list): + if isinstance(conf, list) and len(conf) > 0: conf = conf[0] consider_home = timedelta( seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int, diff --git a/homeassistant/components/device_tracker/bluetooth_tracker.py b/homeassistant/components/device_tracker/bluetooth_tracker.py new file mode 100644 index 00000000000..70fefbca1b7 --- /dev/null +++ b/homeassistant/components/device_tracker/bluetooth_tracker.py @@ -0,0 +1,91 @@ +"""Tracking for bluetooth devices.""" +import logging +from datetime import timedelta + +from homeassistant.helpers.event import track_point_in_utc_time +from homeassistant.components.device_tracker import ( + YAML_DEVICES, + CONF_TRACK_NEW, + CONF_SCAN_INTERVAL, + DEFAULT_SCAN_INTERVAL, + load_config, +) +import homeassistant.util as util +import homeassistant.util.dt as dt_util + +_LOGGER = logging.getLogger(__name__) + +REQUIREMENTS = ['pybluez==0.22'] + +BT_PREFIX = 'BT_' + + +def setup_scanner(hass, config, see): + """Setup the Bluetooth Scanner.""" + # pylint: disable=import-error + import bluetooth + + def see_device(device): + """Mark a device as seen.""" + see(mac=BT_PREFIX + device[0], host_name=device[1]) + + def discover_devices(): + """Discover bluetooth devices.""" + result = bluetooth.discover_devices(duration=8, + lookup_names=True, + flush_cache=True, + lookup_class=False) + _LOGGER.debug("Bluetooth devices discovered = " + str(len(result))) + return result + + yaml_path = hass.config.path(YAML_DEVICES) + devs_to_track = [] + devs_donot_track = [] + + # Load all known devices. + # We just need the devices so set consider_home and home range + # to 0 + for device in load_config(yaml_path, hass, 0, 0): + # check if device is a valid bluetooth device + if device.mac and device.mac[:3].upper() == BT_PREFIX: + if device.track: + devs_to_track.append(device.mac[3:]) + else: + devs_donot_track.append(device.mac[3:]) + + # if track new devices is true discover new devices + # on startup. + track_new = util.convert(config.get(CONF_TRACK_NEW), bool, + len(devs_to_track) == 0) + if track_new: + for dev in discover_devices(): + if dev[0] not in devs_to_track and \ + dev[0] not in devs_donot_track: + devs_to_track.append(dev[0]) + see_device(dev) + + if not devs_to_track: + _LOGGER.warning("No bluetooth devices to track!") + return False + + interval = util.convert(config.get(CONF_SCAN_INTERVAL), int, + DEFAULT_SCAN_INTERVAL) + + def update_bluetooth(now): + """Lookup bluetooth device and update status.""" + try: + for mac in devs_to_track: + _LOGGER.debug("Scanning " + mac) + result = bluetooth.lookup_name(mac, timeout=5) + if not result: + # Could not lookup device name + continue + see_device((mac, result)) + except bluetooth.BluetoothError: + _LOGGER.exception('Error looking up bluetooth device!') + track_point_in_utc_time(hass, update_bluetooth, + now + timedelta(seconds=interval)) + + update_bluetooth(dt_util.utcnow()) + + return True diff --git a/homeassistant/components/device_tracker/fritz.py b/homeassistant/components/device_tracker/fritz.py index e3a453dad3a..8def71cce73 100644 --- a/homeassistant/components/device_tracker/fritz.py +++ b/homeassistant/components/device_tracker/fritz.py @@ -12,7 +12,9 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers import validate_config from homeassistant.util import Throttle -REQUIREMENTS = ['fritzconnection==0.4.6'] +REQUIREMENTS = ['https://github.com/deisi/fritzconnection/archive/' + 'b5c14515e1c8e2652b06b6316a7f3913df942841.zip' + '#fritzconnection==0.4.6'] # Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) diff --git a/homeassistant/components/device_tracker/netgear.py b/homeassistant/components/device_tracker/netgear.py index 6b0cbc5f465..b20e5aae60e 100644 --- a/homeassistant/components/device_tracker/netgear.py +++ b/homeassistant/components/device_tracker/netgear.py @@ -9,14 +9,15 @@ import threading from datetime import timedelta from homeassistant.components.device_tracker import DOMAIN -from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, \ + CONF_PORT from homeassistant.util import Throttle # Return cached results if last scan was less then this time ago. MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pynetgear==0.3.2'] +REQUIREMENTS = ['pynetgear==0.3.3'] def get_scanner(hass, config): @@ -25,12 +26,13 @@ def get_scanner(hass, config): host = info.get(CONF_HOST) username = info.get(CONF_USERNAME) password = info.get(CONF_PASSWORD) + port = info.get(CONF_PORT) if password is not None and host is None: _LOGGER.warning('Found username or password but no host') return None - scanner = NetgearDeviceScanner(host, username, password) + scanner = NetgearDeviceScanner(host, username, password, port) return scanner if scanner.success_init else None @@ -38,7 +40,7 @@ def get_scanner(hass, config): class NetgearDeviceScanner(object): """Queries a Netgear wireless router using the SOAP-API.""" - def __init__(self, host, username, password): + def __init__(self, host, username, password, port): """Initialize the scanner.""" import pynetgear @@ -49,8 +51,10 @@ class NetgearDeviceScanner(object): self._api = pynetgear.Netgear() elif username is None: self._api = pynetgear.Netgear(password, host) - else: + elif port is None: self._api = pynetgear.Netgear(password, host, username) + else: + self._api = pynetgear.Netgear(password, host, username, port) _LOGGER.info("Logging in") diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index eb41aec1ebc..e2dbd0185f8 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -34,21 +34,33 @@ def setup_scanner(hass, config, see): """Setup an OwnTracks tracker.""" max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY) - def owntracks_location_update(topic, payload, qos): - """MQTT message received.""" - # Docs on available data: - # http://owntracks.org/booklet/tech/json/#_typelocation + def validate_payload(payload, data_type): + """Validate OwnTracks payload.""" try: data = json.loads(payload) except ValueError: # If invalid JSON - _LOGGER.error( - 'Unable to parse payload as JSON: %s', payload) - return + _LOGGER.error('Unable to parse payload as JSON: %s', payload) + return None + if not isinstance(data, dict) or data.get('_type') != data_type: + _LOGGER.debug('Skipping %s update for following data ' + 'because of missing or malformatted data: %s', + data_type, data) + return None + if max_gps_accuracy is not None and \ + convert(data.get('acc'), float, 0.0) > max_gps_accuracy: + _LOGGER.debug('Skipping %s update because expected GPS ' + 'accuracy %s is not met: %s', + data_type, max_gps_accuracy, data) + return None + return data - if (not isinstance(data, dict) or data.get('_type') != 'location') or ( - max_gps_accuracy is not None and - convert(data.get('acc'), float, 0.0) > max_gps_accuracy): + def owntracks_location_update(topic, payload, qos): + """MQTT message received.""" + # Docs on available data: + # http://owntracks.org/booklet/tech/json/#_typelocation + data = validate_payload(payload, 'location') + if not data: return dev_id, kwargs = _parse_see_args(topic, data) @@ -65,24 +77,16 @@ def setup_scanner(hass, config, see): see_beacons(dev_id, kwargs) def owntracks_event_update(topic, payload, qos): - # pylint: disable=too-many-branches, too-many-statements """MQTT event (geofences) received.""" # Docs on available data: # http://owntracks.org/booklet/tech/json/#_typetransition - try: - data = json.loads(payload) - except ValueError: - # If invalid JSON - _LOGGER.error( - 'Unable to parse payload as JSON: %s', payload) - return - - if not isinstance(data, dict) or data.get('_type') != 'transition': + data = validate_payload(payload, 'transition') + if not data: return if data.get('desc') is None: _LOGGER.error( - "Location missing from `enter/exit` message - " + "Location missing from `Entering/Leaving` message - " "please turn `Share` on in OwnTracks app") return # OwnTracks uses - at the start of a beacon zone @@ -93,16 +97,16 @@ def setup_scanner(hass, config, see): dev_id, kwargs = _parse_see_args(topic, data) - if data['event'] == 'enter': + def enter_event(): + """Execute enter event.""" zone = hass.states.get("zone.{}".format(location)) with LOCK: - if zone is None: - if data['t'] == 'b': - # Not a HA zone, and a beacon so assume mobile - beacons = MOBILE_BEACONS_ACTIVE[dev_id] - if location not in beacons: - beacons.append(location) - _LOGGER.info("Added beacon %s", location) + if zone is None and data['t'] == 'b': + # Not a HA zone, and a beacon so assume mobile + beacons = MOBILE_BEACONS_ACTIVE[dev_id] + if location not in beacons: + beacons.append(location) + _LOGGER.info("Added beacon %s", location) else: # Normal region regions = REGIONS_ENTERED[dev_id] @@ -114,7 +118,8 @@ def setup_scanner(hass, config, see): see(**kwargs) see_beacons(dev_id, kwargs) - elif data['event'] == 'leave': + def leave_event(): + """Execute leave event.""" with LOCK: regions = REGIONS_ENTERED[dev_id] if location in regions: @@ -146,6 +151,10 @@ def setup_scanner(hass, config, see): beacons.remove(location) _LOGGER.info("Remove beacon %s", location) + if data['event'] == 'enter': + enter_event() + elif data['event'] == 'leave': + leave_event() else: _LOGGER.error( 'Misformatted mqtt msgs, _type=transition, event=%s', diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 7daf513316e..900d826e61a 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -15,7 +15,7 @@ from homeassistant.const import ( EVENT_PLATFORM_DISCOVERED) DOMAIN = "discovery" -REQUIREMENTS = ['netdisco==0.6.1'] +REQUIREMENTS = ['netdisco==0.6.4'] SCAN_INTERVAL = 300 # seconds diff --git a/homeassistant/components/downloader.py b/homeassistant/components/downloader.py index c425d9cbb23..e05c617bcf0 100644 --- a/homeassistant/components/downloader.py +++ b/homeassistant/components/downloader.py @@ -10,8 +10,10 @@ import re import threading import requests +import voluptuous as vol from homeassistant.helpers import validate_config +import homeassistant.helpers.config_validation as cv from homeassistant.util import sanitize_filename DOMAIN = "downloader" @@ -21,6 +23,11 @@ SERVICE_DOWNLOAD_FILE = "download_file" ATTR_URL = "url" ATTR_SUBDIR = "subdir" +SERVICE_DOWNLOAD_FILE_SCHEMA = vol.Schema({ + vol.Required(ATTR_URL): vol.Url, + vol.Optional(ATTR_SUBDIR): cv.string, +}) + CONF_DOWNLOAD_DIR = 'download_dir' @@ -48,10 +55,6 @@ def setup(hass, config): def download_file(service): """Start thread to download file specified in the URL.""" - if ATTR_URL not in service.data: - logger.error("Service called but 'url' parameter not specified.") - return - def do_download(): """Download the file.""" try: @@ -127,7 +130,7 @@ def setup(hass, config): threading.Thread(target=do_download).start() - hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, - download_file) + hass.services.register(DOMAIN, SERVICE_DOWNLOAD_FILE, download_file, + schema=SERVICE_DOWNLOAD_FILE_SCHEMA) return True diff --git a/homeassistant/components/feedreader.py b/homeassistant/components/feedreader.py new file mode 100644 index 00000000000..b5fec104bbe --- /dev/null +++ b/homeassistant/components/feedreader.py @@ -0,0 +1,80 @@ +"""RSS/Atom feed reader for Home Assistant.""" +from datetime import datetime +from logging import getLogger +import voluptuous as vol +from homeassistant.helpers.event import track_utc_time_change + +REQUIREMENTS = ['feedparser==5.2.1'] +_LOGGER = getLogger(__name__) +DOMAIN = "feedreader" +EVENT_FEEDREADER = "feedreader" +# pylint: disable=no-value-for-parameter +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: { + 'urls': [vol.Url()], + } +}, extra=vol.ALLOW_EXTRA) + + +# pylint: disable=too-few-public-methods +class FeedManager(object): + """Abstraction over feedparser module.""" + + def __init__(self, url, hass): + """Initialize the FeedManager object, poll every hour.""" + self._url = url + self._feed = None + self._hass = hass + # Initialize last entry timestamp as epoch time + self._last_entry_timestamp = datetime.utcfromtimestamp(0).timetuple() + _LOGGER.debug('Loading feed %s', self._url) + self._update() + track_utc_time_change(hass, lambda now: self._update(), + minute=0, second=0) + + def _log_no_entries(self): + """Send no entries log at debug level.""" + _LOGGER.debug('No new entries in feed %s', self._url) + + def _update(self): + """Update the feed and publish new entries in the event bus.""" + import feedparser + _LOGGER.info('Fetching new data from feed %s', self._url) + self._feed = feedparser.parse(self._url, + etag=None if not self._feed + else self._feed.get('etag'), + modified=None if not self._feed + else self._feed.get('modified')) + if not self._feed: + _LOGGER.error('Error fetching feed data from %s', self._url) + else: + if self._feed.bozo != 0: + _LOGGER.error('Error parsing feed %s', self._url) + # Using etag and modified, if there's no new data available, + # the entries list will be empty + elif len(self._feed.entries) > 0: + _LOGGER.debug('Entries available in feed %s', self._url) + self._publish_new_entries() + self._last_entry_timestamp = \ + self._feed.entries[0].published_parsed + else: + self._log_no_entries() + + def _publish_new_entries(self): + """Publish new entries to the event bus.""" + new_entries = False + for entry in self._feed.entries: + # Consider only entries newer then the latest parsed one + if entry.published_parsed > self._last_entry_timestamp: + new_entries = True + entry.update({'feed_url': self._url}) + self._hass.bus.fire(EVENT_FEEDREADER, entry) + if not new_entries: + self._log_no_entries() + + +def setup(hass, config): + """Setup the feedreader component.""" + urls = config.get(DOMAIN)['urls'] + feeds = [FeedManager(url, hass) for url in urls] + return len(feeds) > 0 diff --git a/homeassistant/components/frontend/mdi_version.py b/homeassistant/components/frontend/mdi_version.py index 87b533d9673..2b8df1a660d 100644 --- a/homeassistant/components/frontend/mdi_version.py +++ b/homeassistant/components/frontend/mdi_version.py @@ -1,2 +1,2 @@ """DO NOT MODIFY. Auto-generated by update_mdi script.""" -VERSION = "df49e6b7c930eb39b42ff1909712e95e" +VERSION = "af8a531f1c2e477c07c4b3394bd1ce13" diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 93ae4032b56..b6c83c6ac64 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 = "c2932592a6946e955ddc46f31409b81f" +VERSION = "ffd8a1bde5ba13f300c3d6ad32036526" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 799ecae4a09..b0952f1e979 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -2966,7 +2966,10 @@ font-style: italic; padding-left: var(--paper-toggle-button-label-spacing, 8px); pointer-events: none; color: var(--paper-toggle-button-label-color, --primary-text-color); - }