diff --git a/.coveragerc b/.coveragerc index 28b4b926eab..46a945e760b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -17,6 +17,9 @@ omit = homeassistant/components/*/tellstick.py homeassistant/components/*/vera.py + homeassistant/components/ecobee.py + homeassistant/components/*/ecobee.py + homeassistant/components/verisure.py homeassistant/components/*/verisure.py @@ -29,25 +32,29 @@ omit = homeassistant/components/rfxtrx.py homeassistant/components/*/rfxtrx.py - homeassistant/components/ifttt.py + homeassistant/components/binary_sensor/arest.py homeassistant/components/browser.py homeassistant/components/camera/* homeassistant/components/device_tracker/actiontec.py 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 homeassistant/components/device_tracker/snmp.py homeassistant/components/discovery.py homeassistant/components/downloader.py + homeassistant/components/ifttt.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 +71,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 @@ -93,10 +101,14 @@ omit = homeassistant/components/switch/command_switch.py homeassistant/components/switch/edimax.py homeassistant/components/switch/hikvisioncam.py + homeassistant/components/switch/mystrom.py + homeassistant/components/switch/orvibo.py homeassistant/components/switch/rest.py homeassistant/components/switch/rpi_gpio.py homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py + homeassistant/components/thermostat/homematic.py + homeassistant/components/thermostat/honeywell.py homeassistant/components/thermostat/nest.py homeassistant/components/thermostat/radiotherm.py diff --git a/.travis.yml b/.travis.yml index da3516554ef..f12d318b5d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ sudo: false language: python cache: directories: - - $HOME/virtualenv/python3.4.2/ + - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/ python: - - "3.4" + - 3.4.2 + - 3.5.0 install: - script/bootstrap_server script: diff --git a/Dockerfile b/Dockerfile index 9344ec65245..a1f9d459295 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,27 @@ -FROM python:3-onbuild +FROM python:3.4 MAINTAINER Paulus Schoutsen VOLUME /config -RUN pip3 install --no-cache-dir -r requirements_all.txt +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app # For the nmap tracker RUN apt-get update && \ apt-get install -y --no-install-recommends nmap net-tools && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +COPY script/build_python_openzwave script/build_python_openzwave RUN apt-get update && \ apt-get install -y cython3 libudev-dev && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ pip3 install "cython<0.23" && \ script/build_python_openzwave +COPY requirements_all.txt requirements_all.txt +RUN pip3 install --no-cache-dir -r requirements_all.txt + +# Copy source +COPY . . + CMD [ "python", "-m", "homeassistant", "--config", "/config" ] diff --git a/MANIFEST.in b/MANIFEST.in index 8233015e646..d04d86bae58 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include README.md +include README.rst include LICENSE graft homeassistant prune homeassistant/components/frontend/www_static/home-assistant-polymer diff --git a/README.md b/README.md deleted file mode 100644 index e1528e393bd..00000000000 --- a/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Home Assistant [![Build Status](https://travis-ci.org/balloob/home-assistant.svg?branch=master)](https://travis-ci.org/balloob/home-assistant) [![Coverage Status](https://img.shields.io/coveralls/balloob/home-assistant.svg)](https://coveralls.io/r/balloob/home-assistant?branch=master) [![Join the chat at https://gitter.im/balloob/home-assistant](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/balloob/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -[demo]: https://home-assistant.io/demo/ - -Home Assistant is a home automation platform running on Python 3. The goal of Home Assistant is to be able to track and control all devices at home and offer a platform for automating control. - -To get started: -```bash -python3 -m pip install homeassistant -hass --open-ui -``` - -Check out [the website](https://home-assistant.io) for [a demo][demo], installation instructions, tutorials and documentation. - -[![screenshot-states](https://raw.github.com/balloob/home-assistant/master/docs/screenshots.png)][demo] - -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, 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/) - * Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org). - * [See full list of supported devices](https://home-assistant.io/components/) - -Built home automation on top of your devices: - - * Keep a precise history of every change to the state of your house - * Turn on the lights when people get home after sun set - * Turn on lights slowly during sun set to compensate for less light - * Turn off all lights and devices when everybody leaves the house - * Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects like [OwnTracks](http://owntracks.org/) - * Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), [Telegram](https://telegram.org/), and [Jabber (XMPP)](http://xmpp.org) - -The system is built modular so support for other devices or actions can be implemented easily. See also the [section on architecture](https://home-assistant.io/developers/architecture.html) and the [section on creating your own components](https://home-assistant.io/developers/creating_components.html). - -If you run into issues while using Home Assistant or during development of a component, check the [Home Assistant help section](https://home-assistant.io/help/) how to reach us. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000000..c66b3670f32 --- /dev/null +++ b/README.rst @@ -0,0 +1,98 @@ +Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/balloob/home-assistant| +=========================================================================================================== + +Home Assistant is a home automation platform running on Python 3. The +goal of Home Assistant is to be able to track and control all devices at +home and offer a platform for automating control. + +To get started: + +.. code:: bash + + python3 -m pip install homeassistant + hass --open-ui + +Check out `the website `__ for `a +demo `__, installation instructions, +tutorials and documentation. + +|screenshot-states| + +Examples of devices it can interface it: + +- Monitoring connected devices to a wireless router: + `OpenWrt `__, + `Tomato `__, + `Netgear `__, + `DD-WRT `__, + `TPLink `__, + `ASUSWRT `__ and any SNMP + capable Linksys WAP/WRT +- `Philips Hue `__ lights, + `WeMo `__ + switches, `Edimax `__ switches, + `Efergy `__ energy monitoring, and + `Tellstick `__ devices and + sensors +- `Google + Chromecasts `__, + `Music Player Daemon `__, `Logitech + Squeezebox `__, + `Plex `__, `Kodi (XBMC) `__, + iTunes (by way of + `itunes-api `__), and Amazon + Fire TV (by way of + `python-firetv `__) +- Support for + `ISY994 `__ + (Insteon and X10 devices), `Z-Wave `__, `Nest + Thermostats `__, + `RFXtrx `__, + `Arduino `__, `Raspberry + Pi `__, and + `Modbus `__ +- Interaction with `IFTTT `__ +- Integrate data from the `Bitcoin `__ network, + meteorological data from + `OpenWeatherMap `__ and + `Forecast.io `__, + `Transmission `__, or + `SABnzbd `__. +- `See full list of supported + devices `__ + +Built home automation on top of your devices: + +- Keep a precise history of every change to the state of your house +- Turn on the lights when people get home after sun set +- Turn on lights slowly during sun set to compensate for less light +- Turn off all lights and devices when everybody leaves the house +- Offers a `REST API `__ + and can interface with MQTT for easy integration with other projects + like `OwnTracks `__ +- Allow sending notifications using + `Instapush `__, `Notify My Android + (NMA) `__, + `PushBullet `__, + `PushOver `__, `Slack `__, + `Telegram `__, and `Jabber + (XMPP) `__ + +The system is built modular so support for other devices or actions can +be implemented easily. See also the `section on +architecture `__ +and the `section on creating your own +components `__. + +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 + :target: https://home-assistant.io/demo/ diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index daee13914fd..41377aadebf 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): @@ -80,7 +82,7 @@ def _setup_component(hass, domain, config): return True component = loader.get_component(domain) - missing_deps = [dep for dep in component.DEPENDENCIES + missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) if dep not in hass.config.components] if missing_deps: @@ -104,7 +106,7 @@ def _setup_component(hass, domain, config): # Assumption: if a component does not depend on groups # it communicates with devices - if group.DOMAIN not in component.DEPENDENCIES: + if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []): hass.pool.add_worker() hass.bus.fire( @@ -131,14 +133,13 @@ def prepare_setup_platform(hass, config, domain, platform_name): return platform # Load dependencies - if hasattr(platform, 'DEPENDENCIES'): - for component in platform.DEPENDENCIES: - if not setup_component(hass, component, config): - _LOGGER.error( - 'Unable to prepare setup for platform %s because ' - 'dependency %s could not be initialized', platform_path, - component) - return None + for component in getattr(platform, 'DEPENDENCIES', []): + if not setup_component(hass, component, config): + _LOGGER.error( + 'Unable to prepare setup for platform %s because ' + 'dependency %s could not be initialized', platform_path, + component) + return None if not _handle_requirements(hass, platform, platform_path): return None @@ -167,6 +168,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 +254,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 +282,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/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index d3289e08e62..3f5e6362fb6 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -15,7 +15,6 @@ from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent DOMAIN = 'alarm_control_panel' -DEPENDENCIES = [] SCAN_INTERVAL = 30 ENTITY_ID_FORMAT = DOMAIN + '.{}' diff --git a/homeassistant/components/alarm_control_panel/demo.py b/homeassistant/components/alarm_control_panel/demo.py new file mode 100644 index 00000000000..0ace53167de --- /dev/null +++ b/homeassistant/components/alarm_control_panel/demo.py @@ -0,0 +1,13 @@ +""" +homeassistant.components.alarm_control_panel.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has two fake alarm control panels. +""" +import homeassistant.components.alarm_control_panel.manual as manual + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo alarm control panels. """ + add_devices([ + manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10), + ]) diff --git a/homeassistant/components/alarm_control_panel/manual.py b/homeassistant/components/alarm_control_panel/manual.py index 8c98eec50e1..63bc989f3df 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 @@ -18,8 +18,6 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = [] - DEFAULT_ALARM_NAME = 'HA Alarm' DEFAULT_PENDING_TIME = 60 DEFAULT_TRIGGER_TIME = 120 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..1e6e66baee0 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,10 +14,11 @@ 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, URL_API_LOG_OUT, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL, HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_UNPROCESSABLE_ENTITY) @@ -35,10 +36,6 @@ _LOGGER = logging.getLogger(__name__) def setup(hass, config): """ Register the API with the HTTP interface. """ - if 'http' not in hass.config.components: - _LOGGER.error('Dependency http is not loaded') - return False - # /api - for validation purposes hass.http.register_path('GET', URL_API, _handle_get_api) @@ -89,6 +86,11 @@ 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) + + hass.http.register_path('POST', URL_API_LOG_OUT, _handle_post_api_log_out) + return True @@ -104,6 +106,7 @@ def _handle_get_api_stream(handler, path_match, data): wfile = handler.wfile write_lock = threading.Lock() block = threading.Event() + session_id = None restrict = data.get('restrict') if restrict: @@ -117,6 +120,7 @@ def _handle_get_api_stream(handler, path_match, data): try: wfile.write(msg.encode("UTF-8")) wfile.flush() + handler.server.sessions.extend_validation(session_id) except IOError: block.set() @@ -136,6 +140,7 @@ def _handle_get_api_stream(handler, path_match, data): handler.send_response(HTTP_OK) handler.send_header('Content-type', 'text/event-stream') + session_id = handler.set_session_cookie_header() handler.end_headers() hass.bus.listen(MATCH_ALL, forward_events) @@ -341,6 +346,19 @@ 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. """ + handler.write_file(handler.server.hass.config.path(ERROR_LOG_FILENAME), + False) + + +def _handle_post_api_log_out(handler, path_match, data): + """ Log user out. """ + handler.send_response(HTTP_OK) + handler.destroy_session() + handler.end_headers() + + 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..88967ec1f74 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 @@ -19,7 +19,6 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) DOMAIN = "arduino" -DEPENDENCIES = [] REQUIREMENTS = ['PyMata==2.07a'] BOARD = None _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index b734728e59b..23d83f554ca 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 @@ -121,7 +123,7 @@ def _migrate_old_config(config): _LOGGER.warning( 'You are using an old configuration format. Please upgrade: ' - 'https://home-assistant.io/components/automation.html') + 'https://home-assistant.io/components/automation/') new_conf = { CONF_TRIGGER: dict(config), 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..9f099100084 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 @@ -14,6 +14,7 @@ from homeassistant.helpers.event import track_state_change CONF_ENTITY_ID = "entity_id" CONF_BELOW = "below" CONF_ABOVE = "above" +CONF_ATTRIBUTE = "attribute" _LOGGER = logging.getLogger(__name__) @@ -28,6 +29,7 @@ def trigger(hass, config, action): below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) + attribute = config.get(CONF_ATTRIBUTE) if below is None and above is None: _LOGGER.error("Missing configuration key." @@ -40,8 +42,8 @@ def trigger(hass, config, action): """ Listens for state changes and calls action. """ # Fire action if we go from outside range into range - if _in_range(to_s.state, above, below) and \ - (from_s is None or not _in_range(from_s.state, above, below)): + if _in_range(to_s, above, below, attribute) and \ + (from_s is None or not _in_range(from_s, above, below, attribute)): action() track_state_change( @@ -61,6 +63,7 @@ def if_action(hass, config): below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) + attribute = config.get(CONF_ATTRIBUTE) if below is None and above is None: _LOGGER.error("Missing configuration key." @@ -71,18 +74,19 @@ def if_action(hass, config): def if_numeric_state(): """ Test numeric state condition. """ state = hass.states.get(entity_id) - return state is not None and _in_range(state.state, above, below) + return state is not None and _in_range(state, above, below, attribute) return if_numeric_state -def _in_range(value, range_start, range_end): +def _in_range(state, range_start, range_end, attribute): """ Checks if value is inside the range """ - + value = (state.state if attribute is None + else state.attributes.get(attribute)) try: value = float(value) except ValueError: - _LOGGER.warn("Missing value in numeric check") + _LOGGER.warning("Missing value in numeric check") return False if range_start is not None and range_end is not None: 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..f0f800bd313 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 @@ -46,6 +46,7 @@ def trigger(hass, config, action): from_match = _in_zone(hass, zone_entity_id, from_s) if from_s else None to_match = _in_zone(hass, zone_entity_id, to_s) + # pylint: disable=too-many-boolean-expressions if event == EVENT_ENTER and not from_match and to_match or \ event == EVENT_LEAVE and from_match and not to_match: action() diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..ccfd57aff8c --- /dev/null +++ b/homeassistant/components/binary_sensor/__init__.py @@ -0,0 +1,49 @@ +""" +homeassistant.components.binary_sensor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Component to interface with binary sensors (sensors which only know two states) +that can be monitored. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor/ +""" +import logging + +from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.entity import Entity +from homeassistant.const import (STATE_ON, STATE_OFF) + +DOMAIN = 'binary_sensor' +SCAN_INTERVAL = 30 + +ENTITY_ID_FORMAT = DOMAIN + '.{}' + + +def setup(hass, config): + """ Track states and offer events for binary sensors. """ + component = EntityComponent( + logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) + + component.setup(config) + + return True + + +# pylint: disable=no-self-use +class BinarySensorDevice(Entity): + """ Represents a binary sensor. """ + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return None + + @property + def state(self): + """ Returns the state of the binary sensor. """ + return STATE_ON if self.is_on else STATE_OFF + + @property + def friendly_state(self): + """ Returns the friendly state of the binary sensor. """ + return None diff --git a/homeassistant/components/binary_sensor/arest.py b/homeassistant/components/binary_sensor/arest.py new file mode 100644 index 00000000000..7eafca9f2ae --- /dev/null +++ b/homeassistant/components/binary_sensor/arest.py @@ -0,0 +1,107 @@ +""" +homeassistant.components.binary_sensor.arest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The arest sensor will consume an exposed aREST API of a device. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.arest/ +""" +from datetime import timedelta +import logging + +import requests + +from homeassistant.util import Throttle +from homeassistant.components.binary_sensor import BinarySensorDevice + +_LOGGER = logging.getLogger(__name__) + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + +CONF_RESOURCE = 'resource' +CONF_PIN = 'pin' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Get the aREST binary sensor. """ + + resource = config.get(CONF_RESOURCE) + pin = config.get(CONF_PIN) + + if None in (resource, pin): + _LOGGER.error('Not all required config keys present: %s', + ', '.join((CONF_RESOURCE, CONF_PIN))) + return False + + try: + response = requests.get(resource, timeout=10).json() + except requests.exceptions.MissingSchema: + _LOGGER.error('Missing resource or schema in configuration. ' + 'Add http:// to your URL.') + return False + except requests.exceptions.ConnectionError: + _LOGGER.error('No route to device at %s. ' + 'Please check the IP address in the configuration file.', + resource) + return False + + arest = ArestData(resource, pin) + + add_devices([ArestBinarySensor(arest, + resource, + config.get('name', response['name']), + pin)]) + + +# pylint: disable=too-many-instance-attributes, too-many-arguments +class ArestBinarySensor(BinarySensorDevice): + """ Implements an aREST binary sensor for a pin. """ + + def __init__(self, arest, resource, name, pin): + self.arest = arest + self._resource = resource + self._name = name + self._pin = pin + self.update() + + if self._pin is not None: + request = requests.get('{}/mode/{}/i'.format + (self._resource, self._pin), timeout=10) + if request.status_code is not 200: + _LOGGER.error("Can't set mode. Is device offline?") + + @property + def name(self): + """ The name of the binary sensor. """ + return self._name + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return bool(self.arest.data.get('state')) + + def update(self): + """ Gets the latest data from aREST API. """ + self.arest.update() + + +# pylint: disable=too-few-public-methods +class ArestData(object): + """ Class for handling the data retrieval for pins. """ + + def __init__(self, resource, pin): + self._resource = resource + self._pin = pin + self.data = {} + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Gets the latest data from aREST device. """ + try: + response = requests.get('{}/digital/{}'.format( + self._resource, self._pin), timeout=10) + self.data = {'state': response.json()['return_value']} + except requests.exceptions.ConnectionError: + _LOGGER.error("No route to device '%s'. Is device offline?", + self._resource) diff --git a/homeassistant/components/binary_sensor/demo.py b/homeassistant/components/binary_sensor/demo.py new file mode 100644 index 00000000000..087d7405d9b --- /dev/null +++ b/homeassistant/components/binary_sensor/demo.py @@ -0,0 +1,37 @@ +""" +homeassistant.components.binary_sensor.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has two fake binary sensors. +""" +from homeassistant.components.binary_sensor import BinarySensorDevice + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo binary sensors. """ + add_devices([ + DemoBinarySensor('Basement Floor Wet', False), + DemoBinarySensor('Movement Backyard', True), + ]) + + +class DemoBinarySensor(BinarySensorDevice): + """ A Demo binary sensor. """ + + def __init__(self, name, state): + self._name = name + self._state = state + + @property + def should_poll(self): + """ No polling needed for a demo binary sensor. """ + return False + + @property + def name(self): + """ Returns the name of the binary sensor. """ + return self._name + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return self._state diff --git a/homeassistant/components/binary_sensor/mqtt.py b/homeassistant/components/binary_sensor/mqtt.py new file mode 100644 index 00000000000..cac991d4eb2 --- /dev/null +++ b/homeassistant/components/binary_sensor/mqtt.py @@ -0,0 +1,76 @@ +""" +homeassistant.components.binary_sensor.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT binary sensor. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.mqtt/ +""" +import logging +from homeassistant.components.binary_sensor import BinarySensorDevice +import homeassistant.components.mqtt as mqtt + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'MQTT Binary sensor' +DEFAULT_QOS = 0 +DEFAULT_PAYLOAD_ON = 'ON' +DEFAULT_PAYLOAD_OFF = 'OFF' + +DEPENDENCIES = ['mqtt'] + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Add MQTT binary sensor. """ + + if config.get('state_topic') is None: + _LOGGER.error('Missing required variable: state_topic') + return False + + add_devices([MqttBinarySensor( + hass, + config.get('name', DEFAULT_NAME), + config.get('state_topic', None), + config.get('qos', DEFAULT_QOS), + config.get('payload_on', DEFAULT_PAYLOAD_ON), + config.get('payload_off', DEFAULT_PAYLOAD_OFF))]) + + +# pylint: disable=too-many-arguments, too-many-instance-attributes +class MqttBinarySensor(BinarySensorDevice): + """ Represents a binary sensor that is updated by MQTT. """ + def __init__(self, hass, name, state_topic, qos, payload_on, payload_off): + self._hass = hass + self._name = name + self._state = False + self._state_topic = state_topic + self._payload_on = payload_on + self._payload_off = payload_off + self._qos = qos + + def message_received(topic, payload, qos): + """ A new MQTT message has been received. """ + if payload == self._payload_on: + self._state = True + self.update_ha_state() + elif payload == self._payload_off: + self._state = False + self.update_ha_state() + + mqtt.subscribe(hass, self._state_topic, message_received, self._qos) + + @property + def should_poll(self): + """ No polling needed. """ + return False + + @property + def name(self): + """ The name of the binary sensor. """ + return self._name + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return self._state diff --git a/homeassistant/components/browser.py b/homeassistant/components/browser.py index c5a55afad40..88548e2a1b3 100644 --- a/homeassistant/components/browser.py +++ b/homeassistant/components/browser.py @@ -1,12 +1,13 @@ """ 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" -DEPENDENCIES = [] SERVICE_BROWSE_URL = "browse_url" diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index e34d4169fa2..fc5c739c888 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -4,38 +4,23 @@ 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 -import time import re +import time + +import requests + from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity_component import EntityComponent from homeassistant.const import ( ATTR_ENTITY_PICTURE, HTTP_NOT_FOUND, ATTR_ENTITY_ID, ) -from homeassistant.helpers.entity_component import EntityComponent - DOMAIN = 'camera' DEPENDENCIES = ['http'] @@ -74,7 +59,7 @@ MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n' # pylint: disable=too-many-branches def setup(hass, config): - """ Track states and offer events for sensors. """ + """ Track states and offer events for cameras. """ component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, @@ -96,16 +81,21 @@ def setup(hass, config): def _proxy_camera_image(handler, path_match, data): """ Proxies the camera image via the HA server. """ entity_id = path_match.group(ATTR_ENTITY_ID) + camera = component.entities.get(entity_id) - camera = None - if entity_id in component.entities.keys(): - camera = component.entities[entity_id] - - if camera: - response = camera.camera_image() - handler.wfile.write(response) - else: + if camera is None: handler.send_response(HTTP_NOT_FOUND) + handler.end_headers() + return + + response = camera.camera_image() + + if response is None: + handler.send_response(HTTP_NOT_FOUND) + handler.end_headers() + return + + handler.wfile.write(response) hass.http.register_path( 'GET', @@ -114,18 +104,16 @@ def setup(hass, config): # pylint: disable=unused-argument def _proxy_camera_mjpeg_stream(handler, path_match, data): - """ Proxies the camera image as an mjpeg stream via the HA server. + """ + Proxies the camera image as an mjpeg stream via the HA server. This function takes still images from the IP camera and turns them into an MJPEG stream. This means that HA can return a live video stream even with only a still image URL available. """ entity_id = path_match.group(ATTR_ENTITY_ID) + camera = component.entities.get(entity_id) - camera = None - if entity_id in component.entities.keys(): - camera = component.entities[entity_id] - - if not camera: + if camera is None: handler.send_response(HTTP_NOT_FOUND) handler.end_headers() return @@ -143,9 +131,9 @@ def setup(hass, config): # MJPEG_START_HEADER.format() 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', @@ -159,12 +147,12 @@ def setup(hass, config): handler.request.sendall( bytes('--jpgboundary\r\n', 'utf-8')) + time.sleep(0.5) + except (requests.RequestException, IOError): camera.is_streaming = False camera.update_ha_state() - camera.is_streaming = False - hass.http.register_path( 'GET', re.compile( @@ -175,7 +163,7 @@ def setup(hass, config): class Camera(Entity): - """ The base class for camera components """ + """ The base class for camera components. """ def __init__(self): self.is_streaming = False @@ -183,23 +171,23 @@ class Camera(Entity): @property # pylint: disable=no-self-use def is_recording(self): - """ Returns true if the device is recording """ + """ Returns true if the device is recording. """ return False @property # pylint: disable=no-self-use def brand(self): - """ Should return a string of the camera brand """ + """ Should return a string of the camera brand. """ return None @property # pylint: disable=no-self-use def model(self): - """ Returns string of camera model """ + """ Returns string of camera model. """ return None def camera_image(self): - """ Return bytes of camera image """ + """ Return bytes of camera image. """ raise NotImplementedError() @property diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py new file mode 100644 index 00000000000..0ad992db86d --- /dev/null +++ b/homeassistant/components/camera/demo.py @@ -0,0 +1,37 @@ +""" +homeassistant.components.camera.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Demo platform that has a fake camera. +""" +import os +from homeassistant.components.camera import Camera +import homeassistant.util.dt as dt_util + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Demo camera. """ + add_devices([ + DemoCamera('Demo camera') + ]) + + +class DemoCamera(Camera): + """ A Demo camera. """ + + def __init__(self, name): + super().__init__() + self._name = name + + def camera_image(self): + """ Return a faked still image response. """ + now = dt_util.utcnow() + + image_path = os.path.join(os.path.dirname(__file__), + 'demo_{}.jpg'.format(now.second % 4)) + with open(image_path, 'rb') as file: + return file.read() + + @property + def name(self): + """ Return the name of this device. """ + return self._name diff --git a/homeassistant/components/camera/demo_0.jpg b/homeassistant/components/camera/demo_0.jpg new file mode 100644 index 00000000000..ff87d5179f8 Binary files /dev/null and b/homeassistant/components/camera/demo_0.jpg differ diff --git a/homeassistant/components/camera/demo_1.jpg b/homeassistant/components/camera/demo_1.jpg new file mode 100644 index 00000000000..06166fffa85 Binary files /dev/null and b/homeassistant/components/camera/demo_1.jpg differ diff --git a/homeassistant/components/camera/demo_2.jpg b/homeassistant/components/camera/demo_2.jpg new file mode 100644 index 00000000000..71356479ab0 Binary files /dev/null and b/homeassistant/components/camera/demo_2.jpg differ diff --git a/homeassistant/components/camera/demo_3.jpg b/homeassistant/components/camera/demo_3.jpg new file mode 100644 index 00000000000..06166fffa85 Binary files /dev/null and b/homeassistant/components/camera/demo_3.jpg differ diff --git a/homeassistant/components/camera/foscam.py b/homeassistant/components/camera/foscam.py index 24a42cbf883..b210e1a2f1b 100644 --- a/homeassistant/components/camera/foscam.py +++ b/homeassistant/components/camera/foscam.py @@ -4,14 +4,14 @@ 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 + +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera _LOGGER = logging.getLogger(__name__) @@ -40,7 +40,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 +50,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..c81febccc86 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -4,14 +4,15 @@ 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 -from homeassistant.helpers import validate_config -from homeassistant.components.camera import DOMAIN -from homeassistant.components.camera import Camera + import requests +from requests.auth import HTTPBasicAuth + +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera _LOGGER = logging.getLogger(__name__) @@ -40,13 +41,21 @@ class GenericCamera(Camera): self._still_image_url = device_info['still_image_url'] def camera_image(self): - """ Return a still image reponse from the camera. """ + """ Return a still image response 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..0d59c8d60c7 --- /dev/null +++ b/homeassistant/components/camera/mjpeg.py @@ -0,0 +1,72 @@ +""" +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/ +""" +from contextlib import closing +import logging + +import requests +from requests.auth import HTTPBasicAuth + +from homeassistant.helpers import validate_config +from homeassistant.components.camera import DOMAIN, Camera + +_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/configurator.py b/homeassistant/components/configurator.py index 8bec580abf9..515daffc71c 100644 --- a/homeassistant/components/configurator.py +++ b/homeassistant/components/configurator.py @@ -15,7 +15,6 @@ from homeassistant.helpers import generate_entity_id from homeassistant.const import EVENT_TIME_CHANGED DOMAIN = "configurator" -DEPENDENCIES = [] ENTITY_ID_FORMAT = DOMAIN + ".{}" SERVICE_CONFIGURE = "configure" diff --git a/homeassistant/components/conversation.py b/homeassistant/components/conversation.py index f00a640232f..7cd1193448c 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 @@ -14,7 +14,6 @@ from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF) DOMAIN = "conversation" -DEPENDENCIES = [] SERVICE_PROCESS = "process" diff --git a/homeassistant/components/demo.py b/homeassistant/components/demo.py index 388a869ae0c..8a8c5ed31e5 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -10,14 +10,26 @@ 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'] + 'alarm_control_panel', + 'binary_sensor', + 'camera', + 'device_tracker', + 'light', + 'lock', + 'media_player', + 'notify', + 'rollershutter', + 'sensor', + 'switch', + 'thermostat', +] def setup(hass, config): @@ -54,23 +66,6 @@ def setup(hass, config): group.setup_group(hass, 'bedroom', [lights[0], switches[1], media_players[0]]) - # Setup IP Camera - bootstrap.setup_component( - hass, 'camera', - {'camera': { - 'platform': 'generic', - 'name': 'IP Camera', - 'still_image_url': 'http://home-assistant.io/demo/webcam.jpg', - }}) - - # Setup alarm_control_panel - bootstrap.setup_component( - hass, 'alarm_control_panel', - {'alarm_control_panel': { - 'platform': 'manual', - 'name': 'Test Alarm', - }}) - # Setup scripts bootstrap.setup_component( hass, 'script', @@ -110,25 +105,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 6296d766456..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 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..bc8e8768be0 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 @@ -98,7 +98,7 @@ class NmapDeviceScanner(object): from nmap import PortScanner, PortScannerError scanner = PortScanner() - options = "-F --host-timeout 5" + options = "-F --host-timeout 5s" if self.home_interval: boundary = dt_util.now() - self.home_interval 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..cfd6ffd55eb 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. @@ -18,8 +17,7 @@ from homeassistant.const import ( ATTR_SERVICE, ATTR_DISCOVERED) DOMAIN = "discovery" -DEPENDENCIES = [] -REQUIREMENTS = ['netdisco==0.5.1'] +REQUIREMENTS = ['netdisco==0.5.2'] SCAN_INTERVAL = 300 # seconds diff --git a/homeassistant/components/downloader.py b/homeassistant/components/downloader.py index f145fadfb71..655bf7d4eb6 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 @@ -15,7 +15,6 @@ from homeassistant.helpers import validate_config from homeassistant.util import sanitize_filename DOMAIN = "downloader" -DEPENDENCIES = [] SERVICE_DOWNLOAD_FILE = "download_file" diff --git a/homeassistant/components/ecobee.py b/homeassistant/components/ecobee.py new file mode 100644 index 00000000000..a517e2d5418 --- /dev/null +++ b/homeassistant/components/ecobee.py @@ -0,0 +1,155 @@ +""" +homeassistant.components.ecobee +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Ecobee Component + +This component adds support for Ecobee3 Wireless Thermostats. +You will need to setup developer access to your thermostat, +and create and API key on the ecobee website. + +The first time you run this component you will see a configuration +component card in Home Assistant. This card will contain a PIN code +that you will need to use to authorize access to your thermostat. You +can do this at https://www.ecobee.com/consumerportal/index.html +Click My Apps, Add application, Enter Pin and click Authorize. + +After authorizing the application click the button in the configuration +card. Now your thermostat and sensors should shown in home-assistant. + +You can use the optional hold_temp parameter to set whether or not holds +are set indefintely or until the next scheduled event. + +ecobee: + api_key: asdfasdfasdfasdfasdfaasdfasdfasdfasdf + hold_temp: True + +""" + +from datetime import timedelta +import logging +import os + +from homeassistant.loader import get_component +from homeassistant import bootstrap +from homeassistant.util import Throttle +from homeassistant.const import ( + EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED, CONF_API_KEY) + +DOMAIN = "ecobee" +DISCOVER_THERMOSTAT = "ecobee.thermostat" +DISCOVER_SENSORS = "ecobee.sensor" +NETWORK = None +HOLD_TEMP = 'hold_temp' + +REQUIREMENTS = [ + 'https://github.com/nkgilley/python-ecobee-api/archive/' + '5645f843b64ac4f6e59dfb96233a07083c5e10c1.zip#python-ecobee==0.0.3'] + +_LOGGER = logging.getLogger(__name__) + +ECOBEE_CONFIG_FILE = 'ecobee.conf' +_CONFIGURING = {} + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=180) + + +def request_configuration(network, hass, config): + """ Request configuration steps from the user. """ + configurator = get_component('configurator') + if 'ecobee' in _CONFIGURING: + configurator.notify_errors( + _CONFIGURING['ecobee'], "Failed to register, please try again.") + + return + + # pylint: disable=unused-argument + def ecobee_configuration_callback(callback_data): + """ Actions to do when our configuration callback is called. """ + network.request_tokens() + network.update() + setup_ecobee(hass, network, config) + + _CONFIGURING['ecobee'] = configurator.request_config( + hass, "Ecobee", ecobee_configuration_callback, + description=( + 'Please authorize this app at https://www.ecobee.com/consumer' + 'portal/index.html with pin code: ' + network.pin), + description_image="/static/images/config_ecobee_thermostat.png", + submit_caption="I have authorized the app." + ) + + +def setup_ecobee(hass, network, config): + """ Setup ecobee thermostat """ + # If ecobee has a PIN then it needs to be configured. + if network.pin is not None: + request_configuration(network, hass, config) + return + + if 'ecobee' in _CONFIGURING: + configurator = get_component('configurator') + configurator.request_done(_CONFIGURING.pop('ecobee')) + + # Ensure component is loaded + bootstrap.setup_component(hass, 'thermostat', config) + bootstrap.setup_component(hass, 'sensor', config) + + hold_temp = config[DOMAIN].get(HOLD_TEMP, False) + + # Fire thermostat discovery event + hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { + ATTR_SERVICE: DISCOVER_THERMOSTAT, + ATTR_DISCOVERED: {'hold_temp': hold_temp} + }) + + # Fire sensor discovery event + hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { + ATTR_SERVICE: DISCOVER_SENSORS, + ATTR_DISCOVERED: {} + }) + + +# pylint: disable=too-few-public-methods +class EcobeeData(object): + """ Gets the latest data and update the states. """ + + def __init__(self, config_file): + from pyecobee import Ecobee + self.ecobee = Ecobee(config_file) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Get the latest data from pyecobee. """ + self.ecobee.update() + _LOGGER.info("ecobee data updated successfully.") + + +def setup(hass, config): + """ + Setup Ecobee. + Will automatically load thermostat and sensor components to support + devices discovered on the network. + """ + # pylint: disable=global-statement, import-error + global NETWORK + + if 'ecobee' in _CONFIGURING: + return + + from pyecobee import config_from_file + + # Create ecobee.conf if it doesn't exist + if not os.path.isfile(hass.config.path(ECOBEE_CONFIG_FILE)): + if config[DOMAIN].get(CONF_API_KEY) is None: + _LOGGER.error("No ecobee api_key found in config.") + return + jsonconfig = {"API_KEY": config[DOMAIN].get(CONF_API_KEY)} + config_from_file(hass.config.path(ECOBEE_CONFIG_FILE), jsonconfig) + + NETWORK = EcobeeData(hass.config.path(ECOBEE_CONFIG_FILE)) + + setup_ecobee(hass, NETWORK.ecobee, config) + + return True diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index a15244ac52f..dac2041fa56 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,7 +22,7 @@ _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) @@ -54,8 +54,7 @@ def setup(hass, config): def _handle_get_root(handler, path_match, data): - """ Renders the debug interface. """ - + """ Renders the frontend. """ handler.send_response(HTTP_OK) handler.send_header('Content-type', 'text/html; charset=utf-8') handler.end_headers() @@ -66,7 +65,7 @@ def _handle_get_root(handler, path_match, data): app_url = "frontend-{}.html".format(version.VERSION) # auto login if no password was set, else check api_password param - auth = ('no_password_set' if handler.server.no_password_set + auth = ('no_password_set' if handler.server.api_password is None else data.get('api_password', '')) with open(INDEX_PATH) as template_file: @@ -74,6 +73,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")) diff --git a/homeassistant/components/frontend/index.html.template b/homeassistant/components/frontend/index.html.template index 8906e8902a0..87c5f6638a7 100644 --- a/homeassistant/components/frontend/index.html.template +++ b/homeassistant/components/frontend/index.html.template @@ -4,16 +4,13 @@ Home Assistant - - - + + + href='/static/favicon-apple-180x180.png'> - + -
- -
Initializing
-
+
- + diff --git a/homeassistant/components/frontend/mdi_version.py b/homeassistant/components/frontend/mdi_version.py new file mode 100644 index 00000000000..a8106ecd77e --- /dev/null +++ b/homeassistant/components/frontend/mdi_version.py @@ -0,0 +1,2 @@ +""" DO NOT MODIFY. Auto-generated by update_mdi script """ +VERSION = "7d76081c37634d36af21f5cc1ca79408" diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 1c753d1638e..5c9371f67e4 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 = "33a9830ccda8000eb88700de9d4cd03b" diff --git a/homeassistant/components/frontend/www_static/favicon-384x384.png b/homeassistant/components/frontend/www_static/favicon-384x384.png new file mode 100644 index 00000000000..51f67770790 Binary files /dev/null and b/homeassistant/components/frontend/www_static/favicon-384x384.png differ diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 7343bd3afd0..6e46b80ef88 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -