diff --git a/.coveragerc b/.coveragerc index 8fa465898af..f19e37d00a1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -50,6 +50,7 @@ omit = homeassistant/components/downloader.py homeassistant/components/keyboard.py homeassistant/components/light/hue.py + homeassistant/components/light/mqtt.py homeassistant/components/light/limitlessled.py homeassistant/components/light/blinksticklight.py homeassistant/components/light/hyperion.py @@ -83,6 +84,7 @@ omit = homeassistant/components/sensor/glances.py homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/openweathermap.py + homeassistant/components/switch/orvibo.py homeassistant/components/sensor/rest.py homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py 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 cbc8ec5a633..4d68227d4d0 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -9,11 +9,12 @@ After bootstrapping you can add your own components or start by calling homeassistant.start_home_assistant(bus) """ -import os -import sys +from collections import defaultdict import logging import logging.handlers -from collections import defaultdict +import os +import shutil +import sys import homeassistant.core as core import homeassistant.util.dt as date_util @@ -25,7 +26,7 @@ import homeassistant.components as core_components import homeassistant.components.group as group from homeassistant.helpers.entity import Entity from homeassistant.const import ( - EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, + __version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE, TEMP_CELCIUS, TEMP_FAHRENHEIT) @@ -168,6 +169,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True, hass.config.config_dir = config_dir mount_local_lib_path(config_dir) + process_ha_config_upgrade(hass) process_ha_core_config(hass, config.get(core.DOMAIN, {})) if enable_log: @@ -281,6 +283,31 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None): 'Unable to setup error log %s (access denied)', err_log_path) +def process_ha_config_upgrade(hass): + """ Upgrade config if necessary. """ + version_path = hass.config.path('.HA_VERSION') + + try: + with open(version_path, 'rt') as inp: + conf_version = inp.readline().strip() + except FileNotFoundError: + # Last version to not have this file + conf_version = '0.7.7' + + if conf_version == __version__: + return + + _LOGGER.info('Upgrading config directory from %s to %s', conf_version, + __version__) + + lib_path = hass.config.path('lib') + if os.path.isdir(lib_path): + shutil.rmtree(lib_path) + + with open(version_path, 'wt') as outp: + outp.write(__version__) + + def process_ha_core_config(hass, config): """ Processes the [homeassistant] section from the config. """ hac = hass.config diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..2ef9e83cc30 --- /dev/null +++ b/homeassistant/components/binary_sensor/__init__.py @@ -0,0 +1,50 @@ +""" +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' +DEPENDENCIES = [] +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/demo.py b/homeassistant/components/binary_sensor/demo.py new file mode 100644 index 00000000000..a24b893c610 --- /dev/null +++ b/homeassistant/components/binary_sensor/demo.py @@ -0,0 +1,43 @@ +""" +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('Window Bathroom', True, None), + DemoBinarySensor('Floor Basement', False, None), + ]) + + +class DemoBinarySensor(BinarySensorDevice): + """ A Demo binary sensor. """ + + def __init__(self, name, state, icon=None): + self._name = name + self._state = state + self._icon = icon + + @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 icon(self): + """ Returns the icon to use for device if any. """ + return self._icon + + @property + def is_on(self): + """ True if the binary sensor is on. """ + return self._state diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 01c6c1b6e03..ae5fe28beac 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -58,7 +58,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, @@ -87,7 +87,10 @@ def setup(hass, config): if camera: response = camera.camera_image() - handler.wfile.write(response) + if response is not None: + handler.wfile.write(response) + else: + handler.send_response(HTTP_NOT_FOUND) else: handler.send_response(HTTP_NOT_FOUND) @@ -98,7 +101,8 @@ 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. @@ -129,7 +133,8 @@ def setup(hass, config): while True: img_bytes = camera.camera_image() - + if img_bytes is None: + continue headers_str = '\r\n'.join(( 'Content-length: {}'.format(len(img_bytes)), 'Content-type: image/jpeg', @@ -159,7 +164,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 @@ -167,23 +172,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..fc3ec263143 --- /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 random import randint +from homeassistant.components.camera import Camera + + +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. """ + + image_path = os.path.join(os.path.dirname(__file__), + 'demo_{}.png'.format(randint(1, 5))) + with open(image_path, 'rb') as file: + output = file.read() + return output + + @property + def name(self): + """ Return the name of this device. """ + return self._name diff --git a/homeassistant/components/camera/demo_1.png b/homeassistant/components/camera/demo_1.png new file mode 100644 index 00000000000..fc681fccecd Binary files /dev/null and b/homeassistant/components/camera/demo_1.png differ diff --git a/homeassistant/components/camera/demo_2.png b/homeassistant/components/camera/demo_2.png new file mode 100644 index 00000000000..255cd5c45d4 Binary files /dev/null and b/homeassistant/components/camera/demo_2.png differ diff --git a/homeassistant/components/camera/demo_3.png b/homeassistant/components/camera/demo_3.png new file mode 100644 index 00000000000..b54c1ffb57c Binary files /dev/null and b/homeassistant/components/camera/demo_3.png differ diff --git a/homeassistant/components/camera/demo_4.png b/homeassistant/components/camera/demo_4.png new file mode 100644 index 00000000000..4be36ee42d0 Binary files /dev/null and b/homeassistant/components/camera/demo_4.png differ diff --git a/homeassistant/components/camera/demo_5.png b/homeassistant/components/camera/demo_5.png new file mode 100644 index 00000000000..874b95ef6a5 Binary files /dev/null and b/homeassistant/components/camera/demo_5.png differ diff --git a/homeassistant/components/camera/generic.py b/homeassistant/components/camera/generic.py index 55fa4ec913f..7e9542908d5 100644 --- a/homeassistant/components/camera/generic.py +++ b/homeassistant/components/camera/generic.py @@ -40,13 +40,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/demo.py b/homeassistant/components/demo.py index 7c873e834bd..547a1d54a03 100644 --- a/homeassistant/components/demo.py +++ b/homeassistant/components/demo.py @@ -18,7 +18,7 @@ DEPENDENCIES = ['conversation', 'introduction', 'zone'] COMPONENTS_WITH_DEMO_PLATFORM = [ 'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor', - 'thermostat'] + 'thermostat', 'camera', 'binary_sensor'] def setup(hass, config): diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py index 1bde9a0b376..b90e1ee4448 100644 --- a/homeassistant/components/device_tracker/asuswrt.py +++ b/homeassistant/components/device_tracker/asuswrt.py @@ -134,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, @@ -151,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/frontend/version.py b/homeassistant/components/frontend/version.py index 924e46ae814..0199b05156e 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 = "75532015507fd544f46081ec0eeb5004" +VERSION = "dff74f773ea8b0356b0bd8130ed6f0cf" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 5ae7ce0b002..b2a2eb97b3e 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -1,6 +1,6 @@ -