forked from home-assistant/core
@@ -36,10 +36,12 @@ omit =
|
||||
homeassistant/components/device_tracker/aruba.py
|
||||
homeassistant/components/device_tracker/asuswrt.py
|
||||
homeassistant/components/device_tracker/ddwrt.py
|
||||
homeassistant/components/device_tracker/geofancy.py
|
||||
homeassistant/components/device_tracker/luci.py
|
||||
homeassistant/components/device_tracker/ubus.py
|
||||
homeassistant/components/device_tracker/netgear.py
|
||||
homeassistant/components/device_tracker/nmap_tracker.py
|
||||
homeassistant/components/device_tracker/owntracks.py
|
||||
homeassistant/components/device_tracker/thomson.py
|
||||
homeassistant/components/device_tracker/tomato.py
|
||||
homeassistant/components/device_tracker/tplink.py
|
||||
@@ -48,6 +50,7 @@ omit =
|
||||
homeassistant/components/downloader.py
|
||||
homeassistant/components/keyboard.py
|
||||
homeassistant/components/light/hue.py
|
||||
homeassistant/components/light/mqtt.py
|
||||
homeassistant/components/light/limitlessled.py
|
||||
homeassistant/components/light/blinksticklight.py
|
||||
homeassistant/components/light/hyperion.py
|
||||
@@ -64,6 +67,7 @@ omit =
|
||||
homeassistant/components/notify/instapush.py
|
||||
homeassistant/components/notify/nma.py
|
||||
homeassistant/components/notify/pushbullet.py
|
||||
homeassistant/components/notify/pushetta.py
|
||||
homeassistant/components/notify/pushover.py
|
||||
homeassistant/components/notify/slack.py
|
||||
homeassistant/components/notify/smtp.py
|
||||
@@ -80,6 +84,7 @@ omit =
|
||||
homeassistant/components/sensor/glances.py
|
||||
homeassistant/components/sensor/mysensors.py
|
||||
homeassistant/components/sensor/openweathermap.py
|
||||
homeassistant/components/switch/orvibo.py
|
||||
homeassistant/components/sensor/rest.py
|
||||
homeassistant/components/sensor/rpi_gpio.py
|
||||
homeassistant/components/sensor/sabnzbd.py
|
||||
@@ -97,6 +102,7 @@ omit =
|
||||
homeassistant/components/switch/rpi_gpio.py
|
||||
homeassistant/components/switch/transmission.py
|
||||
homeassistant/components/switch/wemo.py
|
||||
homeassistant/components/thermostat/honeywell.py
|
||||
homeassistant/components/thermostat/nest.py
|
||||
homeassistant/components/thermostat/radiotherm.py
|
||||
|
||||
|
||||
+7
-7
@@ -17,19 +17,19 @@ For help on building your component, please see the [developer documentation](ht
|
||||
|
||||
After you finish adding support for your device:
|
||||
|
||||
- Update the supported devices in the `README.md` file.
|
||||
- Add any new dependencies to `requirements_all.txt`. There is no ordering right now, so just add it to the end.
|
||||
- Update the `.coveragerc` file.
|
||||
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io). It's OK to add a docstring with configuration details to the file header.
|
||||
- Add a link to the website of your device/service/component in the "examples" listing of the `README.md` file.
|
||||
- Add any new dependencies to `requirements_all.txt` if needed. There is no ordering right now, so just add it to the end of the file.
|
||||
- Update the `.coveragerc` file to exclude your platform if there are no tests available.
|
||||
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io).
|
||||
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `./script/lint`.
|
||||
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
|
||||
- Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/).
|
||||
|
||||
If you've added a component:
|
||||
If you add a platform for an existing component, there is usually no need for updating the frontend. Only if you've added a new component that should show up in the frontend, there are more steps needed:
|
||||
|
||||
- Update the file [`home-assistant-icons.html`](https://github.com/balloob/home-assistant/blob/master/homeassistant/components/frontend/www_static/polymer/resources/home-assistant-icons.html) with an icon for your domain ([pick one from this list](https://www.polymer-project.org/1.0/components/core-elements/demo.html#core-icon)).
|
||||
- Update the demo component with two states that it provides
|
||||
- Add your component to home-assistant.conf.example
|
||||
- Update the demo component with two states that it provides.
|
||||
- Add your component to `home-assistant.conf.example`.
|
||||
|
||||
Since you've updated `home-assistant-icons.html`, you've made changes to the frontend:
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat
|
||||
Examples of devices it can interface it:
|
||||
|
||||
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT
|
||||
*
|
||||
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
|
||||
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
|
||||
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv))
|
||||
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
|
||||
* Interaction with [IFTTT](https://ifttt.com/)
|
||||
|
||||
@@ -9,11 +9,12 @@ After bootstrapping you can add your own components or
|
||||
start by calling homeassistant.start_home_assistant(bus)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
import logging.handlers
|
||||
from collections import defaultdict
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import homeassistant.core as core
|
||||
import homeassistant.util.dt as date_util
|
||||
@@ -25,7 +26,7 @@ import homeassistant.components as core_components
|
||||
import homeassistant.components.group as group
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import (
|
||||
EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
|
||||
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
|
||||
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
@@ -34,6 +35,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
ATTR_COMPONENT = 'component'
|
||||
|
||||
PLATFORM_FORMAT = '{}.{}'
|
||||
ERROR_LOG_FILENAME = 'home-assistant.log'
|
||||
|
||||
|
||||
def setup_component(hass, domain, config=None):
|
||||
@@ -167,6 +169,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||
hass.config.config_dir = config_dir
|
||||
mount_local_lib_path(config_dir)
|
||||
|
||||
process_ha_config_upgrade(hass)
|
||||
process_ha_core_config(hass, config.get(core.DOMAIN, {}))
|
||||
|
||||
if enable_log:
|
||||
@@ -252,7 +255,7 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
||||
"Colorlog package not found, console coloring disabled")
|
||||
|
||||
# Log errors to a file if we have write access to file or config dir
|
||||
err_log_path = hass.config.path('home-assistant.log')
|
||||
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
|
||||
err_path_exists = os.path.isfile(err_log_path)
|
||||
|
||||
# Check if we can write to the error log if it exists or that
|
||||
@@ -280,6 +283,31 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
||||
'Unable to setup error log %s (access denied)', err_log_path)
|
||||
|
||||
|
||||
def process_ha_config_upgrade(hass):
|
||||
""" Upgrade config if necessary. """
|
||||
version_path = hass.config.path('.HA_VERSION')
|
||||
|
||||
try:
|
||||
with open(version_path, 'rt') as inp:
|
||||
conf_version = inp.readline().strip()
|
||||
except FileNotFoundError:
|
||||
# Last version to not have this file
|
||||
conf_version = '0.7.7'
|
||||
|
||||
if conf_version == __version__:
|
||||
return
|
||||
|
||||
_LOGGER.info('Upgrading config directory from %s to %s', conf_version,
|
||||
__version__)
|
||||
|
||||
lib_path = hass.config.path('lib')
|
||||
if os.path.isdir(lib_path):
|
||||
shutil.rmtree(lib_path)
|
||||
|
||||
with open(version_path, 'wt') as outp:
|
||||
outp.write(__version__)
|
||||
|
||||
|
||||
def process_ha_core_config(hass, config):
|
||||
""" Processes the [homeassistant] section from the config. """
|
||||
hac = hass.config
|
||||
|
||||
@@ -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 "<DOMAIN>.<OBJECT_ID>".
|
||||
|
||||
Each component should publish services only under its own domain.
|
||||
|
||||
"""
|
||||
import itertools as it
|
||||
import logging
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.api
|
||||
Provides a Rest API for Home Assistant.
|
||||
|
||||
For more details about the RESTful API, please refer to the documentation at
|
||||
https://home-assistant.io/developers/api.html
|
||||
https://home-assistant.io/developers/api/
|
||||
"""
|
||||
import re
|
||||
import logging
|
||||
@@ -14,13 +14,14 @@ import json
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.helpers.state import TrackStates
|
||||
import homeassistant.remote as rem
|
||||
from homeassistant.bootstrap import ERROR_LOG_FILENAME
|
||||
from homeassistant.const import (
|
||||
URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM,
|
||||
URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, URL_API_COMPONENTS,
|
||||
URL_API_CONFIG, URL_API_BOOTSTRAP,
|
||||
URL_API_CONFIG, URL_API_BOOTSTRAP, URL_API_ERROR_LOG,
|
||||
EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL,
|
||||
HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
|
||||
HTTP_UNPROCESSABLE_ENTITY)
|
||||
HTTP_UNPROCESSABLE_ENTITY, CONTENT_TYPE_TEXT_PLAIN)
|
||||
|
||||
|
||||
DOMAIN = 'api'
|
||||
@@ -89,6 +90,9 @@ def setup(hass, config):
|
||||
hass.http.register_path(
|
||||
'GET', URL_API_COMPONENTS, _handle_get_api_components)
|
||||
|
||||
hass.http.register_path('GET', URL_API_ERROR_LOG,
|
||||
_handle_get_api_error_log)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -341,6 +345,13 @@ def _handle_get_api_components(handler, path_match, data):
|
||||
handler.write_json(handler.server.hass.config.components)
|
||||
|
||||
|
||||
def _handle_get_api_error_log(handler, path_match, data):
|
||||
""" Returns the logged errors for this session. """
|
||||
error_path = handler.server.hass.config.path(ERROR_LOG_FILENAME)
|
||||
with open(error_path, 'rb') as error_log:
|
||||
handler.write_file_pointer(CONTENT_TYPE_TEXT_PLAIN, error_log)
|
||||
|
||||
|
||||
def _services_json(hass):
|
||||
""" Generate services data to JSONify. """
|
||||
return [{"domain": key, "services": value}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
"""
|
||||
homeassistant.components.browser
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Provides functionality to launch a webbrowser on the host machine.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/browser/
|
||||
"""
|
||||
|
||||
DOMAIN = "browser"
|
||||
|
||||
@@ -4,24 +4,8 @@ homeassistant.components.camera
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Component to interface with various cameras.
|
||||
|
||||
The following features are supported:
|
||||
- Returning recorded camera images and streams
|
||||
- Proxying image requests via HA for external access
|
||||
- Converting a still image url into a live video stream
|
||||
|
||||
Upcoming features
|
||||
- Recording
|
||||
- Snapshot
|
||||
- Motion Detection Recording(for supported cameras)
|
||||
- Automatic Configuration(for supported cameras)
|
||||
- Creation of child entities for supported functions
|
||||
- Collating motion event images passed via FTP into time based events
|
||||
- A service for calling camera functions
|
||||
- Camera movement(panning)
|
||||
- Zoom
|
||||
- Light/Nightvision toggling
|
||||
- Support for more devices
|
||||
- Expanded documentation
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera/
|
||||
"""
|
||||
import requests
|
||||
import logging
|
||||
@@ -103,7 +87,10 @@ def setup(hass, config):
|
||||
|
||||
if camera:
|
||||
response = camera.camera_image()
|
||||
handler.wfile.write(response)
|
||||
if response is not None:
|
||||
handler.wfile.write(response)
|
||||
else:
|
||||
handler.send_response(HTTP_NOT_FOUND)
|
||||
else:
|
||||
handler.send_response(HTTP_NOT_FOUND)
|
||||
|
||||
@@ -145,7 +132,8 @@ def setup(hass, config):
|
||||
while True:
|
||||
|
||||
img_bytes = camera.camera_image()
|
||||
|
||||
if img_bytes is None:
|
||||
continue
|
||||
headers_str = '\r\n'.join((
|
||||
'Content-length: {}'.format(len(img_bytes)),
|
||||
'Content-type: image/jpeg',
|
||||
|
||||
@@ -4,14 +4,13 @@ homeassistant.components.camera.foscam
|
||||
This component provides basic support for Foscam IP cameras.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.foscam.html
|
||||
https://home-assistant.io/components/camera.foscam/
|
||||
"""
|
||||
import logging
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.camera import DOMAIN
|
||||
from homeassistant.components.camera import Camera
|
||||
import requests
|
||||
import re
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,7 +39,7 @@ class FoscamCamera(Camera):
|
||||
self._username = device_info.get('username')
|
||||
self._password = device_info.get('password')
|
||||
self._snap_picture_url = self._base_url \
|
||||
+ 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture&usr=' \
|
||||
+ 'cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=' \
|
||||
+ self._username + '&pwd=' + self._password
|
||||
self._name = device_info.get('name', 'Foscam Camera')
|
||||
|
||||
@@ -50,17 +49,9 @@ class FoscamCamera(Camera):
|
||||
def camera_image(self):
|
||||
""" Return a still image reponse from the camera. """
|
||||
|
||||
# send the request to snap a picture
|
||||
# Send the request to snap a picture and return raw jpg data
|
||||
response = requests.get(self._snap_picture_url)
|
||||
|
||||
# parse the response to find the image file name
|
||||
|
||||
pattern = re.compile('src="[.][.]/(.*[.]jpg)"')
|
||||
filename = pattern.search(response.content.decode("utf-8")).group(1)
|
||||
|
||||
# send request for the image
|
||||
response = requests.get(self._base_url + filename)
|
||||
|
||||
return response.content
|
||||
|
||||
@property
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.camera.generic
|
||||
Support for IP Cameras.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.generic.html
|
||||
https://home-assistant.io/components/camera.generic/
|
||||
"""
|
||||
import logging
|
||||
from requests.auth import HTTPBasicAuth
|
||||
@@ -42,11 +42,19 @@ class GenericCamera(Camera):
|
||||
def camera_image(self):
|
||||
""" Return a still image reponse from the camera. """
|
||||
if self._username and self._password:
|
||||
response = requests.get(
|
||||
self._still_image_url,
|
||||
auth=HTTPBasicAuth(self._username, self._password))
|
||||
try:
|
||||
response = requests.get(
|
||||
self._still_image_url,
|
||||
auth=HTTPBasicAuth(self._username, self._password))
|
||||
except requests.exceptions.RequestException as error:
|
||||
_LOGGER.error('Error getting camera image: %s', error)
|
||||
return None
|
||||
else:
|
||||
response = requests.get(self._still_image_url)
|
||||
try:
|
||||
response = requests.get(self._still_image_url)
|
||||
except requests.exceptions.RequestException as error:
|
||||
_LOGGER.error('Error getting camera image: %s', error)
|
||||
return None
|
||||
|
||||
return response.content
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
homeassistant.components.camera.mjpeg
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for IP Cameras.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.mjpeg/
|
||||
"""
|
||||
import logging
|
||||
from requests.auth import HTTPBasicAuth
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.camera import DOMAIN
|
||||
from homeassistant.components.camera import Camera
|
||||
import requests
|
||||
from contextlib import closing
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Adds a mjpeg IP Camera. """
|
||||
if not validate_config({DOMAIN: config}, {DOMAIN: ['mjpeg_url']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
add_devices_callback([MjpegCamera(config)])
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class MjpegCamera(Camera):
|
||||
"""
|
||||
A generic implementation of an IP camera that is reachable over a URL.
|
||||
"""
|
||||
|
||||
def __init__(self, device_info):
|
||||
super().__init__()
|
||||
self._name = device_info.get('name', 'Mjpeg Camera')
|
||||
self._username = device_info.get('username')
|
||||
self._password = device_info.get('password')
|
||||
self._mjpeg_url = device_info['mjpeg_url']
|
||||
|
||||
def camera_image(self):
|
||||
""" Return a still image response from the camera. """
|
||||
|
||||
def process_response(response):
|
||||
""" Take in a response object, return the jpg from it. """
|
||||
data = b''
|
||||
for chunk in response.iter_content(1024):
|
||||
data += chunk
|
||||
jpg_start = data.find(b'\xff\xd8')
|
||||
jpg_end = data.find(b'\xff\xd9')
|
||||
if jpg_start != -1 and jpg_end != -1:
|
||||
jpg = data[jpg_start:jpg_end + 2]
|
||||
return jpg
|
||||
|
||||
if self._username and self._password:
|
||||
with closing(requests.get(self._mjpeg_url,
|
||||
auth=HTTPBasicAuth(self._username,
|
||||
self._password),
|
||||
stream=True)) as response:
|
||||
return process_response(response)
|
||||
else:
|
||||
with closing(requests.get(self._mjpeg_url,
|
||||
stream=True)) as response:
|
||||
return process_response(response)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Return the name of this device. """
|
||||
return self._name
|
||||
@@ -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
|
||||
|
||||
@@ -10,14 +10,15 @@ import homeassistant.core as ha
|
||||
import homeassistant.bootstrap as bootstrap
|
||||
import homeassistant.loader as loader
|
||||
from homeassistant.const import (
|
||||
CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME)
|
||||
CONF_PLATFORM, ATTR_ENTITY_ID)
|
||||
|
||||
DOMAIN = "demo"
|
||||
|
||||
DEPENDENCIES = ['introduction', 'conversation']
|
||||
DEPENDENCIES = ['conversation', 'introduction', 'zone']
|
||||
|
||||
COMPONENTS_WITH_DEMO_PLATFORM = [
|
||||
'switch', 'light', 'sensor', 'thermostat', 'media_player', 'notify']
|
||||
'device_tracker', 'light', 'media_player', 'notify', 'switch', 'sensor',
|
||||
'thermostat']
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
@@ -110,25 +111,6 @@ def setup(hass, config):
|
||||
}},
|
||||
]})
|
||||
|
||||
# Setup fake device tracker
|
||||
hass.states.set("device_tracker.paulus", "home",
|
||||
{ATTR_ENTITY_PICTURE:
|
||||
"http://graph.facebook.com/297400035/picture",
|
||||
ATTR_FRIENDLY_NAME: 'Paulus'})
|
||||
hass.states.set("device_tracker.anne_therese", "not_home",
|
||||
{ATTR_FRIENDLY_NAME: 'Anne Therese',
|
||||
'latitude': hass.config.latitude + 0.002,
|
||||
'longitude': hass.config.longitude + 0.002})
|
||||
|
||||
hass.states.set("group.all_devices", "home",
|
||||
{
|
||||
"auto": True,
|
||||
ATTR_ENTITY_ID: [
|
||||
"device_tracker.paulus",
|
||||
"device_tracker.anne_therese"
|
||||
]
|
||||
})
|
||||
|
||||
# Setup configurator
|
||||
configurator_ids = []
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,7 @@ Device tracker platform that supports scanning an Actiontec MI424WR
|
||||
(Verizon FIOS) router for device presence.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/device_tracker.actiontec.html
|
||||
https://home-assistant.io/components/device_tracker.actiontec/
|
||||
"""
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
@@ -17,20 +17,19 @@ import telnetlib
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.util import Throttle, convert
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.components.device_tracker import DOMAIN
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||
|
||||
# Interval in minutes to exclude devices from a scan while they are home
|
||||
CONF_HOME_INTERVAL = "home_interval"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_LEASES_REGEX = re.compile(
|
||||
r'(?P<ip>([0-9]{1,3}[\.]){3}[0-9]{1,3})' +
|
||||
r'\smac:\s(?P<mac>([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))')
|
||||
r'\smac:\s(?P<mac>([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))' +
|
||||
r'\svalid\sfor:\s(?P<timevalid>(-?\d+))' +
|
||||
r'\ssec')
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
@@ -40,9 +39,7 @@ def get_scanner(hass, config):
|
||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
scanner = ActiontecDeviceScanner(config[DOMAIN])
|
||||
|
||||
return scanner if scanner.success_init else None
|
||||
|
||||
Device = namedtuple("Device", ["mac", "ip", "last_update"])
|
||||
@@ -58,19 +55,11 @@ class ActiontecDeviceScanner(object):
|
||||
self.host = config[CONF_HOST]
|
||||
self.username = config[CONF_USERNAME]
|
||||
self.password = config[CONF_PASSWORD]
|
||||
minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0)
|
||||
self.home_interval = timedelta(minutes=minutes)
|
||||
|
||||
self.lock = threading.Lock()
|
||||
|
||||
self.last_results = []
|
||||
|
||||
# Test the router is accessible
|
||||
data = self.get_actiontec_data()
|
||||
self.success_init = data is not None
|
||||
_LOGGER.info("actiontec scanner initialized")
|
||||
if self.home_interval:
|
||||
_LOGGER.info("home_interval set to: %s", self.home_interval)
|
||||
|
||||
def scan_devices(self):
|
||||
"""
|
||||
@@ -100,27 +89,13 @@ class ActiontecDeviceScanner(object):
|
||||
return False
|
||||
|
||||
with self.lock:
|
||||
exclude_targets = set()
|
||||
exclude_target_list = []
|
||||
now = dt_util.now()
|
||||
if self.home_interval:
|
||||
for host in self.last_results:
|
||||
if host.last_update + self.home_interval > now:
|
||||
exclude_targets.add(host)
|
||||
if len(exclude_targets) > 0:
|
||||
exclude_target_list = [t.ip for t in exclude_targets]
|
||||
|
||||
actiontec_data = self.get_actiontec_data()
|
||||
if not actiontec_data:
|
||||
return False
|
||||
self.last_results = []
|
||||
for client in exclude_target_list:
|
||||
if client in actiontec_data:
|
||||
actiontec_data.pop(client)
|
||||
for name, data in actiontec_data.items():
|
||||
device = Device(data['mac'], name, now)
|
||||
self.last_results.append(device)
|
||||
self.last_results.extend(exclude_targets)
|
||||
self.last_results = [Device(data['mac'], name, now)
|
||||
for name, data in actiontec_data.items()
|
||||
if data['timevalid'] > -60]
|
||||
_LOGGER.info("actiontec scan successful")
|
||||
return True
|
||||
|
||||
@@ -153,6 +128,7 @@ class ActiontecDeviceScanner(object):
|
||||
if match is not None:
|
||||
devices[match.group('ip')] = {
|
||||
'ip': match.group('ip'),
|
||||
'mac': match.group('mac').upper()
|
||||
'mac': match.group('mac').upper(),
|
||||
'timevalid': int(match.group('timevalid'))
|
||||
}
|
||||
return devices
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
homeassistant.components.device_tracker.demo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Demo platform for the device tracker.
|
||||
|
||||
device_tracker:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@ import re
|
||||
import os
|
||||
import logging
|
||||
|
||||
from . import version
|
||||
from . import version, mdi_version
|
||||
import homeassistant.util as util
|
||||
from homeassistant.const import URL_ROOT, HTTP_OK
|
||||
from homeassistant.config import get_default_config_dir
|
||||
@@ -22,9 +22,11 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
FRONTEND_URLS = [
|
||||
URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState',
|
||||
'/devEvent']
|
||||
'/devEvent', '/devInfo']
|
||||
STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)')
|
||||
|
||||
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
""" Setup serving the frontend. """
|
||||
@@ -72,6 +74,7 @@ def _handle_get_root(handler, path_match, data):
|
||||
|
||||
template_html = template_html.replace('{{ app_url }}', app_url)
|
||||
template_html = template_html.replace('{{ auth }}', auth)
|
||||
template_html = template_html.replace('{{ icons }}', mdi_version.VERSION)
|
||||
|
||||
handler.wfile.write(template_html.encode("UTF-8"))
|
||||
|
||||
@@ -80,9 +83,10 @@ def _handle_get_static(handler, path_match, data):
|
||||
""" Returns a static file for the frontend. """
|
||||
req_file = util.sanitize_path(path_match.group('file'))
|
||||
|
||||
# Strip md5 hash out of frontend filename
|
||||
if re.match(r'^frontend-[A-Za-z0-9]{32}\.html$', req_file):
|
||||
req_file = "frontend.html"
|
||||
# Strip md5 hash out
|
||||
fingerprinted = _FINGERPRINT.match(req_file)
|
||||
if fingerprinted:
|
||||
req_file = "{}.{}".format(*fingerprinted.groups())
|
||||
|
||||
path = os.path.join(os.path.dirname(__file__), 'www_static', req_file)
|
||||
|
||||
|
||||
@@ -46,6 +46,6 @@
|
||||
</div>
|
||||
<script src='/static/webcomponents-lite.min.js'></script>
|
||||
<link rel='import' href='/static/{{ app_url }}' />
|
||||
<home-assistant auth='{{ auth }}'></home-assistant>
|
||||
<home-assistant auth='{{ auth }}' icons='{{ icons }}'></home-assistant>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
""" DO NOT MODIFY. Auto-generated by update_mdi script """
|
||||
VERSION = "38EF63D0474411E4B3CF842B2B6CFE1B"
|
||||
@@ -1,2 +1,2 @@
|
||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||
VERSION = "beb922c55bb26ea576581b453f6d7c04"
|
||||
VERSION = "b75e3c9ebd3de2dae0912a89499127a9"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ homeassistant.components.group
|
||||
Provides functionality to group devices that can be turned on or off.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/group.html
|
||||
https://home-assistant.io/components/group/
|
||||
"""
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.helpers import generate_entity_id
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.history
|
||||
Provide pre-made queries on top of the recorder component.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/history.html
|
||||
https://home-assistant.io/components/history/
|
||||
"""
|
||||
import re
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.http
|
||||
This module provides an API and a HTTP interface for debug purposes.
|
||||
|
||||
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 json
|
||||
import threading
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.ifttt
|
||||
This component enable you to trigger Maker IFTTT recipes.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/ifttt.html
|
||||
https://home-assistant.io/components/ifttt/
|
||||
"""
|
||||
import logging
|
||||
import requests
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.introduction
|
||||
Component that will help guide the user taking its first steps.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/introduction.html
|
||||
https://home-assistant.io/components/introduction/
|
||||
"""
|
||||
import logging
|
||||
|
||||
@@ -26,13 +26,13 @@ def setup(hass, config=None):
|
||||
Here are some resources to get started:
|
||||
|
||||
- Configuring Home Assistant:
|
||||
https://home-assistant.io/getting-started/configuration.html
|
||||
https://home-assistant.io/getting-started/configuration/
|
||||
|
||||
- Available components:
|
||||
https://home-assistant.io/components/
|
||||
|
||||
- Troubleshooting your configuration:
|
||||
https://home-assistant.io/getting-started/troubleshooting-configuration.html
|
||||
https://home-assistant.io/getting-started/troubleshooting-configuration/
|
||||
|
||||
- Getting help:
|
||||
https://home-assistant.io/help/
|
||||
|
||||
@@ -5,7 +5,7 @@ Connects to an ISY-994 controller and loads relevant components to control its
|
||||
devices. Also contains the base classes for ISY Sensors, Lights, and Switches.
|
||||
|
||||
For configuration details please visit the documentation for this component at
|
||||
https://home-assistant.io/components/isy994.html
|
||||
https://home-assistant.io/components/isy994/
|
||||
"""
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.keyboard
|
||||
Provides functionality to emulate keyboard presses on host machine.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/keyboard.html
|
||||
https://home-assistant.io/components/keyboard/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -1,58 +1,16 @@
|
||||
"""
|
||||
homeassistant.components.light
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Provides functionality to interact with lights.
|
||||
|
||||
It offers the following services:
|
||||
|
||||
TURN_OFF - Turns one or multiple lights off.
|
||||
|
||||
Supports following parameters:
|
||||
- transition
|
||||
Integer that represents the time the light should take to transition to
|
||||
the new state.
|
||||
- entity_id
|
||||
String or list of strings that point at entity_ids of lights.
|
||||
|
||||
TURN_ON - Turns one or multiple lights on and change attributes.
|
||||
|
||||
Supports following parameters:
|
||||
- transition
|
||||
Integer that represents the time the light should take to transition to
|
||||
the new state.
|
||||
|
||||
- entity_id
|
||||
String or list of strings that point at entity_ids of lights.
|
||||
|
||||
- profile
|
||||
String with the name of one of the built-in profiles (relax, energize,
|
||||
concentrate, reading) or one of the custom profiles defined in
|
||||
light_profiles.csv in the current working directory.
|
||||
|
||||
Light profiles define a xy color and a brightness.
|
||||
|
||||
If a profile is given and a brightness or xy color then the profile values
|
||||
will be overwritten.
|
||||
|
||||
- xy_color
|
||||
A list containing two floats representing the xy color you want the light
|
||||
to be.
|
||||
|
||||
- rgb_color
|
||||
A list containing three integers representing the xy color you want the
|
||||
light to be.
|
||||
|
||||
- brightness
|
||||
Integer between 0 and 255 representing how bright you want the light to be.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/light/
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import csv
|
||||
|
||||
from homeassistant.components import group, discovery, wink, isy994
|
||||
from homeassistant.components import group, discovery, wink, isy994, zwave
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.const import (
|
||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||
@@ -77,6 +35,7 @@ ATTR_TRANSITION = "transition"
|
||||
# lists holding color values
|
||||
ATTR_RGB_COLOR = "rgb_color"
|
||||
ATTR_XY_COLOR = "xy_color"
|
||||
ATTR_COLOR_TEMP = "color_temp"
|
||||
|
||||
# int with value 0 .. 255 representing brightness of the light
|
||||
ATTR_BRIGHTNESS = "brightness"
|
||||
@@ -92,6 +51,7 @@ FLASH_LONG = "long"
|
||||
# Apply an effect to the light, can be EFFECT_COLORLOOP
|
||||
ATTR_EFFECT = "effect"
|
||||
EFFECT_COLORLOOP = "colorloop"
|
||||
EFFECT_WHITE = "white"
|
||||
|
||||
LIGHT_PROFILES_FILE = "light_profiles.csv"
|
||||
|
||||
@@ -100,11 +60,14 @@ DISCOVERY_PLATFORMS = {
|
||||
wink.DISCOVER_LIGHTS: 'wink',
|
||||
isy994.DISCOVER_LIGHTS: 'isy994',
|
||||
discovery.SERVICE_HUE: 'hue',
|
||||
zwave.DISCOVER_LIGHTS: 'zwave',
|
||||
}
|
||||
|
||||
PROP_TO_ATTR = {
|
||||
'brightness': ATTR_BRIGHTNESS,
|
||||
'color_xy': ATTR_XY_COLOR,
|
||||
'color_temp': ATTR_COLOR_TEMP,
|
||||
'rgb_color': ATTR_RGB_COLOR,
|
||||
'xy_color': ATTR_XY_COLOR,
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -119,8 +82,8 @@ def is_on(hass, entity_id=None):
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def turn_on(hass, entity_id=None, transition=None, brightness=None,
|
||||
rgb_color=None, xy_color=None, profile=None, flash=None,
|
||||
effect=None):
|
||||
rgb_color=None, xy_color=None, color_temp=None, profile=None,
|
||||
flash=None, effect=None):
|
||||
""" Turns all or specified light on. """
|
||||
data = {
|
||||
key: value for key, value in [
|
||||
@@ -130,6 +93,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
|
||||
(ATTR_BRIGHTNESS, brightness),
|
||||
(ATTR_RGB_COLOR, rgb_color),
|
||||
(ATTR_XY_COLOR, xy_color),
|
||||
(ATTR_COLOR_TEMP, color_temp),
|
||||
(ATTR_FLASH, flash),
|
||||
(ATTR_EFFECT, effect),
|
||||
] if value is not None
|
||||
@@ -150,7 +114,7 @@ def turn_off(hass, entity_id=None, transition=None):
|
||||
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches, too-many-locals
|
||||
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
||||
def setup(hass, config):
|
||||
""" Exposes light control via statemachine and services. """
|
||||
|
||||
@@ -240,6 +204,15 @@ def setup(hass, config):
|
||||
# ValueError if value could not be converted to float
|
||||
pass
|
||||
|
||||
if ATTR_COLOR_TEMP in dat:
|
||||
# color_temp should be an int of mirads value
|
||||
colortemp = dat.get(ATTR_COLOR_TEMP)
|
||||
|
||||
# Without this check, a ctcolor with value '99' would work
|
||||
# These values are based on Philips Hue, may need ajustment later
|
||||
if isinstance(colortemp, int) and 154 <= colortemp <= 500:
|
||||
params[ATTR_COLOR_TEMP] = colortemp
|
||||
|
||||
if ATTR_RGB_COLOR in dat:
|
||||
try:
|
||||
# rgb_color should be a list containing 3 ints
|
||||
@@ -247,26 +220,17 @@ def setup(hass, config):
|
||||
|
||||
if len(rgb_color) == 3:
|
||||
params[ATTR_RGB_COLOR] = [int(val) for val in rgb_color]
|
||||
params[ATTR_XY_COLOR] = \
|
||||
color_util.color_RGB_to_xy(int(rgb_color[0]),
|
||||
int(rgb_color[1]),
|
||||
int(rgb_color[2]))
|
||||
|
||||
except (TypeError, ValueError):
|
||||
# TypeError if rgb_color is not iterable
|
||||
# ValueError if not all values can be converted to int
|
||||
pass
|
||||
|
||||
if ATTR_FLASH in dat:
|
||||
if dat[ATTR_FLASH] == FLASH_SHORT:
|
||||
params[ATTR_FLASH] = FLASH_SHORT
|
||||
if dat.get(ATTR_FLASH) in (FLASH_SHORT, FLASH_LONG):
|
||||
params[ATTR_FLASH] = dat[ATTR_FLASH]
|
||||
|
||||
elif dat[ATTR_FLASH] == FLASH_LONG:
|
||||
params[ATTR_FLASH] = FLASH_LONG
|
||||
|
||||
if ATTR_EFFECT in dat:
|
||||
if dat[ATTR_EFFECT] == EFFECT_COLORLOOP:
|
||||
params[ATTR_EFFECT] = EFFECT_COLORLOOP
|
||||
if dat.get(ATTR_EFFECT) in (EFFECT_COLORLOOP, EFFECT_WHITE):
|
||||
params[ATTR_EFFECT] = dat[ATTR_EFFECT]
|
||||
|
||||
for light in target_lights:
|
||||
light.turn_on(**params)
|
||||
@@ -297,10 +261,20 @@ class Light(ToggleEntity):
|
||||
return None
|
||||
|
||||
@property
|
||||
def color_xy(self):
|
||||
def xy_color(self):
|
||||
""" XY color value [float, float]. """
|
||||
return None
|
||||
|
||||
@property
|
||||
def rgb_color(self):
|
||||
""" RGB color value [int, int, int] """
|
||||
return None
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
""" CT color value in mirads. """
|
||||
return None
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
""" Returns device specific state attributes. """
|
||||
@@ -317,6 +291,12 @@ class Light(ToggleEntity):
|
||||
if value:
|
||||
data[attr] = value
|
||||
|
||||
if ATTR_RGB_COLOR not in data and ATTR_XY_COLOR in data and \
|
||||
ATTR_BRIGHTNESS in data:
|
||||
data[ATTR_RGB_COLOR] = color_util.color_xy_brightness_to_RGB(
|
||||
data[ATTR_XY_COLOR][0], data[ATTR_XY_COLOR][1],
|
||||
data[ATTR_BRIGHTNESS])
|
||||
|
||||
device_attr = self.device_state_attributes
|
||||
|
||||
if device_attr is not None:
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.light.blinksticklight
|
||||
Support for Blinkstick lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.blinksticklight.html
|
||||
https://home-assistant.io/components/light.blinksticklight/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -1,37 +1,40 @@
|
||||
"""
|
||||
homeassistant.components.light.demo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Demo platform that implements lights.
|
||||
|
||||
"""
|
||||
import random
|
||||
|
||||
from homeassistant.components.light import (
|
||||
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR)
|
||||
Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR, ATTR_COLOR_TEMP)
|
||||
|
||||
|
||||
LIGHT_COLORS = [
|
||||
[0.368, 0.180],
|
||||
[0.460, 0.470],
|
||||
[237, 224, 33],
|
||||
[255, 63, 111],
|
||||
]
|
||||
|
||||
LIGHT_TEMPS = [240, 380]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Find and return demo lights. """
|
||||
add_devices_callback([
|
||||
DemoLight("Bed Light", False),
|
||||
DemoLight("Ceiling Lights", True, LIGHT_COLORS[0]),
|
||||
DemoLight("Kitchen Lights", True, LIGHT_COLORS[1])
|
||||
DemoLight("Ceiling Lights", True, LIGHT_COLORS[0], LIGHT_TEMPS[1]),
|
||||
DemoLight("Kitchen Lights", True, LIGHT_COLORS[1], LIGHT_TEMPS[0])
|
||||
])
|
||||
|
||||
|
||||
class DemoLight(Light):
|
||||
""" Provides a demo switch. """
|
||||
def __init__(self, name, state, xy=None, brightness=180):
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, name, state, rgb=None, ct=None, brightness=180):
|
||||
self._name = name
|
||||
self._state = state
|
||||
self._xy = xy or random.choice(LIGHT_COLORS)
|
||||
self._rgb = rgb or random.choice(LIGHT_COLORS)
|
||||
self._ct = ct or random.choice(LIGHT_TEMPS)
|
||||
self._brightness = brightness
|
||||
|
||||
@property
|
||||
@@ -50,9 +53,14 @@ class DemoLight(Light):
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def color_xy(self):
|
||||
""" XY color value. """
|
||||
return self._xy
|
||||
def rgb_color(self):
|
||||
""" rgb color value. """
|
||||
return self._rgb
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
""" CT color temperature. """
|
||||
return self._ct
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
@@ -63,8 +71,11 @@ class DemoLight(Light):
|
||||
""" Turn the device on. """
|
||||
self._state = True
|
||||
|
||||
if ATTR_XY_COLOR in kwargs:
|
||||
self._xy = kwargs[ATTR_XY_COLOR]
|
||||
if ATTR_RGB_COLOR in kwargs:
|
||||
self._rgb = kwargs[ATTR_RGB_COLOR]
|
||||
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
self._ct = kwargs[ATTR_COLOR_TEMP]
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
|
||||
@@ -3,20 +3,24 @@ homeassistant.components.light.hue
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Hue lights.
|
||||
|
||||
https://home-assistant.io/components/light.hue.html
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.hue/
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
from datetime import timedelta
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from homeassistant.loader import get_component
|
||||
import homeassistant.util as util
|
||||
import homeassistant.util.color as color_util
|
||||
from homeassistant.const import CONF_HOST, DEVICE_DEFAULT_NAME
|
||||
from homeassistant.components.light import (
|
||||
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
|
||||
ATTR_FLASH, FLASH_LONG, FLASH_SHORT, ATTR_EFFECT,
|
||||
EFFECT_COLORLOOP)
|
||||
Light, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_COLOR_TEMP,
|
||||
ATTR_TRANSITION, ATTR_FLASH, FLASH_LONG, FLASH_SHORT,
|
||||
ATTR_EFFECT, EFFECT_COLORLOOP, ATTR_RGB_COLOR)
|
||||
|
||||
REQUIREMENTS = ['phue==0.8']
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||
@@ -30,21 +34,37 @@ _CONFIGURING = {}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _find_host_from_config(hass):
|
||||
""" Attempt to detect host based on existing configuration. """
|
||||
path = hass.config.path(PHUE_CONFIG_FILE)
|
||||
|
||||
if not os.path.isfile(path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(path) as inp:
|
||||
return next(json.loads(''.join(inp)).keys().__iter__())
|
||||
except (ValueError, AttributeError, StopIteration):
|
||||
# ValueError if can't parse as JSON
|
||||
# AttributeError if JSON value is not a dict
|
||||
# StopIteration if no keys
|
||||
return None
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Gets the Hue lights. """
|
||||
try:
|
||||
# pylint: disable=unused-variable
|
||||
import phue # noqa
|
||||
except ImportError:
|
||||
_LOGGER.exception("Error while importing dependency phue.")
|
||||
|
||||
return
|
||||
|
||||
if discovery_info is not None:
|
||||
host = urlparse(discovery_info[1]).hostname
|
||||
else:
|
||||
host = config.get(CONF_HOST, None)
|
||||
|
||||
if host is None:
|
||||
host = _find_host_from_config(hass)
|
||||
|
||||
if host is None:
|
||||
_LOGGER.error('No host found in configuration')
|
||||
return False
|
||||
|
||||
# Only act if we are not already configuring this host
|
||||
if host in _CONFIGURING:
|
||||
return
|
||||
@@ -125,6 +145,7 @@ def request_configuration(host, hass, add_devices_callback):
|
||||
|
||||
return
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def hue_configuration_callback(data):
|
||||
""" Actions to do when our configuration callback is called. """
|
||||
setup_bridge(host, hass, add_devices_callback)
|
||||
@@ -164,10 +185,15 @@ class HueLight(Light):
|
||||
return self.info['state']['bri']
|
||||
|
||||
@property
|
||||
def color_xy(self):
|
||||
def xy_color(self):
|
||||
""" XY color value. """
|
||||
return self.info['state'].get('xy')
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
""" CT color value. """
|
||||
return self.info['state'].get('ct')
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
""" True if device is on. """
|
||||
@@ -189,6 +215,12 @@ class HueLight(Light):
|
||||
|
||||
if ATTR_XY_COLOR in kwargs:
|
||||
command['xy'] = kwargs[ATTR_XY_COLOR]
|
||||
elif ATTR_RGB_COLOR in kwargs:
|
||||
command['xy'] = color_util.color_RGB_to_xy(
|
||||
*(int(val) for val in kwargs[ATTR_RGB_COLOR]))
|
||||
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
command['ct'] = kwargs[ATTR_COLOR_TEMP]
|
||||
|
||||
flash = kwargs.get(ATTR_FLASH)
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ homeassistant.components.light.hyperion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Hyperion remotes.
|
||||
|
||||
https://home-assistant.io/components/light.hyperion.html
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.hyperion/
|
||||
"""
|
||||
import logging
|
||||
import socket
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
homeassistant.components.light.isy994
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for ISY994 lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/isy994/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
"""
|
||||
homeassistant.components.light.limitlessled
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for LimitlessLED bulbs.
|
||||
|
||||
Support for LimitlessLED bulbs, also known as...
|
||||
|
||||
- EasyBulb
|
||||
- AppLight
|
||||
- AppLamp
|
||||
- MiLight
|
||||
- LEDme
|
||||
- dekolight
|
||||
- iLight
|
||||
|
||||
https://home-assistant.io/components/light.limitlessled.html
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.limitlessled/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.const import DEVICE_DEFAULT_NAME
|
||||
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS,
|
||||
ATTR_XY_COLOR)
|
||||
from homeassistant.util.color import color_RGB_to_xy
|
||||
ATTR_RGB_COLOR, ATTR_EFFECT,
|
||||
EFFECT_COLORLOOP, EFFECT_WHITE)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
REQUIREMENTS = ['ledcontroller==1.1.0']
|
||||
|
||||
COLOR_TABLE = {
|
||||
'white': [0xFF, 0xFF, 0xFF],
|
||||
'violet': [0xEE, 0x82, 0xEE],
|
||||
'royal_blue': [0x41, 0x69, 0xE1],
|
||||
'baby_blue': [0x87, 0xCE, 0xFA],
|
||||
'aqua': [0x00, 0xFF, 0xFF],
|
||||
'royal_mint': [0x7F, 0xFF, 0xD4],
|
||||
'seafoam_green': [0x2E, 0x8B, 0x57],
|
||||
'green': [0x00, 0x80, 0x00],
|
||||
'lime_green': [0x32, 0xCD, 0x32],
|
||||
'yellow': [0xFF, 0xFF, 0x00],
|
||||
'yellow_orange': [0xDA, 0xA5, 0x20],
|
||||
'orange': [0xFF, 0xA5, 0x00],
|
||||
'red': [0xFF, 0x00, 0x00],
|
||||
'pink': [0xFF, 0xC0, 0xCB],
|
||||
'fusia': [0xFF, 0x00, 0xFF],
|
||||
'lilac': [0xDA, 0x70, 0xD6],
|
||||
'lavendar': [0xE6, 0xE6, 0xFA],
|
||||
}
|
||||
|
||||
|
||||
def _distance_squared(rgb1, rgb2):
|
||||
""" Return sum of squared distances of each color part. """
|
||||
return sum((val1-val2)**2 for val1, val2 in zip(rgb1, rgb2))
|
||||
|
||||
|
||||
def _rgb_to_led_color(rgb_color):
|
||||
""" Convert an RGB color to the closest color string and color. """
|
||||
return sorted((_distance_squared(rgb_color, color), name)
|
||||
for name, color in COLOR_TABLE.items())[0][1]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Gets the LimitlessLED lights. """
|
||||
@@ -105,49 +128,15 @@ class RGBWLimitlessLED(LimitlessLED):
|
||||
super().__init__(pool, controller_id, group, name, 'rgbw')
|
||||
|
||||
self._brightness = 100
|
||||
self._xy_color = color_RGB_to_xy(255, 255, 255)
|
||||
|
||||
# Build a color table that maps an RGB color to a color string
|
||||
# recognized by LedController's set_color method
|
||||
self._color_table = [(color_RGB_to_xy(*x[0]), x[1]) for x in [
|
||||
((0xFF, 0xFF, 0xFF), 'white'),
|
||||
((0xEE, 0x82, 0xEE), 'violet'),
|
||||
((0x41, 0x69, 0xE1), 'royal_blue'),
|
||||
((0x87, 0xCE, 0xFA), 'baby_blue'),
|
||||
((0x00, 0xFF, 0xFF), 'aqua'),
|
||||
((0x7F, 0xFF, 0xD4), 'royal_mint'),
|
||||
((0x2E, 0x8B, 0x57), 'seafoam_green'),
|
||||
((0x00, 0x80, 0x00), 'green'),
|
||||
((0x32, 0xCD, 0x32), 'lime_green'),
|
||||
((0xFF, 0xFF, 0x00), 'yellow'),
|
||||
((0xDA, 0xA5, 0x20), 'yellow_orange'),
|
||||
((0xFF, 0xA5, 0x00), 'orange'),
|
||||
((0xFF, 0x00, 0x00), 'red'),
|
||||
((0xFF, 0xC0, 0xCB), 'pink'),
|
||||
((0xFF, 0x00, 0xFF), 'fusia'),
|
||||
((0xDA, 0x70, 0xD6), 'lilac'),
|
||||
((0xE6, 0xE6, 0xFA), 'lavendar'),
|
||||
]]
|
||||
self._led_color = 'white'
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def color_xy(self):
|
||||
return self._xy_color
|
||||
|
||||
def _xy_to_led_color(self, xy_color):
|
||||
""" Convert an XY color to the closest LedController color string. """
|
||||
def abs_dist_squared(p_0, p_1):
|
||||
""" Returns the absolute value of the squared distance """
|
||||
return abs((p_0[0] - p_1[0])**2 + (p_0[1] - p_1[1])**2)
|
||||
|
||||
candidates = [(abs_dist_squared(xy_color, x[0]), x[1]) for x in
|
||||
self._color_table]
|
||||
|
||||
# First candidate in the sorted list is closest to desired color:
|
||||
return sorted(candidates)[0][1]
|
||||
def rgb_color(self):
|
||||
return COLOR_TABLE[self._led_color]
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
""" Turn the device on. """
|
||||
@@ -156,13 +145,23 @@ class RGBWLimitlessLED(LimitlessLED):
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
|
||||
if ATTR_XY_COLOR in kwargs:
|
||||
self._xy_color = kwargs[ATTR_XY_COLOR]
|
||||
if ATTR_RGB_COLOR in kwargs:
|
||||
self._led_color = _rgb_to_led_color(kwargs[ATTR_RGB_COLOR])
|
||||
|
||||
self.pool.execute(self.controller_id, "set_color",
|
||||
self._xy_to_led_color(self._xy_color), self.group)
|
||||
effect = kwargs.get(ATTR_EFFECT)
|
||||
|
||||
if effect == EFFECT_COLORLOOP:
|
||||
self.pool.execute(self.controller_id, "disco", self.group)
|
||||
elif effect == EFFECT_WHITE:
|
||||
self.pool.execute(self.controller_id, "white", self.group)
|
||||
else:
|
||||
self.pool.execute(self.controller_id, "set_color",
|
||||
self._led_color, self.group)
|
||||
|
||||
# Brightness can be set independently of color
|
||||
self.pool.execute(self.controller_id, "set_brightness",
|
||||
self._brightness / 255.0, self.group)
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
"""
|
||||
homeassistant.components.light.mqtt
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Allows to configure a MQTT light.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.mqtt/
|
||||
"""
|
||||
import logging
|
||||
|
||||
import homeassistant.util.color as color_util
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
from homeassistant.components.light import (Light,
|
||||
ATTR_BRIGHTNESS, ATTR_RGB_COLOR)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "MQTT Light"
|
||||
DEFAULT_QOS = 0
|
||||
DEFAULT_PAYLOAD_ON = "on"
|
||||
DEFAULT_PAYLOAD_OFF = "off"
|
||||
DEFAULT_RGB_PATTERN = "%d,%d,%d"
|
||||
DEFAULT_OPTIMISTIC = False
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add MQTT Light. """
|
||||
|
||||
if config.get('command_topic') is None:
|
||||
_LOGGER.error("Missing required variable: command_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttLight(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
{"state_topic": config.get('state_topic'),
|
||||
"command_topic": config.get('command_topic'),
|
||||
"brightness_state_topic": config.get('brightness_state_topic'),
|
||||
"brightness_command_topic":
|
||||
config.get('brightness_command_topic'),
|
||||
"rgb_state_topic": config.get('rgb_state_topic'),
|
||||
"rgb_command_topic": config.get('rgb_command_topic')},
|
||||
config.get('rgb', None),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
{"on": config.get('payload_on', DEFAULT_PAYLOAD_ON),
|
||||
"off": config.get('payload_off', DEFAULT_PAYLOAD_OFF)},
|
||||
config.get('brightness'),
|
||||
config.get('optimistic', DEFAULT_OPTIMISTIC))])
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
|
||||
class MqttLight(Light):
|
||||
""" Provides a MQTT light. """
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, hass, name,
|
||||
topic,
|
||||
rgb, qos,
|
||||
payload,
|
||||
brightness, optimistic):
|
||||
|
||||
self._hass = hass
|
||||
self._name = name
|
||||
self._topic = topic
|
||||
self._rgb = rgb
|
||||
self._qos = qos
|
||||
self._payload = payload
|
||||
self._brightness = brightness
|
||||
self._optimistic = optimistic
|
||||
self._state = False
|
||||
self._xy = None
|
||||
|
||||
def message_received(topic, payload, qos):
|
||||
""" A new MQTT message has been received. """
|
||||
if payload == self._payload["on"]:
|
||||
self._state = True
|
||||
elif payload == self._payload["off"]:
|
||||
self._state = False
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
if self._topic["state_topic"] is None:
|
||||
# force optimistic mode
|
||||
self._optimistic = True
|
||||
else:
|
||||
# Subscribe the state_topic
|
||||
mqtt.subscribe(self._hass, self._topic["state_topic"],
|
||||
message_received, self._qos)
|
||||
|
||||
def brightness_received(topic, payload, qos):
|
||||
""" A new MQTT message for the brightness has been received. """
|
||||
self._brightness = int(payload)
|
||||
self.update_ha_state()
|
||||
|
||||
def rgb_received(topic, payload, qos):
|
||||
""" A new MQTT message has been received. """
|
||||
self._rgb = [int(val) for val in payload.split(',')]
|
||||
self._xy = color_util.color_RGB_to_xy(int(self._rgb[0]),
|
||||
int(self._rgb[1]),
|
||||
int(self._rgb[2]))
|
||||
self.update_ha_state()
|
||||
|
||||
if self._topic["brightness_state_topic"] is not None:
|
||||
mqtt.subscribe(self._hass, self._topic["brightness_state_topic"],
|
||||
brightness_received, self._qos)
|
||||
self._brightness = 255
|
||||
else:
|
||||
self._brightness = None
|
||||
|
||||
if self._topic["rgb_state_topic"] is not None:
|
||||
mqtt.subscribe(self._hass, self._topic["rgb_state_topic"],
|
||||
rgb_received, self._qos)
|
||||
self._xy = [0, 0]
|
||||
else:
|
||||
self._xy = None
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
""" Brightness of this light between 0..255. """
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def rgb_color(self):
|
||||
""" RGB color value. """
|
||||
return self._rgb
|
||||
|
||||
@property
|
||||
def color_xy(self):
|
||||
""" RGB color value. """
|
||||
return self._xy
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed for a MQTT light. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device if any. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
""" True if device is on. """
|
||||
return self._state
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
""" Turn the device on. """
|
||||
|
||||
if ATTR_RGB_COLOR in kwargs and \
|
||||
self._topic["rgb_command_topic"] is not None:
|
||||
self._rgb = kwargs[ATTR_RGB_COLOR]
|
||||
rgb = DEFAULT_RGB_PATTERN % tuple(self._rgb)
|
||||
mqtt.publish(self._hass, self._topic["rgb_command_topic"],
|
||||
rgb, self._qos)
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs and \
|
||||
self._topic["brightness_command_topic"] is not None:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
mqtt.publish(self._hass, self._topic["brightness_command_topic"],
|
||||
self._brightness, self._qos)
|
||||
|
||||
mqtt.publish(self._hass, self._topic["command_topic"],
|
||||
self._payload["on"], self._qos)
|
||||
|
||||
if self._optimistic:
|
||||
# optimistically assume that switch has changed state
|
||||
self._state = True
|
||||
self.update_ha_state()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
""" Turn the device off. """
|
||||
mqtt.publish(self._hass, self._topic["command_topic"],
|
||||
self._payload["off"], self._qos)
|
||||
|
||||
if self._optimistic:
|
||||
# optimistically assume that switch has changed state
|
||||
self._state = False
|
||||
self.update_ha_state()
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.light.rfxtrx
|
||||
Support for RFXtrx lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.rfxtrx.html
|
||||
https://home-assistant.io/components/light.rfxtrx/
|
||||
"""
|
||||
import logging
|
||||
import homeassistant.components.rfxtrx as rfxtrx
|
||||
@@ -59,6 +59,11 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
|
||||
# Check if entity exists or previously added automatically
|
||||
if entity_id in rfxtrx.RFX_DEVICES:
|
||||
_LOGGER.debug(
|
||||
"EntityID: %s light_update. Command: %s",
|
||||
entity_id,
|
||||
event.values['Command']
|
||||
)
|
||||
if event.values['Command'] == 'On'\
|
||||
or event.values['Command'] == 'Off':
|
||||
if event.values['Command'] == 'On':
|
||||
|
||||
@@ -20,6 +20,10 @@ turn_on:
|
||||
description: Color for the light in XY-format
|
||||
example: '[0.52, 0.43]'
|
||||
|
||||
color_temp:
|
||||
description: Color temperature for the light in mireds (154-500)
|
||||
example: '250'
|
||||
|
||||
brightness:
|
||||
description: Number between 0..255 indicating brightness
|
||||
example: 120
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.light.tellstick
|
||||
Support for Tellstick lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.tellstick.html
|
||||
https://home-assistant.io/components/light.tellstick/
|
||||
"""
|
||||
import logging
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
|
||||
@@ -4,15 +4,19 @@ homeassistant.components.light.vera
|
||||
Support for Vera lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.vera.html
|
||||
https://home-assistant.io/components/light.vera/
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
|
||||
from requests.exceptions import RequestException
|
||||
from homeassistant.components.switch.vera import VeraSwitch
|
||||
|
||||
REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/'
|
||||
'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip'
|
||||
'#python-vera==0.1']
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||
|
||||
REQUIREMENTS = ['https://github.com/pavoni/home-assistant-vera-api/archive/'
|
||||
'efdba4e63d58a30bc9b36d9e01e69858af9130b8.zip'
|
||||
'#python-vera==0.1.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,7 +39,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
controller = veraApi.VeraController(base_url)
|
||||
devices = []
|
||||
try:
|
||||
devices = controller.get_devices(['Switch', 'On/Off Switch'])
|
||||
devices = controller.get_devices([
|
||||
'Switch',
|
||||
'On/Off Switch',
|
||||
'Dimmable Switch'])
|
||||
except RequestException:
|
||||
# There was a network related error connecting to the vera controller
|
||||
_LOGGER.exception("Error communicating with Vera API")
|
||||
@@ -47,6 +54,28 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
exclude = extra_data.get('exclude', False)
|
||||
|
||||
if exclude is not True:
|
||||
lights.append(VeraSwitch(device, extra_data))
|
||||
lights.append(VeraLight(device, extra_data))
|
||||
|
||||
add_devices_callback(lights)
|
||||
|
||||
|
||||
class VeraLight(VeraSwitch):
|
||||
""" Represents a Vera Light, including dimmable. """
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
attr = super().state_attributes or {}
|
||||
|
||||
if self.vera_device.is_dimmable:
|
||||
attr[ATTR_BRIGHTNESS] = self.vera_device.get_brightness()
|
||||
|
||||
return attr
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
if ATTR_BRIGHTNESS in kwargs and self.vera_device.is_dimmable:
|
||||
self.vera_device.set_brightness(kwargs[ATTR_BRIGHTNESS])
|
||||
else:
|
||||
self.vera_device.switch_on()
|
||||
|
||||
self.last_command_send = time.time()
|
||||
self.is_on_status = True
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.light.wink
|
||||
Support for Wink lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.wink.html
|
||||
https://home-assistant.io/components/light.wink/
|
||||
"""
|
||||
import logging
|
||||
|
||||
@@ -13,8 +13,8 @@ from homeassistant.components.wink import WinkToggleDevice
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
|
||||
REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/'
|
||||
'c2b700e8ca866159566ecf5e644d9c297f69f257.zip'
|
||||
'#python-wink==0.1']
|
||||
'9eb39eaba0717922815e673ad1114c685839d890.zip'
|
||||
'#python-wink==0.1.1']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
"""
|
||||
homeassistant.components.light.zwave
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Z-Wave lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.zwave/
|
||||
"""
|
||||
# pylint: disable=import-error
|
||||
from openzwave.network import ZWaveNetwork
|
||||
from pydispatch import dispatcher
|
||||
|
||||
import homeassistant.components.zwave as zwave
|
||||
|
||||
from homeassistant.const import STATE_ON, STATE_OFF
|
||||
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS)
|
||||
from threading import Timer
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Find and add Z-Wave lights. """
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
|
||||
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
|
||||
|
||||
if value.command_class != zwave.COMMAND_CLASS_SWITCH_MULTILEVEL:
|
||||
return
|
||||
if value.type != zwave.TYPE_BYTE:
|
||||
return
|
||||
if value.genre != zwave.GENRE_USER:
|
||||
return
|
||||
|
||||
value.set_change_verified(False)
|
||||
add_devices([ZwaveDimmer(value)])
|
||||
|
||||
|
||||
def brightness_state(value):
|
||||
"""
|
||||
Returns the brightness and state according to the current data of given
|
||||
value.
|
||||
"""
|
||||
if value.data > 0:
|
||||
return (value.data / 99) * 255, STATE_ON
|
||||
else:
|
||||
return 255, STATE_OFF
|
||||
|
||||
|
||||
class ZwaveDimmer(Light):
|
||||
""" Provides a Z-Wave dimmer. """
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, value):
|
||||
self._value = value
|
||||
self._node = value.node
|
||||
|
||||
self._brightness, self._state = brightness_state(value)
|
||||
|
||||
# Used for value change event handling
|
||||
self._refreshing = False
|
||||
self._timer = None
|
||||
|
||||
dispatcher.connect(
|
||||
self._value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
|
||||
|
||||
def _value_changed(self, value):
|
||||
""" Called when a value has changed on the network. """
|
||||
if self._value.value_id != value.value_id:
|
||||
return
|
||||
|
||||
if self._refreshing:
|
||||
self._refreshing = False
|
||||
self._brightness, self._state = brightness_state(value)
|
||||
else:
|
||||
def _refresh_value():
|
||||
"""Used timer callback for delayed value refresh."""
|
||||
self._refreshing = True
|
||||
self._value.refresh()
|
||||
|
||||
if self._timer is not None and self._timer.isAlive():
|
||||
self._timer.cancel()
|
||||
|
||||
self._timer = Timer(2, _refresh_value)
|
||||
self._timer.start()
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed for a light. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device if any. """
|
||||
name = self._node.name or "{}".format(self._node.product_name)
|
||||
|
||||
return "{}".format(name or self._value.label)
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
""" Brightness of this light between 0..255. """
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
""" True if device is on. """
|
||||
return self._state == STATE_ON
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
""" Turn the device on. """
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
|
||||
# Zwave multilevel switches use a range of [0, 99] to control
|
||||
# brightness.
|
||||
brightness = (self._brightness / 255) * 99
|
||||
|
||||
if self._node.set_dimmer(self._value.value_id, brightness):
|
||||
self._state = STATE_ON
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
""" Turn the device off. """
|
||||
if self._node.set_dimmer(self._value.value_id, 0):
|
||||
self._state = STATE_OFF
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.logbook
|
||||
Parses events and generates a human log.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/logbook.html
|
||||
https://home-assistant.io/components/logbook/
|
||||
"""
|
||||
from datetime import timedelta
|
||||
from itertools import groupby
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
homeassistant.components.logger
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Component that will help set the level of logging for components.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/logger/
|
||||
"""
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
DOMAIN = 'logger'
|
||||
DEPENDENCIES = []
|
||||
|
||||
LOGSEVERITY = {
|
||||
'CRITICAL': 50,
|
||||
'FATAL': 50,
|
||||
'ERROR': 40,
|
||||
'WARNING': 30,
|
||||
'WARN': 30,
|
||||
'INFO': 20,
|
||||
'DEBUG': 10,
|
||||
'NOTSET': 0
|
||||
}
|
||||
|
||||
LOGGER_DEFAULT = 'default'
|
||||
LOGGER_LOGS = 'logs'
|
||||
|
||||
|
||||
class HomeAssistantLogFilter(logging.Filter):
|
||||
""" A log filter. """
|
||||
# pylint: disable=no-init,too-few-public-methods
|
||||
|
||||
def __init__(self, logfilter):
|
||||
super().__init__()
|
||||
|
||||
self.logfilter = logfilter
|
||||
|
||||
def filter(self, record):
|
||||
|
||||
# Log with filtered severity
|
||||
if LOGGER_LOGS in self.logfilter:
|
||||
for filtername in self.logfilter[LOGGER_LOGS]:
|
||||
logseverity = self.logfilter[LOGGER_LOGS][filtername]
|
||||
if record.name.startswith(filtername):
|
||||
return record.levelno >= logseverity
|
||||
|
||||
# Log with default severity
|
||||
default = self.logfilter[LOGGER_DEFAULT]
|
||||
return record.levelno >= default
|
||||
|
||||
|
||||
def setup(hass, config=None):
|
||||
""" Setup the logger component. """
|
||||
|
||||
logfilter = dict()
|
||||
|
||||
# Set default log severity
|
||||
logfilter[LOGGER_DEFAULT] = LOGSEVERITY['DEBUG']
|
||||
if LOGGER_DEFAULT in config.get(DOMAIN):
|
||||
logfilter[LOGGER_DEFAULT] = LOGSEVERITY[
|
||||
config.get(DOMAIN)[LOGGER_DEFAULT].upper()
|
||||
]
|
||||
|
||||
# Compute log severity for components
|
||||
if LOGGER_LOGS in config.get(DOMAIN):
|
||||
for key, value in config.get(DOMAIN)[LOGGER_LOGS].items():
|
||||
config.get(DOMAIN)[LOGGER_LOGS][key] = LOGSEVERITY[value.upper()]
|
||||
|
||||
logs = OrderedDict(
|
||||
sorted(
|
||||
config.get(DOMAIN)[LOGGER_LOGS].items(),
|
||||
key=lambda t: len(t[0]),
|
||||
reverse=True
|
||||
)
|
||||
)
|
||||
|
||||
logfilter[LOGGER_LOGS] = logs
|
||||
|
||||
# Set log filter for all log handler
|
||||
for handler in logging.root.handlers:
|
||||
handler.addFilter(HomeAssistantLogFilter(logfilter))
|
||||
|
||||
return True
|
||||
@@ -1,8 +1,10 @@
|
||||
"""
|
||||
homeassistant.components.media_player
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Component to interface with various media players.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player/
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.chromecast
|
||||
Provides functionality to interact with Cast devices on the network.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.cast.html
|
||||
https://home-assistant.io/components/media_player.cast/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.denon
|
||||
Provides an interface to Denon Network Receivers.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.denon.html
|
||||
https://home-assistant.io/components/media_player.denon/
|
||||
"""
|
||||
import telnetlib
|
||||
import logging
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.firetv
|
||||
Provides functionality to interact with FireTV devices.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.firetv.html
|
||||
https://home-assistant.io/components/media_player.firetv/
|
||||
"""
|
||||
import logging
|
||||
import requests
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.itunes
|
||||
Provides an interface to iTunes API.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.itunes.html
|
||||
https://home-assistant.io/components/media_player.itunes/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.kodi
|
||||
Provides an interface to the XBMC/Kodi JSON-RPC API
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.kodi.html
|
||||
https://home-assistant.io/components/media_player.kodi/
|
||||
"""
|
||||
import urllib
|
||||
import logging
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.mpd
|
||||
Provides functionality to interact with a Music Player Daemon.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.mpd.html
|
||||
https://home-assistant.io/components/media_player.mpd/
|
||||
"""
|
||||
import logging
|
||||
import socket
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.plex
|
||||
Provides an interface to the Plex API.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.plex.html
|
||||
https://home-assistant.io/components/media_player.plex/
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.sonos
|
||||
Provides an interface to Sonos players (via SoCo)
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.sonos.html
|
||||
https://home-assistant.io/components/media_player.sonos/
|
||||
"""
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.media_player.squeezebox
|
||||
Provides an interface to the Logitech SqueezeBox API
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.squeezebox.html
|
||||
https://home-assistant.io/components/media_player.squeezebox/
|
||||
"""
|
||||
import logging
|
||||
import telnetlib
|
||||
@@ -173,7 +173,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||
def volume_level(self):
|
||||
""" Volume level of the media player (0..1). """
|
||||
if 'mixer volume' in self._status:
|
||||
return int(self._status['mixer volume']) / 100.0
|
||||
return int(float(self._status['mixer volume'])) / 100.0
|
||||
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.modbus
|
||||
Modbus component, using pymodbus (python3 branch).
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/modbus.html
|
||||
https://home-assistant.io/components/modbus/
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.mqtt
|
||||
MQTT component, using paho-mqtt.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/mqtt.html
|
||||
https://home-assistant.io/components/mqtt/
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
"""
|
||||
homeassistant.components.notify
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Provides functionality to notify people.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify/
|
||||
"""
|
||||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
|
||||
import homeassistant.bootstrap as bootstrap
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.loader import get_component
|
||||
from homeassistant.helpers import config_per_platform
|
||||
|
||||
from homeassistant.const import CONF_NAME
|
||||
@@ -21,6 +23,9 @@ DEPENDENCIES = []
|
||||
ATTR_TITLE = "title"
|
||||
ATTR_TITLE_DEFAULT = "Home Assistant"
|
||||
|
||||
# Target of the notification (user, device, etc)
|
||||
ATTR_TARGET = 'target'
|
||||
|
||||
# Text to notify user of
|
||||
ATTR_MESSAGE = "message"
|
||||
|
||||
@@ -43,16 +48,15 @@ def setup(hass, config):
|
||||
|
||||
for platform, p_config in config_per_platform(config, DOMAIN, _LOGGER):
|
||||
# get platform
|
||||
notify_implementation = get_component(
|
||||
'notify.{}'.format(platform))
|
||||
notify_implementation = bootstrap.prepare_setup_platform(
|
||||
hass, config, DOMAIN, platform)
|
||||
|
||||
if notify_implementation is None:
|
||||
_LOGGER.error("Unknown notification service specified.")
|
||||
continue
|
||||
|
||||
# create platform service
|
||||
notify_service = notify_implementation.get_service(
|
||||
hass, {DOMAIN: p_config})
|
||||
notify_service = notify_implementation.get_service(hass, p_config)
|
||||
|
||||
if notify_service is None:
|
||||
_LOGGER.error("Failed to initialize notification service %s",
|
||||
@@ -68,8 +72,9 @@ def setup(hass, config):
|
||||
return
|
||||
|
||||
title = call.data.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
|
||||
target = call.data.get(ATTR_TARGET)
|
||||
|
||||
notify_service.send_message(message, title=title)
|
||||
notify_service.send_message(message, title=title, target=target)
|
||||
|
||||
# register service
|
||||
service_call_handler = partial(notify_message, notify_service)
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.notify.file
|
||||
File notification service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.file.html
|
||||
https://home-assistant.io/components/notify.file/
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
@@ -20,14 +20,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||
def get_service(hass, config):
|
||||
""" Get the file notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: ['filename',
|
||||
'timestamp']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
filename = config[DOMAIN]['filename']
|
||||
timestamp = config[DOMAIN]['timestamp']
|
||||
filename = config['filename']
|
||||
timestamp = config['timestamp']
|
||||
|
||||
return FileNotificationService(hass, filename, timestamp)
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ homeassistant.components.notify.instapush
|
||||
Instapush notification service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.instapush.html
|
||||
https://home-assistant.io/components/notify.instapush/
|
||||
"""
|
||||
import logging
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN, ATTR_TITLE, BaseNotificationService)
|
||||
@@ -21,7 +23,7 @@ _RESOURCE = 'https://api.instapush.im/v1/'
|
||||
def get_service(hass, config):
|
||||
""" Get the instapush notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: [CONF_API_KEY,
|
||||
'app_secret',
|
||||
'event',
|
||||
@@ -29,52 +31,29 @@ def get_service(hass, config):
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
headers = {'x-instapush-appid': config[CONF_API_KEY],
|
||||
'x-instapush-appsecret': config['app_secret']}
|
||||
|
||||
try:
|
||||
import requests
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import requests. "
|
||||
"Did you maybe not install the 'Requests' package?")
|
||||
|
||||
response = requests.get(_RESOURCE + 'events/list',
|
||||
headers=headers).json()
|
||||
except ValueError:
|
||||
_LOGGER.error('Unexpected answer from Instapush API.')
|
||||
return None
|
||||
|
||||
# pylint: disable=unused-variable
|
||||
try:
|
||||
response = requests.get(_RESOURCE)
|
||||
if 'error' in response:
|
||||
_LOGGER.error(response['msg'])
|
||||
return None
|
||||
|
||||
except requests.ConnectionError:
|
||||
if len([app for app in response if app['title'] == config['event']]) == 0:
|
||||
_LOGGER.error(
|
||||
"Connection error "
|
||||
"Please check if https://instapush.im is available.")
|
||||
|
||||
"No app match your given value. "
|
||||
"Please create an app at https://instapush.im")
|
||||
return None
|
||||
|
||||
instapush = requests.Session()
|
||||
headers = {'x-instapush-appid': config[DOMAIN][CONF_API_KEY],
|
||||
'x-instapush-appsecret': config[DOMAIN]['app_secret']}
|
||||
response = instapush.get(_RESOURCE + 'events/list',
|
||||
headers=headers)
|
||||
|
||||
try:
|
||||
if response.json()['error']:
|
||||
_LOGGER.error(response.json()['msg'])
|
||||
# pylint: disable=bare-except
|
||||
except:
|
||||
try:
|
||||
next(events for events in response.json()
|
||||
if events['title'] == config[DOMAIN]['event'])
|
||||
except StopIteration:
|
||||
_LOGGER.error(
|
||||
"No event match your given value. "
|
||||
"Please create an event at https://instapush.im")
|
||||
else:
|
||||
return InstapushNotificationService(
|
||||
config[DOMAIN].get(CONF_API_KEY),
|
||||
config[DOMAIN]['app_secret'],
|
||||
config[DOMAIN]['event'],
|
||||
config[DOMAIN]['tracker']
|
||||
)
|
||||
return InstapushNotificationService(
|
||||
config[CONF_API_KEY], config['app_secret'], config['event'],
|
||||
config['tracker'])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
@@ -82,9 +61,6 @@ class InstapushNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for Instapush. """
|
||||
|
||||
def __init__(self, api_key, app_secret, event, tracker):
|
||||
# pylint: disable=no-name-in-module, unused-variable
|
||||
from requests import Session
|
||||
|
||||
self._api_key = api_key
|
||||
self._app_secret = app_secret
|
||||
self._event = event
|
||||
@@ -94,8 +70,6 @@ class InstapushNotificationService(BaseNotificationService):
|
||||
'x-instapush-appsecret': self._app_secret,
|
||||
'Content-Type': 'application/json'}
|
||||
|
||||
self.instapush = Session()
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
|
||||
@@ -104,10 +78,8 @@ class InstapushNotificationService(BaseNotificationService):
|
||||
data = {"event": self._event,
|
||||
"trackers": {self._tracker: title + " : " + message}}
|
||||
|
||||
response = self.instapush.post(
|
||||
_RESOURCE + 'post',
|
||||
data=json.dumps(data),
|
||||
headers=self._headers)
|
||||
response = requests.post(_RESOURCE + 'post', data=json.dumps(data),
|
||||
headers=self._headers)
|
||||
|
||||
if response.json()['status'] == 401:
|
||||
_LOGGER.error(
|
||||
|
||||
@@ -4,11 +4,13 @@ homeassistant.components.notify.nma
|
||||
NMA (Notify My Android) notification service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.nma.html
|
||||
https://home-assistant.io/components/notify.nma/
|
||||
"""
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN, ATTR_TITLE, BaseNotificationService)
|
||||
@@ -21,31 +23,20 @@ _RESOURCE = 'https://www.notifymyandroid.com/publicapi/'
|
||||
def get_service(hass, config):
|
||||
""" Get the NMA notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: [CONF_API_KEY]},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
try:
|
||||
# pylint: disable=unused-variable
|
||||
from requests import Session
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import requests. "
|
||||
"Did you maybe not install the 'Requests' package?")
|
||||
|
||||
return None
|
||||
|
||||
nma = Session()
|
||||
response = nma.get(_RESOURCE + 'verify',
|
||||
params={"apikey": config[DOMAIN][CONF_API_KEY]})
|
||||
response = requests.get(_RESOURCE + 'verify',
|
||||
params={"apikey": config[CONF_API_KEY]})
|
||||
tree = ET.fromstring(response.content)
|
||||
|
||||
if tree[0].tag == 'error':
|
||||
_LOGGER.error("Wrong API key supplied. %s", tree[0].text)
|
||||
else:
|
||||
return NmaNotificationService(config[DOMAIN][CONF_API_KEY])
|
||||
return None
|
||||
|
||||
return NmaNotificationService(config[CONF_API_KEY])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
@@ -53,26 +44,20 @@ class NmaNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for NMA. """
|
||||
|
||||
def __init__(self, api_key):
|
||||
# pylint: disable=no-name-in-module, unused-variable
|
||||
from requests import Session
|
||||
|
||||
self._api_key = api_key
|
||||
self._data = {"apikey": self._api_key}
|
||||
|
||||
self.nma = Session()
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
|
||||
title = kwargs.get(ATTR_TITLE)
|
||||
data = {
|
||||
"apikey": self._api_key,
|
||||
"application": 'home-assistant',
|
||||
"event": kwargs.get(ATTR_TITLE),
|
||||
"description": message,
|
||||
"priority": 0,
|
||||
}
|
||||
|
||||
self._data['application'] = 'home-assistant'
|
||||
self._data['event'] = title
|
||||
self._data['description'] = message
|
||||
self._data['priority'] = 0
|
||||
|
||||
response = self.nma.get(_RESOURCE + 'notify',
|
||||
params=self._data)
|
||||
response = requests.get(_RESOURCE + 'notify', params=data)
|
||||
tree = ET.fromstring(response.content)
|
||||
|
||||
if tree[0].tag == 'error':
|
||||
|
||||
@@ -4,59 +4,118 @@ homeassistant.components.notify.pushbullet
|
||||
PushBullet platform for notify component.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.pushbullet.html
|
||||
https://home-assistant.io/components/notify.pushbullet/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN, ATTR_TITLE, BaseNotificationService)
|
||||
ATTR_TITLE, ATTR_TARGET, BaseNotificationService)
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
REQUIREMENTS = ['pushbullet.py==0.7.1']
|
||||
REQUIREMENTS = ['pushbullet.py==0.9.0']
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_service(hass, config):
|
||||
""" Get the PushBullet notification service. """
|
||||
from pushbullet import PushBullet
|
||||
from pushbullet import InvalidKeyError
|
||||
|
||||
if not validate_config(config,
|
||||
{DOMAIN: [CONF_API_KEY]},
|
||||
_LOGGER):
|
||||
if CONF_API_KEY not in config:
|
||||
_LOGGER.error("Unable to find config key '%s'", CONF_API_KEY)
|
||||
return None
|
||||
|
||||
try:
|
||||
# pylint: disable=unused-variable
|
||||
from pushbullet import PushBullet, InvalidKeyError # noqa
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import pushbullet. "
|
||||
"Did you maybe not install the 'pushbullet.py' package?")
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
return PushBulletNotificationService(config[DOMAIN][CONF_API_KEY])
|
||||
|
||||
pushbullet = PushBullet(config[CONF_API_KEY])
|
||||
except InvalidKeyError:
|
||||
_LOGGER.error(
|
||||
"Wrong API key supplied. "
|
||||
"Get it at https://www.pushbullet.com/account")
|
||||
return None
|
||||
|
||||
return PushBulletNotificationService(pushbullet)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class PushBulletNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for Pushbullet. """
|
||||
|
||||
def __init__(self, api_key):
|
||||
from pushbullet import PushBullet
|
||||
def __init__(self, pb):
|
||||
self.pushbullet = pb
|
||||
self.pbtargets = {}
|
||||
self.refresh()
|
||||
|
||||
self.pushbullet = PushBullet(api_key)
|
||||
def refresh(self):
|
||||
'''
|
||||
Refresh devices, contacts, channels, etc
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
pbtargets stores all targets available from this pushbullet instance
|
||||
into a dict. These are PB objects!. It sacrifices a bit of memory
|
||||
for faster processing at send_message
|
||||
'''
|
||||
self.pushbullet.refresh()
|
||||
self.pbtargets = {
|
||||
'device':
|
||||
{tgt.nickname: tgt for tgt in self.pushbullet.devices},
|
||||
'contact':
|
||||
{tgt.email: tgt for tgt in self.pushbullet.contacts},
|
||||
'channel':
|
||||
{tgt.channel_tag: tgt for tgt in self.pushbullet.channels},
|
||||
}
|
||||
|
||||
def send_message(self, message=None, **kwargs):
|
||||
"""
|
||||
Send a message to a specified target.
|
||||
If no target specified, a 'normal' push will be sent to all devices
|
||||
linked to the PB account.
|
||||
"""
|
||||
targets = kwargs.get(ATTR_TARGET)
|
||||
title = kwargs.get(ATTR_TITLE)
|
||||
refreshed = False
|
||||
|
||||
self.pushbullet.push_note(title, message)
|
||||
if not targets:
|
||||
# Backward compatebility, notify all devices in own account
|
||||
self.pushbullet.push_note(title, message)
|
||||
_LOGGER.info('Sent notification to self')
|
||||
return
|
||||
|
||||
# Make list if not so
|
||||
if not isinstance(targets, list):
|
||||
targets = [targets]
|
||||
|
||||
# Main loop, Process all targets specified
|
||||
for target in targets:
|
||||
|
||||
# Allow for untargeted push, combined with other types
|
||||
if target in ['device', 'device/']:
|
||||
self.pushbullet.push_note(title, message)
|
||||
_LOGGER.info('Sent notification to self')
|
||||
continue
|
||||
|
||||
try:
|
||||
ttype, tname = target.split('/', 1)
|
||||
except ValueError:
|
||||
_LOGGER.error('Invalid target syntax: %s', target)
|
||||
continue
|
||||
|
||||
# Refresh if name not found. While awaiting periodic refresh
|
||||
# solution in component, poor mans refresh ;)
|
||||
if ttype not in self.pbtargets:
|
||||
_LOGGER.error('Invalid target syntax: %s', target)
|
||||
continue
|
||||
if tname not in self.pbtargets[ttype] and not refreshed:
|
||||
self.refresh()
|
||||
refreshed = True
|
||||
|
||||
# Attempt push_note on a dict value. Keys are types & target
|
||||
# name. Dict pbtargets has all *actual* targets.
|
||||
try:
|
||||
self.pbtargets[ttype][tname].push_note(title, message)
|
||||
except KeyError:
|
||||
_LOGGER.error('No such target: %s.%s', ttype, tname)
|
||||
continue
|
||||
except self.pushbullet.errors.PushError:
|
||||
_LOGGER.error('Notify failed to: %s.%s', ttype, tname)
|
||||
continue
|
||||
_LOGGER.info('Sent notification to %s.%s', ttype, tname)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
homeassistant.components.notify.pushetta
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Pushetta platform for notify component.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.pushetta/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN, ATTR_TITLE, BaseNotificationService)
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['pushetta==1.0.15']
|
||||
|
||||
|
||||
def get_service(hass, config):
|
||||
""" Get the Pushetta notification service. """
|
||||
|
||||
from pushetta import Pushetta, exceptions
|
||||
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: [CONF_API_KEY, 'channel_name']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
try:
|
||||
pushetta = Pushetta(config[CONF_API_KEY])
|
||||
pushetta.pushMessage(config['channel_name'], "Home Assistant started")
|
||||
except exceptions.TokenValidationError:
|
||||
_LOGGER.error("Please check your access token")
|
||||
return None
|
||||
except exceptions.ChannelNotFoundError:
|
||||
_LOGGER.error("Channel '%s' not found", config['channel_name'])
|
||||
return None
|
||||
|
||||
return PushettaNotificationService(config[CONF_API_KEY],
|
||||
config['channel_name'])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class PushettaNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for Pushetta. """
|
||||
|
||||
def __init__(self, api_key, channel_name):
|
||||
|
||||
from pushetta import Pushetta
|
||||
|
||||
self._api_key = api_key
|
||||
self._channel_name = channel_name
|
||||
self.pushetta = Pushetta(self._api_key)
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
|
||||
title = kwargs.get(ATTR_TITLE)
|
||||
|
||||
self.pushetta.pushMessage(self._channel_name,
|
||||
"{} {}".format(title, message))
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.notify.pushover
|
||||
Pushover platform for notify component.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.pushover.html
|
||||
https://home-assistant.io/components/notify.pushover/
|
||||
"""
|
||||
import logging
|
||||
|
||||
@@ -21,32 +21,21 @@ _LOGGER = logging.getLogger(__name__)
|
||||
def get_service(hass, config):
|
||||
""" Get the pushover notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: ['user_key', CONF_API_KEY]},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
try:
|
||||
# pylint: disable=no-name-in-module, unused-variable
|
||||
from pushover import InitError
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import pushover. "
|
||||
"Did you maybe not install the 'python-pushover.py' package?")
|
||||
|
||||
return None
|
||||
from pushover import InitError
|
||||
|
||||
try:
|
||||
api_token = config[DOMAIN].get(CONF_API_KEY)
|
||||
return PushoverNotificationService(
|
||||
config[DOMAIN]['user_key'],
|
||||
api_token)
|
||||
|
||||
return PushoverNotificationService(config['user_key'],
|
||||
config[CONF_API_KEY])
|
||||
except InitError:
|
||||
_LOGGER.error(
|
||||
"Wrong API key supplied. "
|
||||
"Get it at https://pushover.net")
|
||||
return None
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
@@ -54,7 +43,6 @@ class PushoverNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for Pushover. """
|
||||
|
||||
def __init__(self, user_key, api_token):
|
||||
# pylint: disable=no-name-in-module, unused-variable
|
||||
from pushover import Client
|
||||
self._user_key = user_key
|
||||
self._api_token = api_token
|
||||
@@ -63,11 +51,9 @@ class PushoverNotificationService(BaseNotificationService):
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from pushover import RequestError
|
||||
title = kwargs.get(ATTR_TITLE)
|
||||
|
||||
try:
|
||||
self.pushover.send_message(message, title=title)
|
||||
self.pushover.send_message(message, title=kwargs.get(ATTR_TITLE))
|
||||
except RequestError:
|
||||
_LOGGER.exception("Could not send pushover notification")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
notify:
|
||||
description: Send a notification
|
||||
|
||||
fields:
|
||||
message:
|
||||
description: Message body of the notification.
|
||||
example: The garage door has been open for 10 minutes.
|
||||
|
||||
title:
|
||||
description: Optional title for your notification.
|
||||
example: 'Your Garage Door Friend'
|
||||
|
||||
target:
|
||||
description: Target of the notification. Optional depending on the platform
|
||||
example: platform specific
|
||||
|
||||
@@ -4,13 +4,12 @@ homeassistant.components.notify.slack
|
||||
Slack platform for notify component.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.slack.html
|
||||
https://home-assistant.io/components/notify.slack/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers import validate_config
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN, BaseNotificationService)
|
||||
from homeassistant.components.notify import DOMAIN, BaseNotificationService
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
|
||||
REQUIREMENTS = ['slacker==0.6.8']
|
||||
@@ -20,34 +19,22 @@ _LOGGER = logging.getLogger(__name__)
|
||||
# pylint: disable=unused-variable
|
||||
def get_service(hass, config):
|
||||
""" Get the slack notification service. """
|
||||
import slacker
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: ['default_channel', CONF_API_KEY]},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
try:
|
||||
# pylint: disable=no-name-in-module, unused-variable
|
||||
from slacker import Error as SlackError
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import slacker. "
|
||||
"Did you maybe not install the 'slacker.py' package?")
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
api_token = config[DOMAIN].get(CONF_API_KEY)
|
||||
|
||||
return SlackNotificationService(
|
||||
config[DOMAIN]['default_channel'],
|
||||
api_token)
|
||||
config['default_channel'],
|
||||
config[CONF_API_KEY])
|
||||
|
||||
except SlackError as ex:
|
||||
_LOGGER.error(
|
||||
except slacker.Error:
|
||||
_LOGGER.exception(
|
||||
"Slack authentication failed")
|
||||
_LOGGER.exception(ex)
|
||||
return None
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
@@ -56,6 +43,7 @@ class SlackNotificationService(BaseNotificationService):
|
||||
|
||||
def __init__(self, default_channel, api_token):
|
||||
from slacker import Slacker
|
||||
|
||||
self._default_channel = default_channel
|
||||
self._api_token = api_token
|
||||
self.slack = Slacker(self._api_token)
|
||||
@@ -63,11 +51,11 @@ class SlackNotificationService(BaseNotificationService):
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
import slacker
|
||||
|
||||
from slacker import Error as SlackError
|
||||
channel = kwargs.get('channel', self._default_channel)
|
||||
|
||||
try:
|
||||
self.slack.chat.post_message(channel, message)
|
||||
except SlackError as ex:
|
||||
except slacker.Error:
|
||||
_LOGGER.exception("Could not send slack notification")
|
||||
_LOGGER.exception(ex)
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.notify.smtp
|
||||
Mail (SMTP) notification service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.smtp.html
|
||||
https://home-assistant.io/components/notify.smtp/
|
||||
"""
|
||||
import logging
|
||||
import smtplib
|
||||
@@ -20,35 +20,31 @@ _LOGGER = logging.getLogger(__name__)
|
||||
def get_service(hass, config):
|
||||
""" Get the mail notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
{DOMAIN: ['server',
|
||||
'port',
|
||||
'sender',
|
||||
'username',
|
||||
'password',
|
||||
'recipient']},
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: ['server', 'port', 'sender', 'username',
|
||||
'password', 'recipient']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
smtp_server = config[DOMAIN]['server']
|
||||
port = int(config[DOMAIN]['port'])
|
||||
username = config[DOMAIN]['username']
|
||||
password = config[DOMAIN]['password']
|
||||
smtp_server = config['server']
|
||||
port = int(config['port'])
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
starttls = int(config['starttls'])
|
||||
|
||||
server = None
|
||||
try:
|
||||
server = smtplib.SMTP(smtp_server, port)
|
||||
server.ehlo()
|
||||
if int(config[DOMAIN]['starttls']) == 1:
|
||||
if starttls == 1:
|
||||
server.starttls()
|
||||
server.ehlo()
|
||||
|
||||
try:
|
||||
server.login(username, password)
|
||||
|
||||
except (smtplib.SMTPException, smtplib.SMTPSenderRefused) as error:
|
||||
_LOGGER.exception(error,
|
||||
"Please check your settings.")
|
||||
except (smtplib.SMTPException, smtplib.SMTPSenderRefused):
|
||||
_LOGGER.exception("Please check your settings.")
|
||||
|
||||
return None
|
||||
|
||||
@@ -66,18 +62,13 @@ def get_service(hass, config):
|
||||
|
||||
return None
|
||||
|
||||
if server:
|
||||
server.quit()
|
||||
finally:
|
||||
if server:
|
||||
server.quit()
|
||||
|
||||
return MailNotificationService(
|
||||
config[DOMAIN]['server'],
|
||||
config[DOMAIN]['port'],
|
||||
config[DOMAIN]['sender'],
|
||||
config[DOMAIN]['starttls'],
|
||||
config[DOMAIN]['username'],
|
||||
config[DOMAIN]['password'],
|
||||
config[DOMAIN]['recipient']
|
||||
)
|
||||
smtp_server, port, config['sender'], starttls, username, password,
|
||||
config['recipient'])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods, too-many-instance-attributes
|
||||
@@ -90,7 +81,7 @@ class MailNotificationService(BaseNotificationService):
|
||||
self._server = server
|
||||
self._port = port
|
||||
self._sender = sender
|
||||
self.starttls = int(starttls)
|
||||
self.starttls = starttls
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.recipient = recipient
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.notify.syslog
|
||||
Syslog notification service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.syslog.html
|
||||
https://home-assistant.io/components/notify.syslog/
|
||||
"""
|
||||
import logging
|
||||
import syslog
|
||||
@@ -52,16 +52,14 @@ PRIORITIES = {5: syslog.LOG_EMERG,
|
||||
def get_service(hass, config):
|
||||
""" Get the mail notification service. """
|
||||
|
||||
if not validate_config(config,
|
||||
{DOMAIN: ['facility',
|
||||
'option',
|
||||
'priority']},
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: ['facility', 'option', 'priority']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
_facility = FACILITIES.get(config[DOMAIN]['facility'], 40)
|
||||
_option = OPTIONS.get(config[DOMAIN]['option'], 10)
|
||||
_priority = PRIORITIES.get(config[DOMAIN]['priority'], -1)
|
||||
_facility = FACILITIES.get(config['facility'], 40)
|
||||
_option = OPTIONS.get(config['option'], 10)
|
||||
_priority = PRIORITIES.get(config['priority'], -1)
|
||||
|
||||
return SyslogNotificationService(_facility, _option, _priority)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ homeassistant.components.notify.telegram
|
||||
Telegram platform for notify component.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/notify.telegram.html
|
||||
https://home-assistant.io/components/notify.telegram/
|
||||
"""
|
||||
import logging
|
||||
import urllib
|
||||
@@ -16,35 +16,27 @@ from homeassistant.const import CONF_API_KEY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import telegram
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import python-telegram-bot. "
|
||||
"Did you maybe not install the 'python-telegram-bot' package?")
|
||||
|
||||
REQUIREMENTS = ['python-telegram-bot==2.8.7']
|
||||
|
||||
|
||||
def get_service(hass, config):
|
||||
""" Get the Telegram notification service. """
|
||||
import telegram
|
||||
|
||||
if not validate_config(config,
|
||||
if not validate_config({DOMAIN: config},
|
||||
{DOMAIN: [CONF_API_KEY, 'chat_id']},
|
||||
_LOGGER):
|
||||
return None
|
||||
|
||||
try:
|
||||
bot = telegram.Bot(token=config[DOMAIN][CONF_API_KEY])
|
||||
bot = telegram.Bot(token=config[CONF_API_KEY])
|
||||
username = bot.getMe()['username']
|
||||
_LOGGER.info("Telegram bot is '%s'.", username)
|
||||
except urllib.error.HTTPError:
|
||||
_LOGGER.error("Please check your access token.")
|
||||
return None
|
||||
|
||||
return TelegramNotificationService(
|
||||
config[DOMAIN][CONF_API_KEY],
|
||||
config[DOMAIN]['chat_id'])
|
||||
return TelegramNotificationService(config[CONF_API_KEY], config['chat_id'])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
@@ -52,12 +44,15 @@ class TelegramNotificationService(BaseNotificationService):
|
||||
""" Implements notification service for Telegram. """
|
||||
|
||||
def __init__(self, api_key, chat_id):
|
||||
import telegram
|
||||
|
||||
self._api_key = api_key
|
||||
self._chat_id = chat_id
|
||||
self.bot = telegram.Bot(token=self._api_key)
|
||||
|
||||
def send_message(self, message="", **kwargs):
|
||||
""" Send a message to a user. """
|
||||
import telegram
|
||||
|
||||
title = kwargs.get(ATTR_TITLE)
|
||||
|
||||
@@ -65,4 +60,4 @@ class TelegramNotificationService(BaseNotificationService):
|
||||
self.bot.sendMessage(chat_id=self._chat_id,
|
||||
text=title + " " + message)
|
||||
except telegram.error.TelegramError:
|
||||
_LOGGER.error("Your chat id '%s' is not valid.", self._chat_id)
|
||||
_LOGGER.exception("Error sending message.")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user