Merge pull request #1365 from balloob/dev

0.14
This commit is contained in:
Paulus Schoutsen 2016-02-27 16:21:54 -08:00
commit 50bf147d73
384 changed files with 8494 additions and 3902 deletions

View File

@ -39,6 +39,9 @@ omit =
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/wemo.py
homeassistant/components/*/wemo.py
homeassistant/components/wink.py
homeassistant/components/*/wink.py
@ -66,7 +69,10 @@ omit =
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
homeassistant/components/camera/*
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/foscam.py
homeassistant/components/camera/generic.py
homeassistant/components/camera/mjpeg.py
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
@ -110,6 +116,7 @@ omit =
homeassistant/components/notify/pushetta.py
homeassistant/components/notify/pushover.py
homeassistant/components/notify/rest.py
homeassistant/components/notify/sendgrid.py
homeassistant/components/notify/slack.py
homeassistant/components/notify/smtp.py
homeassistant/components/notify/syslog.py
@ -119,6 +126,7 @@ omit =
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/bitcoin.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/deutsche_bahn.py
homeassistant/components/sensor/dht.py
homeassistant/components/sensor/dweet.py
homeassistant/components/sensor/efergy.py
@ -126,10 +134,12 @@ omit =
homeassistant/components/sensor/forecast.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/netatmo.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/onewire.py
homeassistant/components/sensor/openweathermap.py
homeassistant/components/sensor/rest.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/sensor/steam_online.py
homeassistant/components/sensor/speedtest.py
homeassistant/components/sensor/swiss_public_transport.py
homeassistant/components/sensor/systemmonitor.py
@ -141,15 +151,14 @@ omit =
homeassistant/components/sensor/worldclock.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/edimax.py
homeassistant/components/switch/dlink.py
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wemo.py
homeassistant/components/thermostat/heatmiser.py
homeassistant/components/thermostat/homematic.py
homeassistant/components/thermostat/honeywell.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py

31
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,31 @@
**Home Assistant release (`hass --version`):**
**Python release (`python3 --version`):**
**Component/platform:**
**Description of problem:**
**Expected:**
**Problem-relevant `configuration.yaml` entries and steps to reproduce:**
```yaml
```
1.
2.
3.
**Traceback (if applicable):**
```bash
```
**Additional info:**

28
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,28 @@
**Description:**
**Related issue (if applicable):** #
**Example entry for `configuration.yaml` (if applicable):**
```yaml
```
**Checklist:**
- [ ] Local tests with `tox` ran successfully.
- [ ] No CI failures. **Your PR cannot be merged unless CI is green!**
- [ ] [Fork is up to date][fork] and was rebased on the `dev` branch before creating the PR.
- If code communicates with devices:
- [ ] 3rd party library/libraries for communication is/are added as dependencies via the `REQUIREMENTS` variable ([example][ex-requir]).
- [ ] 3rd party dependencies are imported inside functions that use them ([example][ex-import]).
- [ ] `requirements_all.txt` is up-to-date, `script/gen_requirements_all.py` ran and the updated file is included in the PR.
- [ ] New files were added to `.coveragerc`.
- If the code does not depend on external Python module:
- [ ] Tests to verify that the code works are included.
- [ ] [Commits will be squashed][squash] when the PR is ready to be merged.
[fork]: http://stackoverflow.com/a/7244456
[squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit
[ex-requir]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L16
[ex-import]: https://github.com/balloob/home-assistant/blob/dev/homeassistant/components/keyboard.py#L51

View File

@ -1,15 +1,19 @@
sudo: false
language: python
matrix:
fast_finish: true
include:
- python: "3.4"
env: TOXENV=py34
- python: "3.4"
env: TOXENV=requirements
- python: "3.5"
env: TOXENV=lint
- python: "3.5"
env: TOXENV=py35
cache:
directories:
- $HOME/.cache/pip
# - "$HOME/virtualenv/python$TRAVIS_PYTHON_VERSION"
python:
- 3.4
- 3.5
install:
- "true"
script:
- script/cibuild
matrix:
fast_finish: true
install: pip install -U tox coveralls
language: python
script: tox
after_success: coveralls

View File

@ -6,7 +6,7 @@ The process is straight-forward.
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Check it with ``pylint`` and ``flake8``.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should read the next sections and get more details.
@ -17,12 +17,13 @@ For help on building your component, please see the [developer documentation](ht
After you finish adding support for your device:
- Check that all dependencies are included via the `REQUIREMENTS` variable in your platform/component and only imported inside functions that use them.
- Add any new dependencies to `requirements_all.txt` if needed. Use `script/gen_requirements_all.py`.
- Update the `.coveragerc` file to exclude your platform if there are no tests available.
- Update the `.coveragerc` file to exclude your platform if there are no tests available or your new code uses a 3rd party library for communication with the device/service/sensor.
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). It's OK to just add a docstring with configuration details (sample entry for `configuration.yaml` file and alike) to the file header as a start. Visit the [website documentation](https://home-assistant.io/developers/website/) for further information on contributing to [home-assistant.io](https://github.com/balloob/home-assistant.io).
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `./script/lint`.
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `tox` or `script/lint`.
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
- Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/).
- Check for comments and suggestions on your Pull Request and keep an eye on the [CI output](https://travis-ci.org/balloob/home-assistant/).
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:
@ -66,6 +67,21 @@ The frontend is composed of [Polymer](https://www.polymer-project.org) web-compo
When you are done with development and ready to commit your changes, run `build_frontend`, set `development=0` in your config and validate that everything still works.
## Testing your code
To test your code before submission, used the `tox` tool.
```shell
> pip install -U tox
> tox
```
This will run unit tests against python 3.4 and 3.5 (if both are available locally), as well as run a set of tests which validate `pep8` and `pylint` style of the code.
You can optionally run tests on only one tox target using the `-e` option to select an environment.
For instance `tox -e lint` will run the linters only, `tox -e py34` will run unit tests only on python 3.4.
### Notes on PyLint and PEP8 validation
In case a PyLint warning cannot be avoided, add a comment to disable the PyLint check for that line. This can be done using the format `# pylint: disable=YOUR-ERROR-NAME`. Example of an unavoidable PyLint warning is if you do not use the passed in datetime if you're listening for time change.

View File

@ -6,6 +6,8 @@ VOLUME /config
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN pip3 install --no-cache-dir colorlog
# For the nmap tracker
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools && \

View File

@ -1,6 +1,5 @@
"""
custom_components.example
~~~~~~~~~~~~~~~~~~~~~~~~~
Example of a custom component.
Example component to target an entity_id to:
- turn it on at 7AM in the morning
@ -37,21 +36,21 @@ import homeassistant.components as core
from homeassistant.components import device_tracker
from homeassistant.components import light
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "example"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
# We depend on group because group will be loaded after all the components that
# initialize devices have been setup.
DEPENDENCIES = ['group', 'device_tracker', 'light']
# Configuration key for the entity id we are targetting
# Configuration key for the entity id we are targeting.
CONF_TARGET = 'target'
# Variable for storing configuration parameters
# Variable for storing configuration parameters.
TARGET_ID = None
# Name of the service that we expose
# Name of the service that we expose.
SERVICE_FLASH = 'flash'
# Shortcut for the logger
@ -59,16 +58,16 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Setup example component. """
"""Setup example component."""
global TARGET_ID
# Validate that all required config options are given
# Validate that all required config options are given.
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
return False
TARGET_ID = config[DOMAIN][CONF_TARGET]
# Validate that the target entity id exists
# Validate that the target entity id exists.
if hass.states.get(TARGET_ID) is None:
_LOGGER.error("Target entity id %s does not exist",
TARGET_ID)
@ -78,13 +77,13 @@ def setup(hass, config):
TARGET_ID = None
return False
# Tell the bootstrapper that we initialized successfully
# Tell the bootstrapper that we initialized successfully.
return True
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
def track_devices(hass, entity_id, old_state, new_state):
""" Called when the group.all devices change state. """
"""Called when the group.all devices change state."""
# If the target id is not set, return
if not TARGET_ID:
return
@ -94,7 +93,7 @@ def track_devices(hass, entity_id, old_state, new_state):
core.turn_on(hass, TARGET_ID)
# If all people leave the house and the entity is on, turn it off
# If all people leave the house and the entity is on, turn it off.
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
core.turn_off(hass, TARGET_ID)
@ -116,7 +115,7 @@ def wake_up(hass, now):
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
def all_lights_off(hass, entity_id, old_state, new_state):
""" If all lights turn off, turn off. """
"""If all lights turn off, turn off."""
if not TARGET_ID:
return

View File

@ -1,7 +1,7 @@
"""
custom_components.hello_world
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Implements the bare minimum that a component should implement.
The "hello world" custom component.
This component implements the bare minimum that a component should implement.
Configuration:
@ -11,18 +11,18 @@ configuration.yaml file.
hello_world:
"""
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "hello_world"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
DEPENDENCIES = []
def setup(hass, config):
""" Setup our skeleton component. """
"""Setup our skeleton component."""
# States are in the format DOMAIN.OBJECT_ID
# States are in the format DOMAIN.OBJECT_ID.
hass.states.set('hello_world.Hello_World', 'Works!')
# return boolean to indicate that initialization was successful
# Return boolean to indicate that initialization was successfully.
return True

View File

@ -1,6 +1,6 @@
"""
custom_components.mqtt_example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example of a custom MQTT component.
Shows how to communicate with MQTT. Follows a topic on MQTT and updates the
state of an entity to the last message received on that topic.
@ -15,45 +15,41 @@ configuration.yaml file.
mqtt_example:
topic: home-assistant/mqtt_example
"""
import homeassistant.loader as loader
# The domain of your component. Should be equal to the name of your component
# The domain of your component. Should be equal to the name of your component.
DOMAIN = "mqtt_example"
# List of component names (string) your component depends upon
# List of component names (string) your component depends upon.
DEPENDENCIES = ['mqtt']
CONF_TOPIC = 'topic'
DEFAULT_TOPIC = 'home-assistant/mqtt_example'
def setup(hass, config):
""" Setup our mqtt_example component. """
"""Setup the MQTT example component."""
mqtt = loader.get_component('mqtt')
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
entity_id = 'mqtt_example.last_message'
# Listen to a message on MQTT
# Listen to a message on MQTT.
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
"""A new MQTT message has been received."""
hass.states.set(entity_id, payload)
mqtt.subscribe(hass, topic, message_received)
hass.states.set(entity_id, 'No messages')
# Service to publish a message on MQTT
# Service to publish a message on MQTT.
def set_state_service(call):
""" Service to send a message. """
"""Service to send a message."""
mqtt.publish(hass, topic, call.data.get('new_state'))
# Register our service with Home Assistant
# Register our service with Home Assistant.
hass.services.register(DOMAIN, 'set_state', set_state_service)
# return boolean to indicate that initialization was successful
# Return boolean to indicate that initialization was successfully.
return True

View File

@ -1,18 +1,18 @@
""" Starts home assistant. """
from __future__ import print_function
from multiprocessing import Process
import argparse
import os
import signal
import sys
import threading
import os
import argparse
import time
from multiprocessing import Process
from homeassistant import bootstrap
import homeassistant.config as config_util
from homeassistant.const import (__version__, EVENT_HOMEASSISTANT_START,
RESTART_EXIT_CODE)
from homeassistant import bootstrap
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, RESTART_EXIT_CODE, __version__)
def validate_python():

View File

@ -1,37 +1,31 @@
"""
homeassistant.bootstrap
~~~~~~~~~~~~~~~~~~~~~~~
Provides methods to bootstrap a home assistant instance.
"""Provides methods to bootstrap a home assistant instance."""
Each method will return a tuple (bus, statemachine).
After bootstrapping you can add your own components or
start by calling homeassistant.start_home_assistant(bus)
"""
from collections import defaultdict
import logging
import logging.handlers
import os
import shutil
import sys
from collections import defaultdict
from threading import RLock
import homeassistant.core as core
import homeassistant.util.dt as date_util
import homeassistant.util.package as pkg_util
import homeassistant.util.location as loc_util
import homeassistant.config as config_util
import homeassistant.loader as loader
import homeassistant.components as core_components
import homeassistant.components.group as group
import homeassistant.config as config_util
import homeassistant.core as core
import homeassistant.loader as loader
import homeassistant.util.dt as date_util
import homeassistant.util.location as loc_util
import homeassistant.util.package as pkg_util
from homeassistant.const import (
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
TEMP_CELCIUS, TEMP_FAHRENHEIT, __version__)
from homeassistant.helpers import event_decorators, service
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
__version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, CONF_CUSTOMIZE,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
_LOGGER = logging.getLogger(__name__)
_SETUP_LOCK = RLock()
_CURRENT_SETUP = []
ATTR_COMPONENT = 'component'
@ -78,42 +72,57 @@ def _handle_requirements(hass, component, name):
def _setup_component(hass, domain, config):
""" Setup a component for Home Assistant. """
"""Setup a component for Home Assistant."""
# pylint: disable=too-many-return-statements
if domain in hass.config.components:
return True
component = loader.get_component(domain)
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
if dep not in hass.config.components]
with _SETUP_LOCK:
# It might have been loaded while waiting for lock
if domain in hass.config.components:
return True
if missing_deps:
_LOGGER.error(
'Not initializing %s because not all dependencies loaded: %s',
domain, ", ".join(missing_deps))
return False
if not _handle_requirements(hass, component, domain):
return False
try:
if not component.setup(hass, config):
_LOGGER.error('component %s failed to initialize', domain)
if domain in _CURRENT_SETUP:
_LOGGER.error('Attempt made to setup %s during setup of %s',
domain, domain)
return False
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error during setup of component %s', domain)
return False
hass.config.components.append(component.DOMAIN)
component = loader.get_component(domain)
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
if dep not in hass.config.components]
# Assumption: if a component does not depend on groups
# it communicates with devices
if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
hass.pool.add_worker()
if missing_deps:
_LOGGER.error(
'Not initializing %s because not all dependencies loaded: %s',
domain, ", ".join(missing_deps))
return False
hass.bus.fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN})
if not _handle_requirements(hass, component, domain):
return False
return True
_CURRENT_SETUP.append(domain)
try:
if not component.setup(hass, config):
_LOGGER.error('component %s failed to initialize', domain)
return False
except Exception: # pylint: disable=broad-except
_LOGGER.exception('Error during setup of component %s', domain)
return False
finally:
_CURRENT_SETUP.remove(domain)
hass.config.components.append(component.DOMAIN)
# Assumption: if a component does not depend on groups
# it communicates with devices
if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
hass.pool.add_worker()
hass.bus.fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN})
return True
def prepare_setup_platform(hass, config, domain, platform_name):

View File

@ -21,7 +21,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
verisure.DISCOVER_SENSORS: 'verisure'
verisure.DISCOVER_ALARMS: 'verisure'
}
SERVICE_TO_METHOD = {

View File

@ -9,18 +9,16 @@ https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import (
STATE_UNKNOWN,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['https://github.com/Xorso/pyalarmdotcom'
'/archive/0.0.7.zip'
'#pyalarmdotcom==0.0.7']
'/archive/0.1.1.zip'
'#pyalarmdotcom==0.1.1']
DEFAULT_NAME = 'Alarm.com'

View File

@ -1,13 +1,14 @@
"""
homeassistant.components.alarm_control_panel.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has two fake alarm control panels.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.components.alarm_control_panel.manual as manual
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo alarm control panels. """
"""Setup the Demo alarm control panel platform."""
add_devices([
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10),
])

View File

@ -6,15 +6,15 @@ 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/
"""
import logging
import datetime
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.helpers.event import track_point_in_time
import homeassistant.util.dt as dt_util
import logging
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
from homeassistant.helpers.event import track_point_in_time
_LOGGER = logging.getLogger(__name__)

View File

@ -7,11 +7,11 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.mqtt/
"""
import logging
import homeassistant.components.mqtt as mqtt
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.mqtt as mqtt
from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)

View File

@ -7,14 +7,15 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.nx584/
"""
import logging
import requests
from homeassistant.const import (STATE_UNKNOWN, STATE_ALARM_DISARMED,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY)
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
REQUIREMENTS = ['pynx584==0.1']
REQUIREMENTS = ['pynx584==0.2']
_LOGGER = logging.getLogger(__name__)

View File

@ -8,12 +8,12 @@ https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.verisure import HUB as hub
from homeassistant.const import (
STATE_UNKNOWN,
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
@ -21,18 +21,13 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
alarms = []
alarms.extend([
VerisureAlarm(value)
for value in verisure.ALARM_STATUS.values()
if verisure.SHOW_ALARM
])
if int(hub.config.get('alarm', '1')):
hub.update_alarms()
alarms.extend([
VerisureAlarm(value.id)
for value in hub.alarm_status.values()
])
add_devices(alarms)
@ -40,9 +35,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureAlarm(alarm.AlarmControlPanel):
""" Represents a Verisure alarm status. """
def __init__(self, alarm_status):
self._id = alarm_status.id
def __init__(self, device_id):
self._id = device_id
self._state = STATE_UNKNOWN
self._digits = int(hub.config.get('code_digits', '4'))
@property
def name(self):
@ -56,41 +52,41 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
""" Four digit code required. """
return '^\\d{%s}$' % verisure.CODE_DIGITS
""" code format as regex """
return '^\\d{%s}$' % self._digits
def update(self):
""" Update alarm status """
verisure.update_alarm()
hub.update_alarms()
if verisure.ALARM_STATUS[self._id].status == 'unarmed':
if hub.alarm_status[self._id].status == 'unarmed':
self._state = STATE_ALARM_DISARMED
elif verisure.ALARM_STATUS[self._id].status == 'armedhome':
elif hub.alarm_status[self._id].status == 'armedhome':
self._state = STATE_ALARM_ARMED_HOME
elif verisure.ALARM_STATUS[self._id].status == 'armed':
elif hub.alarm_status[self._id].status == 'armed':
self._state = STATE_ALARM_ARMED_AWAY
elif verisure.ALARM_STATUS[self._id].status != 'pending':
elif hub.alarm_status[self._id].status != 'pending':
_LOGGER.error(
'Unknown alarm state %s',
verisure.ALARM_STATUS[self._id].status)
hub.alarm_status[self._id].status)
def alarm_disarm(self, code=None):
""" Send disarm command. """
verisure.MY_PAGES.alarm.set(code, 'DISARMED')
hub.my_pages.alarm.set(code, 'DISARMED')
_LOGGER.info('verisure alarm disarming')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_home(self, code=None):
""" Send arm home command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_HOME')
hub.my_pages.alarm.set(code, 'ARMED_HOME')
_LOGGER.info('verisure alarm arming home')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_away(self, code=None):
""" Send arm away command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_AWAY')
hub.my_pages.alarm.set(code, 'ARMED_AWAY')
_LOGGER.info('verisure alarm arming away')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()

View File

@ -1,7 +1,5 @@
"""
components.alexa
~~~~~~~~~~~~~~~~
Component to offer a service end point for an Alexa skill.
Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
@ -10,8 +8,8 @@ import enum
import logging
from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY
from homeassistant.util import template
from homeassistant.helpers.service import call_from_config
from homeassistant.helpers import template
DOMAIN = 'alexa'
DEPENDENCIES = ['http']
@ -28,7 +26,7 @@ CONF_ACTION = 'action'
def setup(hass, config):
""" Activate Alexa component. """
"""Activate Alexa component."""
_CONFIG.update(config[DOMAIN].get(CONF_INTENTS, {}))
hass.http.register_path('POST', API_ENDPOINT, _handle_alexa, True)
@ -37,7 +35,7 @@ def setup(hass, config):
def _handle_alexa(handler, path_match, data):
""" Handle Alexa. """
"""Handle Alexa."""
_LOGGER.debug('Received Alexa request: %s', data)
req = data.get('request')
@ -99,19 +97,19 @@ def _handle_alexa(handler, path_match, data):
class SpeechType(enum.Enum):
""" Alexa speech types. """
"""Alexa speech types."""
plaintext = "PlainText"
ssml = "SSML"
class CardType(enum.Enum):
""" Alexa card types. """
"""Alexa card types."""
simple = "Simple"
link_account = "LinkAccount"
class AlexaResponse(object):
""" Helps generating the response for Alexa. """
"""Helps generating the response for Alexa."""
def __init__(self, hass, intent=None):
self.hass = hass
@ -154,7 +152,7 @@ class AlexaResponse(object):
}
def add_reprompt(self, speech_type, text):
""" Add repromopt if user does not answer. """
"""Add reprompt if user does not answer."""
assert self.reprompt is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
@ -165,7 +163,7 @@ class AlexaResponse(object):
}
def as_dict(self):
""" Returns response in an Alexa valid dict. """
"""Returns response in an Alexa valid dict."""
response = {
'shouldEndSession': self.should_end_session
}
@ -188,5 +186,5 @@ class AlexaResponse(object):
}
def _render(self, template_string):
""" Render a response, adding data from intent if available. """
"""Render a response, adding data from intent if available."""
return template.render(self.hass, template_string, self.variables)

View File

@ -1,31 +1,27 @@
"""
homeassistant.components.api
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a Rest API for Home Assistant.
Rest API for Home Assistant.
For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/
"""
import re
import logging
import threading
import json
import logging
import re
import threading
import homeassistant.core as ha
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import TrackStates
import homeassistant.remote as rem
from homeassistant.util import template
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_ERROR_LOG, URL_API_LOG_OUT,
URL_API_TEMPLATE, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL,
HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
HTTP_UNPROCESSABLE_ENTITY, HTTP_HEADER_CONTENT_TYPE,
CONTENT_TYPE_TEXT_PLAIN)
CONTENT_TYPE_TEXT_PLAIN, EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_HEADER_CONTENT_TYPE, HTTP_NOT_FOUND,
HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, MATCH_ALL, URL_API, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_ERROR_LOG, URL_API_EVENT_FORWARD, URL_API_EVENTS,
URL_API_LOG_OUT, URL_API_SERVICES, URL_API_STATES, URL_API_STATES_ENTITY,
URL_API_STREAM, URL_API_TEMPLATE)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import TrackStates
from homeassistant.helpers import template
DOMAIN = 'api'
DEPENDENCIES = ['http']
@ -37,7 +33,7 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Register the API with the HTTP interface. """
"""Register the API with the HTTP interface."""
# /api - for validation purposes
hass.http.register_path('GET', URL_API, _handle_get_api)
@ -48,10 +44,6 @@ def setup(hass, config):
# /api/config
hass.http.register_path('GET', URL_API_CONFIG, _handle_get_api_config)
# /api/bootstrap
hass.http.register_path(
'GET', URL_API_BOOTSTRAP, _handle_get_api_bootstrap)
# /states
hass.http.register_path('GET', URL_API_STATES, _handle_get_api_states)
hass.http.register_path(
@ -63,6 +55,9 @@ def setup(hass, config):
hass.http.register_path(
'PUT', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
_handle_post_state_entity)
hass.http.register_path(
'DELETE', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
_handle_delete_state_entity)
# /events
hass.http.register_path('GET', URL_API_EVENTS, _handle_get_api_events)
@ -101,12 +96,12 @@ def setup(hass, config):
def _handle_get_api(handler, path_match, data):
""" Renders the debug interface. """
"""Renders the debug interface."""
handler.write_json_message("API running.")
def _handle_get_api_stream(handler, path_match, data):
""" Provide a streaming interface for the event bus. """
"""Provide a streaming interface for the event bus."""
gracefully_closed = False
hass = handler.server.hass
wfile = handler.wfile
@ -119,7 +114,7 @@ def _handle_get_api_stream(handler, path_match, data):
restrict = restrict.split(',')
def write_message(payload):
""" Writes a message to the output. """
"""Writes a message to the output."""
with write_lock:
msg = "data: {}\n\n".format(payload)
@ -132,7 +127,7 @@ def _handle_get_api_stream(handler, path_match, data):
block.set()
def forward_events(event):
""" Forwards events to the open request. """
"""Forwards events to the open request."""
nonlocal gracefully_closed
if block.is_set() or event.event_type == EVENT_TIME_CHANGED:
@ -176,29 +171,17 @@ def _handle_get_api_stream(handler, path_match, data):
def _handle_get_api_config(handler, path_match, data):
""" Returns the Home Assistant config. """
"""Returns the Home Assistant configuration."""
handler.write_json(handler.server.hass.config.as_dict())
def _handle_get_api_bootstrap(handler, path_match, data):
""" Returns all data needed to bootstrap Home Assistant. """
hass = handler.server.hass
handler.write_json({
'config': hass.config.as_dict(),
'states': hass.states.all(),
'events': _events_json(hass),
'services': _services_json(hass),
})
def _handle_get_api_states(handler, path_match, data):
""" Returns a dict containing all entity ids and their state. """
"""Returns a dict containing all entity ids and their state."""
handler.write_json(handler.server.hass.states.all())
def _handle_get_api_states_entity(handler, path_match, data):
""" Returns the state of a specific entity. """
"""Returns the state of a specific entity."""
entity_id = path_match.group('entity_id')
state = handler.server.hass.states.get(entity_id)
@ -210,7 +193,7 @@ def _handle_get_api_states_entity(handler, path_match, data):
def _handle_post_state_entity(handler, path_match, data):
""" Handles updating the state of an entity.
"""Handles updating the state of an entity.
This handles the following paths:
/api/states/<entity_id>
@ -240,13 +223,29 @@ def _handle_post_state_entity(handler, path_match, data):
location=URL_API_STATES_ENTITY.format(entity_id))
def _handle_delete_state_entity(handler, path_match, data):
"""Handle request to delete an entity from state machine.
This handles the following paths:
/api/states/<entity_id>
"""
entity_id = path_match.group('entity_id')
if handler.server.hass.states.remove(entity_id):
handler.write_json_message(
"Entity not found", HTTP_NOT_FOUND)
else:
handler.write_json_message(
"Entity removed", HTTP_OK)
def _handle_get_api_events(handler, path_match, data):
""" Handles getting overview of event listeners. """
handler.write_json(_events_json(handler.server.hass))
"""Handles getting overview of event listeners."""
handler.write_json(events_json(handler.server.hass))
def _handle_api_post_events_event(handler, path_match, event_data):
""" Handles firing of an event.
"""Handles firing of an event.
This handles the following paths:
/api/events/<event_type>
@ -258,6 +257,7 @@ def _handle_api_post_events_event(handler, path_match, event_data):
if event_data is not None and not isinstance(event_data, dict):
handler.write_json_message(
"event_data should be an object", HTTP_UNPROCESSABLE_ENTITY)
return
event_origin = ha.EventOrigin.remote
@ -276,13 +276,13 @@ def _handle_api_post_events_event(handler, path_match, event_data):
def _handle_get_api_services(handler, path_match, data):
""" Handles getting overview of services. """
handler.write_json(_services_json(handler.server.hass))
"""Handles getting overview of services."""
handler.write_json(services_json(handler.server.hass))
# pylint: disable=invalid-name
def _handle_post_api_services_domain_service(handler, path_match, data):
""" Handles calling a service.
"""Handles calling a service.
This handles the following paths:
/api/services/<domain>/<service>
@ -298,8 +298,7 @@ def _handle_post_api_services_domain_service(handler, path_match, data):
# pylint: disable=invalid-name
def _handle_post_api_event_forward(handler, path_match, data):
""" Handles adding an event forwarding target. """
"""Handles adding an event forwarding target."""
try:
host = data['host']
api_password = data['api_password']
@ -332,8 +331,7 @@ def _handle_post_api_event_forward(handler, path_match, data):
def _handle_delete_api_event_forward(handler, path_match, data):
""" Handles deleting an event forwarding target. """
"""Handles deleting an event forwarding target."""
try:
host = data['host']
except KeyError:
@ -356,26 +354,25 @@ def _handle_delete_api_event_forward(handler, path_match, data):
def _handle_get_api_components(handler, path_match, data):
""" Returns all the loaded components. """
"""Returns all the loaded components."""
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. """
"""Returns the logged errors for this session."""
handler.write_file(handler.server.hass.config.path(ERROR_LOG_FILENAME),
False)
def _handle_post_api_log_out(handler, path_match, data):
""" Log user out. """
"""Log user out."""
handler.send_response(HTTP_OK)
handler.destroy_session()
handler.end_headers()
def _handle_post_api_template(handler, path_match, data):
""" Log user out. """
"""Log user out."""
template_string = data.get('template', '')
try:
@ -390,13 +387,13 @@ def _handle_post_api_template(handler, path_match, data):
return
def _services_json(hass):
""" Generate services data to JSONify. """
def services_json(hass):
"""Generate services data to JSONify."""
return [{"domain": key, "services": value}
for key, value in hass.services.services.items()]
def _events_json(hass):
""" Generate event data to JSONify. """
def events_json(hass):
"""Generate event data to JSONify."""
return [{"event": key, "listener_count": value}
for key, value in hass.bus.listeners.items()]

View File

@ -9,9 +9,9 @@ https://home-assistant.io/components/arduino/
"""
import logging
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import validate_config
from homeassistant.const import (EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
DOMAIN = "arduino"
REQUIREMENTS = ['PyMata==2.07a']

View File

@ -6,13 +6,12 @@ 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/#numeric-state-trigger
"""
from functools import partial
import logging
from functools import partial
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers.event import track_state_change
from homeassistant.util import template
from homeassistant.helpers import template
CONF_ENTITY_ID = "entity_id"
CONF_BELOW = "below"

View File

@ -7,15 +7,47 @@ For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#state-trigger
"""
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_state_change
from homeassistant.const import MATCH_ALL
import homeassistant.util.dt as dt_util
from homeassistant.const import (
EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL)
from homeassistant.components.automation.time import (
CONF_HOURS, CONF_MINUTES, CONF_SECONDS)
from homeassistant.helpers.event import track_state_change, track_point_in_time
CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from"
CONF_TO = "to"
CONF_STATE = "state"
CONF_FOR = "for"
def get_time_config(config):
""" Helper function to extract the time specified in the config """
if CONF_FOR not in config:
return None
hours = config[CONF_FOR].get(CONF_HOURS)
minutes = config[CONF_FOR].get(CONF_MINUTES)
seconds = config[CONF_FOR].get(CONF_SECONDS)
if hours is None and minutes is None and seconds is None:
logging.getLogger(__name__).error(
"Received invalid value for '%s': %s",
config[CONF_FOR], CONF_FOR)
return None
if config.get(CONF_TO) is None and config.get(CONF_STATE) is None:
logging.getLogger(__name__).error(
"For: requires a to: value'%s': %s",
config[CONF_FOR], CONF_FOR)
return None
return timedelta(hours=(hours or 0.0),
minutes=(minutes or 0.0),
seconds=(seconds or 0.0))
def trigger(hass, config, action):
@ -25,19 +57,47 @@ def trigger(hass, config, action):
if entity_id is None:
logging.getLogger(__name__).error(
"Missing trigger configuration key %s", CONF_ENTITY_ID)
return False
return None
from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
time_delta = get_time_config(config)
if isinstance(from_state, bool) or isinstance(to_state, bool):
logging.getLogger(__name__).error(
'Config error. Surround to/from values with quotes.')
return False
return None
if CONF_FOR in config and time_delta is None:
return None
def state_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """
action()
def state_for_listener(now):
""" Fires on state changes after a delay and calls action. """
hass.bus.remove_listener(
EVENT_STATE_CHANGED, for_state_listener)
action()
def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
""" Fires on state changes and cancels
for listener if state changed. """
if inner_to_s == to_s:
return
hass.bus.remove_listener(EVENT_TIME_CHANGED, for_time_listener)
hass.bus.remove_listener(
EVENT_STATE_CHANGED, for_state_listener)
if time_delta is not None:
target_tm = dt_util.utcnow() + time_delta
for_time_listener = track_point_in_time(
hass, state_for_listener, target_tm)
for_state_listener = track_state_change(
hass, entity_id, state_for_cancel_listener,
MATCH_ALL, MATCH_ALL)
else:
action()
track_state_change(
hass, entity_id, state_automation_listener, from_state, to_state)
@ -56,10 +116,18 @@ def if_action(hass, config):
CONF_STATE)
return None
time_delta = get_time_config(config)
if CONF_FOR in config and time_delta is None:
return None
state = str(state)
def if_state():
""" Test if condition. """
return hass.states.is_state(entity_id, state)
is_state = hass.states.is_state(entity_id, state)
return (time_delta is None and is_state or
time_delta is not None and
dt_util.utcnow() - time_delta >
hass.states.get(entity_id).last_changed)
return if_state

View File

@ -9,9 +9,9 @@ at https://home-assistant.io/components/automation/#sun-trigger
import logging
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.components import sun
from homeassistant.helpers.event import track_sunrise, track_sunset
import homeassistant.util.dt as dt_util
DEPENDENCIES = ['sun']

View File

@ -10,7 +10,7 @@ import logging
from homeassistant.const import CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED
from homeassistant.exceptions import TemplateError
from homeassistant.util import template
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)

View File

@ -9,10 +9,9 @@ at https://home-assistant.io/components/automation/#zone-trigger
import logging
from homeassistant.components import zone
from homeassistant.helpers.event import track_state_change
from homeassistant.const import (
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL)
from homeassistant.helpers.event import track_state_change
CONF_ENTITY_ID = "entity_id"
CONF_ZONE = "zone"

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with binary sensors (sensors which only know two states)
that can be monitored.
@ -12,17 +10,43 @@ import logging
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.components import (bloomsky, mysensors, zwave, wink)
DOMAIN = 'binary_sensor'
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SENSOR_CLASSES = [
None, # Generic on/off
'opening', # Door, window, etc
'motion', # Motion sensor
'gas', # CO, CO2, etc
'smoke', # Smoke detector
'moisture', # Specifically a wetness sensor
'light', # Lightness threshold
'power', # Power, over-current, etc
'safety', # Generic on=unsafe, off=safe
'heat', # On means hot (or too hot)
'cold', # On means cold (or too cold)
'moving', # On means moving, Off means stopped
'sound', # On means sound detected, Off means no sound
'vibration', # On means vibration detected, Off means no vibration
]
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
wink.DISCOVER_BINARY_SENSORS: 'wink'
}
def setup(hass, config):
""" Track states and offer events for binary sensors. """
"""Track states and offer events for binary sensors."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
component.setup(config)
@ -31,19 +55,29 @@ def setup(hass, config):
# pylint: disable=no-self-use
class BinarySensorDevice(Entity):
""" Represents a binary sensor. """
"""Represent a binary sensor."""
@property
def is_on(self):
""" True if the binary sensor is on. """
"""Return True if the binary sensor is on."""
return None
@property
def state(self):
""" Returns the state of the binary sensor. """
"""Return the state of the binary sensor."""
return STATE_ON if self.is_on else STATE_OFF
@property
def friendly_state(self):
""" Returns the friendly state of the binary sensor. """
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return None
@property
def state_attributes(self):
"""Return device specific state attributes."""
attr = {}
if self.sensor_class is not None:
attr['sensor_class'] = self.sensor_class
return attr

View File

@ -1,25 +1,23 @@
"""
homeassistant.components.binary_sensor.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a binary sensor to track online status of a UPS.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.apcupsd/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components import apcupsd
from homeassistant.components.binary_sensor import BinarySensorDevice
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Online Status"
def setup_platform(hass, config, add_entities, discovery_info=None):
""" Instantiate an OnlineStatus binary sensor entity and add it to HA. """
"""Instantiate an OnlineStatus binary sensor entity."""
add_entities((OnlineStatus(config, apcupsd.DATA),))
class OnlineStatus(BinarySensorDevice):
""" Binary sensor to represent UPS online status. """
"""Binary sensor to represent UPS online status."""
def __init__(self, config, data):
self._config = config
self._data = data
@ -33,7 +31,7 @@ class OnlineStatus(BinarySensorDevice):
@property
def is_on(self):
""" True if the UPS is online, else False. """
"""True if the UPS is online, else False."""
return self._state == apcupsd.VALUE_ONLINE
def update(self):

View File

@ -1,18 +1,16 @@
"""
homeassistant.components.binary_sensor.arest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The arest sensor will consume an exposed aREST API of a device.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.arest/
"""
from datetime import timedelta
import logging
from datetime import timedelta
import requests
from homeassistant.util import Throttle
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
@ -24,8 +22,7 @@ CONF_PIN = 'pin'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Get the aREST binary sensor. """
"""Get the aREST binary sensor."""
resource = config.get(CONF_RESOURCE)
pin = config.get(CONF_PIN)
@ -56,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-instance-attributes, too-many-arguments
class ArestBinarySensor(BinarySensorDevice):
""" Implements an aREST binary sensor for a pin. """
"""Implements an aREST binary sensor for a pin."""
def __init__(self, arest, resource, name, pin):
self.arest = arest
@ -73,23 +70,22 @@ class ArestBinarySensor(BinarySensorDevice):
@property
def name(self):
""" The name of the binary sensor. """
"""The name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return bool(self.arest.data.get('state'))
def update(self):
""" Gets the latest data from aREST API. """
"""Gets the latest data from aREST API."""
self.arest.update()
# pylint: disable=too-few-public-methods
class ArestData(object):
""" Class for handling the data retrieval for pins. """
"""Class for handling the data retrieval for pins."""
def __init__(self, resource, pin):
self._resource = resource
self._pin = pin
@ -97,7 +93,7 @@ class ArestData(object):
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
""" Gets the latest data from aREST device. """
"""Gets the latest data from aREST device."""
try:
response = requests.get('{}/digital/{}'.format(
self._resource, self._pin), timeout=10)

View File

@ -0,0 +1,74 @@
"""
Support the binary sensors of a BloomSky weather station.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.bloomsky/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.loader import get_component
DEPENDENCIES = ["bloomsky"]
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
"Rain": "moisture",
"Night": None,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the available BloomSky weather binary sensors."""
logger = logging.getLogger(__name__)
bloomsky = get_component('bloomsky')
sensors = config.get('monitored_conditions', SENSOR_TYPES)
for device in bloomsky.BLOOMSKY.devices.values():
for variable in sensors:
if variable in SENSOR_TYPES:
add_devices([BloomSkySensor(bloomsky.BLOOMSKY,
device,
variable)])
else:
logger.error("Cannot find definition for device: %s", variable)
class BloomSkySensor(BinarySensorDevice):
""" Represents a single binary sensor in a BloomSky device. """
def __init__(self, bs, device, sensor_name):
"""Initialize a BloomSky binary sensor."""
self._bloomsky = bs
self._device_id = device["DeviceID"]
self._sensor_name = sensor_name
self._name = "{} {}".format(device["DeviceName"], sensor_name)
self._unique_id = "bloomsky_binary_sensor {}".format(self._name)
self.update()
@property
def name(self):
"""The name of the BloomSky device and this sensor."""
return self._name
@property
def unique_id(self):
"""Unique ID for this sensor."""
return self._unique_id
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self._sensor_name)
@property
def is_on(self):
"""If binary sensor is on."""
return self._state
def update(self):
"""Request an update from the BloomSky API."""
self._bloomsky.refresh_devices()
self._state = \
self._bloomsky.devices[self._device_id]["Data"][self._sensor_name]

View File

@ -1,8 +1,6 @@
"""
homeassistant.components.binary_sensor.command_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure custom shell commands to turn a value
into a logical value for a binary sensor.
Allows to configure custom shell commands to turn a value into a logical value
for a binary sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.command/
@ -10,10 +8,10 @@ https://home-assistant.io/components/binary_sensor.command/
import logging
from datetime import timedelta
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.command_sensor import CommandSensorData
from homeassistant.util import template
from homeassistant.components.sensor.command_line import CommandSensorData
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@ -27,8 +25,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Add the Command Sensor. """
"""Add the Command Sensor."""
if config.get('command') is None:
_LOGGER.error('Missing required variable: "command"')
return False
@ -47,8 +44,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments
class CommandBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that is returning
a value of a shell commands. """
"""
Represents a binary sensor that is returning a value of a shell commands.
"""
def __init__(self, hass, data, name, payload_on,
payload_off, value_template):
self._hass = hass
@ -62,16 +60,16 @@ class CommandBinarySensor(BinarySensorDevice):
@property
def name(self):
""" The name of the sensor. """
"""The name of the sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return self._state
def update(self):
""" Gets the latest data and updates the state. """
"""Gets the latest data and updates the state."""
self.data.update()
value = self.data.value

View File

@ -1,37 +1,43 @@
"""
homeassistant.components.binary_sensor.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has two fake binary sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo binary sensors. """
"""Setup the Demo binary sensor platform."""
add_devices([
DemoBinarySensor('Basement Floor Wet', False),
DemoBinarySensor('Movement Backyard', True),
DemoBinarySensor('Basement Floor Wet', False, 'moisture'),
DemoBinarySensor('Movement Backyard', True, 'motion'),
])
class DemoBinarySensor(BinarySensorDevice):
""" A Demo binary sensor. """
def __init__(self, name, state):
"""A Demo binary sensor."""
def __init__(self, name, state, sensor_class):
self._name = name
self._state = state
self._sensor_type = sensor_class
@property
def sensor_class(self):
"""Return the class of this sensor."""
return self._sensor_type
@property
def should_poll(self):
""" No polling needed for a demo binary sensor. """
"""No polling needed for a demo binary sensor."""
return False
@property
def name(self):
""" Returns the name of the binary sensor. """
"""Return the name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""Return true if the binary sensor is on."""
return self._state

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.mqtt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a MQTT binary sensor.
For more details about this platform, please refer to the documentation at
@ -8,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.mqtt/
"""
import logging
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.util import template
import homeassistant.components.mqtt as mqtt
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@ -25,7 +23,7 @@ DEPENDENCIES = ['mqtt']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Add MQTT binary sensor. """
"""Add MQTT binary sensor."""
if config.get('state_topic') is None:
_LOGGER.error('Missing required variable: state_topic')
@ -43,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments, too-many-instance-attributes
class MqttBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that is updated by MQTT. """
"""Represents a binary sensor that is updated by MQTT."""
def __init__(self, hass, name, state_topic, qos, payload_on, payload_off,
value_template):
self._hass = hass
@ -55,7 +53,7 @@ class MqttBinarySensor(BinarySensorDevice):
self._qos = qos
def message_received(topic, payload, qos):
""" A new MQTT message has been received. """
"""A new MQTT message has been received."""
if value_template is not None:
payload = template.render_with_possible_json_value(
hass, value_template, payload)
@ -70,15 +68,15 @@ class MqttBinarySensor(BinarySensorDevice):
@property
def should_poll(self):
""" No polling needed. """
"""No polling needed."""
return False
@property
def name(self):
""" The name of the binary sensor. """
"""The name of the binary sensor."""
return self._name
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return self._state

View File

@ -0,0 +1,168 @@
"""
Support for MySensors binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mysensors/
"""
import logging
from homeassistant.const import (
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, SENSOR_CLASSES)
from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for sensors."""
# Only act if loaded via mysensors by discovery event.
# Otherwise gateway is not setup.
if discovery_info is None:
return
mysensors = get_component('mysensors')
for gateway in mysensors.GATEWAYS.values():
# Define the S_TYPES and V_TYPES that the platform should handle as
# states. Map them in a dict of lists.
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
map_sv_types = {
pres.S_DOOR: [set_req.V_TRIPPED],
pres.S_MOTION: [set_req.V_TRIPPED],
pres.S_SMOKE: [set_req.V_TRIPPED],
}
if float(gateway.version) >= 1.5:
map_sv_types.update({
pres.S_SPRINKLER: [set_req.V_TRIPPED],
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
pres.S_SOUND: [set_req.V_TRIPPED],
pres.S_VIBRATION: [set_req.V_TRIPPED],
pres.S_MOISTURE: [set_req.V_TRIPPED],
})
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, add_devices, MySensorsBinarySensor))
class MySensorsBinarySensor(BinarySensorDevice):
"""Represent the value of a MySensors child node."""
# pylint: disable=too-many-arguments,too-many-instance-attributes
def __init__(
self, gateway, node_id, child_id, name, value_type, child_type):
"""
Setup class attributes on instantiation.
Args:
gateway (GatewayWrapper): Gateway object.
node_id (str): Id of node.
child_id (str): Id of child.
name (str): Entity name.
value_type (str): Value type of child. Value is entity state.
child_type (str): Child type of child.
Attributes:
gateway (GatewayWrapper): Gateway object.
node_id (str): Id of node.
child_id (str): Id of child.
_name (str): Entity name.
value_type (str): Value type of child. Value is entity state.
child_type (str): Child type of child.
battery_level (int): Node battery level.
_values (dict): Child values. Non state values set as state attributes.
mysensors (module): Mysensors main component module.
"""
self.gateway = gateway
self.node_id = node_id
self.child_id = child_id
self._name = name
self.value_type = value_type
self.child_type = child_type
self.battery_level = 0
self._values = {}
self.mysensors = get_component('mysensors')
@property
def should_poll(self):
"""MySensor gateway pushes its state to HA."""
return False
@property
def name(self):
"""The name of this entity."""
return self._name
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
attr = {
self.mysensors.ATTR_PORT: self.gateway.port,
self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level,
}
set_req = self.gateway.const.SetReq
for value_type, value in self._values.items():
if value_type != self.value_type:
try:
attr[set_req(value_type).name] = value
except ValueError:
_LOGGER.error('value_type %s is not valid for mysensors '
'version %s', value_type,
self.gateway.version)
return attr
@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.value_type in self._values:
return self._values[self.value_type] == STATE_ON
return False
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
pres = self.gateway.const.Presentation
class_map = {
pres.S_DOOR: 'opening',
pres.S_MOTION: 'motion',
pres.S_SMOKE: 'smoke',
}
if float(self.gateway.version) >= 1.5:
class_map.update({
pres.S_SPRINKLER: 'sprinkler',
pres.S_WATER_LEAK: 'leak',
pres.S_SOUND: 'sound',
pres.S_VIBRATION: 'vibration',
pres.S_MOISTURE: 'moisture',
})
if class_map.get(self.child_type) in SENSOR_CLASSES:
return class_map.get(self.child_type)
@property
def available(self):
"""Return True if entity is available."""
return self.value_type in self._values
def update(self):
"""Update the controller with the latest values from a sensor."""
node = self.gateway.sensors[self.node_id]
child = node.children[self.child_id]
for value_type, value in child.values.items():
_LOGGER.debug(
"%s: value_type %s, value = %s", self._name, value_type, value)
if value_type == self.gateway.const.SetReq.V_TRIPPED:
self._values[value_type] = STATE_ON if int(
value) == 1 else STATE_OFF
else:
self._values[value_type] = value
self.battery_level = node.battery_level

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.nest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Nest Thermostat Binary Sensors.
For more details about this platform, please refer to the documentation at
@ -8,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.nest/
"""
import logging
import socket
import homeassistant.components.nest as nest
from homeassistant.components.sensor.nest import NestSensor
import homeassistant.components.nest as nest
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.nest import NestSensor
DEPENDENCIES = ['nest']
BINARY_TYPES = ['fan',
@ -27,7 +25,7 @@ BINARY_TYPES = ['fan',
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Setup Nest binary sensors. """
"""Setup Nest binary sensors."""
logger = logging.getLogger(__name__)
try:
@ -48,9 +46,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class NestBinarySensor(NestSensor, BinarySensorDevice):
""" Represents a Nest binary sensor. """
"""Represents a Nest binary sensor."""
@property
def is_on(self):
""" True if the binary sensor is on. """
"""True if the binary sensor is on."""
return bool(getattr(self.device, self.variable))

View File

@ -0,0 +1,131 @@
"""
Support for exposing nx584 elements as sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.nx584/
"""
import logging
import threading
import time
import requests
from homeassistant.components.binary_sensor import (
SENSOR_CLASSES, BinarySensorDevice)
REQUIREMENTS = ['pynx584==0.2']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup nx584 sensors."""
from nx584 import client as nx584_client
host = config.get('host', 'localhost:5007')
exclude = config.get('exclude_zones', [])
zone_types = config.get('zone_types', {})
if not all(isinstance(zone, int) for zone in exclude):
_LOGGER.error('Invalid excluded zone specified (use zone number)')
return False
if not all(isinstance(zone, int) and ztype in SENSOR_CLASSES
for zone, ztype in zone_types.items()):
_LOGGER.error('Invalid zone_types entry')
return False
try:
client = nx584_client.Client('http://%s' % host)
zones = client.list_zones()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NX584: %s', str(ex))
return False
version = [int(v) for v in client.get_version().split('.')]
if version < [1, 1]:
_LOGGER.error('NX584 is too old to use for sensors (>=0.2 required)')
return False
zone_sensors = {
zone['number']: NX584ZoneSensor(
zone,
zone_types.get(zone['number'], 'opening'))
for zone in zones
if zone['number'] not in exclude}
if zone_sensors:
add_devices(zone_sensors.values())
watcher = NX584Watcher(client, zone_sensors)
watcher.start()
else:
_LOGGER.warning('No zones found on NX584')
return True
class NX584ZoneSensor(BinarySensorDevice):
"""Represents a NX584 zone as a sensor."""
def __init__(self, zone, zone_type):
self._zone = zone
self._zone_type = zone_type
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return self._zone_type
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Name of the binary sensor."""
return self._zone['name']
@property
def is_on(self):
"""Return true if the binary sensor is on."""
# True means "faulted" or "open" or "abnormal state"
return self._zone['state']
class NX584Watcher(threading.Thread):
"""Event listener thread to process NX584 events."""
def __init__(self, client, zone_sensors):
super(NX584Watcher, self).__init__()
self.daemon = True
self._client = client
self._zone_sensors = zone_sensors
def _process_zone_event(self, event):
zone = event['zone']
zone_sensor = self._zone_sensors.get(zone)
# pylint: disable=protected-access
if not zone_sensor:
return
zone_sensor._zone['state'] = event['zone_state']
zone_sensor.update_ha_state()
def _process_events(self, events):
for event in events:
if event.get('type') == 'zone_status':
self._process_zone_event(event)
def _run(self):
# Throw away any existing events so we don't replay history
self._client.get_events()
while True:
events = self._client.get_events()
if events:
self._process_events(events)
def run(self):
while True:
try:
self._run()
except requests.exceptions.ConnectionError:
_LOGGER.error('Failed to reach NX584 server')
time.sleep(10)

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.rest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rest binary sensor will consume responses sent by an exposed REST API.
For more details about this platform, please refer to the documentation at
@ -8,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.rest/
"""
import logging
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.util import template
from homeassistant.components.sensor.rest import RestData
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.rest import RestData
from homeassistant.const import CONF_VALUE_TEMPLATE
from homeassistant.helpers import template
_LOGGER = logging.getLogger(__name__)
@ -21,7 +19,7 @@ DEFAULT_METHOD = 'GET'
# pylint: disable=unused-variable
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Setup REST binary sensors. """
"""Setup REST binary sensors."""
resource = config.get('resource', None)
method = config.get('method', DEFAULT_METHOD)
payload = config.get('payload', None)
@ -41,10 +39,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments
class RestBinarySensor(BinarySensorDevice):
""" A REST binary sensor. """
"""A REST binary sensor."""
def __init__(self, hass, rest, name, value_template):
""" Initialize a REST binary sensor. """
"""Initialize a REST binary sensor."""
self._hass = hass
self.rest = rest
self._name = name
@ -54,12 +52,12 @@ class RestBinarySensor(BinarySensorDevice):
@property
def name(self):
""" Name of the binary sensor. """
"""Name of the binary sensor."""
return self._name
@property
def is_on(self):
""" Return if the binary sensor is on. """
"""Return true if the binary sensor is on."""
if self.rest.data is None:
return False
@ -69,5 +67,5 @@ class RestBinarySensor(BinarySensorDevice):
return bool(int(self.rest.data))
def update(self):
""" Get the latest data from REST API and updates the state. """
"""Get the latest data from REST API and updates the state."""
self.rest.update()

View File

@ -1,16 +1,14 @@
"""
homeassistant.components.binary_sensor.rpi_gpio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows to configure a binary sensor using RPi GPIO.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rpi_gpio/
"""
import logging
import homeassistant.components.rpi_gpio as rpi_gpio
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import (DEVICE_DEFAULT_NAME)
from homeassistant.const import DEVICE_DEFAULT_NAME
DEFAULT_PULL_MODE = "UP"
DEFAULT_BOUNCETIME = 50
@ -22,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Raspberry PI GPIO devices. """
"""Sets up the Raspberry PI GPIO devices."""
pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE)
bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME)
@ -38,7 +36,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
# pylint: disable=too-many-arguments, too-many-instance-attributes
class RPiGPIOBinarySensor(BinarySensorDevice):
""" Represents a binary sensor that uses Raspberry Pi GPIO. """
"""Represents a binary sensor that uses Raspberry Pi GPIO."""
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
# pylint: disable=no-member
@ -52,22 +50,22 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
self._state = rpi_gpio.read_input(self._port)
def read_gpio(port):
""" Reads state from GPIO. """
"""Reads state from GPIO."""
self._state = rpi_gpio.read_input(self._port)
self.update_ha_state()
rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)
@property
def should_poll(self):
""" No polling needed. """
"""No polling needed."""
return False
@property
def name(self):
""" The name of the sensor. """
"""The name of the sensor."""
return self._name
@property
def is_on(self):
""" Returns the state of the entity. """
"""Returns the state of the entity."""
return self._state != self._invert_logic

View File

@ -0,0 +1,31 @@
"""
Provides a binary sensor which gets its values from a TCP socket.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tcp/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sensor.tcp import Sensor, DOMAIN, CONF_VALUE_ON
DEPENDENCIES = [DOMAIN]
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Create the binary sensor."""
if not BinarySensor.validate_config(config):
return False
add_entities((BinarySensor(hass, config),))
class BinarySensor(BinarySensorDevice, Sensor):
"""A binary sensor which is on when its state == CONF_VALUE_ON."""
required = (CONF_VALUE_ON,)
@property
def is_on(self):
"""True if the binary sensor is on."""
return self._state == self._config[CONF_VALUE_ON]

View File

@ -0,0 +1,122 @@
"""
homeassistant.components.binary_sensor.template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for exposing a templated binary_sensor
"""
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice,
DOMAIN,
SENSOR_CLASSES)
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_VALUE_TEMPLATE
from homeassistant.core import EVENT_STATE_CHANGED
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers import template
from homeassistant.util import slugify
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_SENSORS = 'sensors'
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup template binary sensors."""
sensors = []
if config.get(CONF_SENSORS) is None:
_LOGGER.error('Missing configuration data for binary_sensor platform')
return False
for device, device_config in config[CONF_SENSORS].items():
if device != slugify(device):
_LOGGER.error('Found invalid key for binary_sensor.template: %s. '
'Use %s instead', device, slugify(device))
continue
if not isinstance(device_config, dict):
_LOGGER.error('Missing configuration data for binary_sensor %s',
device)
continue
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
sensor_class = device_config.get('sensor_class')
value_template = device_config.get(CONF_VALUE_TEMPLATE)
if sensor_class not in SENSOR_CLASSES:
_LOGGER.error('Sensor class is not valid')
continue
if value_template is None:
_LOGGER.error(
'Missing %s for sensor %s', CONF_VALUE_TEMPLATE, device)
continue
sensors.append(
BinarySensorTemplate(
hass,
device,
friendly_name,
sensor_class,
value_template)
)
if not sensors:
_LOGGER.error('No sensors added')
return False
add_devices(sensors)
return True
class BinarySensorTemplate(BinarySensorDevice):
"""A virtual binary_sensor that triggers from another sensor."""
# pylint: disable=too-many-arguments
def __init__(self, hass, device, friendly_name, sensor_class,
value_template):
self._hass = hass
self._device = device
self._name = friendly_name
self._sensor_class = sensor_class
self._template = value_template
self._state = None
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, device,
hass=hass)
_LOGGER.info('Started template sensor %s', device)
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
def _event_listener(self, event):
self.update_ha_state(True)
@property
def should_poll(self):
return False
@property
def sensor_class(self):
return self._sensor_class
@property
def name(self):
return self._name
@property
def is_on(self):
return self._state
def update(self):
try:
value = template.render(self._hass, self._template)
except TemplateError as ex:
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
# Common during HA startup - so just a warning
_LOGGER.warning(ex)
return
_LOGGER.error(ex)
value = 'false'
self._state = value.lower() == 'true'

View File

@ -0,0 +1,81 @@
"""
Support for Wink sensors.
For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/sensor.wink/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['python-wink==0.6.2']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
"opened": "opening",
"brightness": "light",
"vibration": "vibration",
"loudness": "sound"
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Sets up the Wink platform."""
import pywink
if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None:
logging.getLogger(__name__).error(
"Missing wink access_token. "
"Get one at https://winkbearertoken.appspot.com/")
return
pywink.set_bearer_token(token)
for sensor in pywink.get_sensors():
if sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(sensor)])
class WinkBinarySensorDevice(BinarySensorDevice, Entity):
"""Represents a Wink sensor."""
def __init__(self, wink):
self.wink = wink
self._unit_of_measurement = self.wink.UNIT
self.capability = self.wink.capability()
@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.capability == "loudness":
return self.wink.loudness_boolean()
elif self.capability == "vibration":
return self.wink.vibration_boolean()
elif self.capability == "brightness":
return self.wink.brightness_boolean()
else:
return self.wink.state()
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return SENSOR_TYPES.get(self.capability)
@property
def unique_id(self):
""" Returns the id of this wink sensor """
return "{}.{}".format(self.__class__, self.wink.device_id())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
def update(self):
""" Update state of the sensor. """
self.wink.update_state()

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.binary_sensor.zigbee
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Contains functionality to use a ZigBee device as a binary sensor.
For more details about this platform, please refer to the documentation at
@ -10,12 +8,11 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.zigbee import (
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
DEPENDENCIES = ["zigbee"]
def setup_platform(hass, config, add_entities, discovery_info=None):
""" Create and add an entity based on the configuration. """
"""Create and add an entity based on the configuration."""
add_entities([
ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))
])

View File

@ -0,0 +1,131 @@
"""
Interfaces with Z-Wave sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/binary_sensor.zwave/
"""
import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components.zwave import (
ATTR_NODE_ID, ATTR_VALUE_ID,
COMMAND_CLASS_SENSOR_BINARY, NETWORK,
ZWaveDeviceEntity, get_config_value)
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
PHILIO = 0x013c
PHILIO_SLIM_SENSOR = 0x0002
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
DEVICE_MAPPINGS = {
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Z-Wave platform for sensors."""
if discovery_info is None or NETWORK is None:
return
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
value = node.values[discovery_info[ATTR_VALUE_ID]]
specific_sensor_key = (int(value.node.manufacturer_id, 16),
int(value.node.product_id, 16),
value.index)
value.set_change_verified(False)
if specific_sensor_key in DEVICE_MAPPINGS:
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
re_arm_multiplier = (get_config_value(value.node, 9) or 4)
add_devices([
ZWaveTriggerSensor(value, "motion",
hass, re_arm_multiplier * 8)
])
elif value.command_class == COMMAND_CLASS_SENSOR_BINARY:
add_devices([ZWaveBinarySensor(value, None)])
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
"""Represents a binary sensor within Z-Wave."""
def __init__(self, value, sensor_class):
self._sensor_type = sensor_class
# pylint: disable=import-error
from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher
ZWaveDeviceEntity.__init__(self, value, DOMAIN)
dispatcher.connect(
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._value.data
@property
def sensor_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return self._sensor_type
@property
def should_poll(self):
"""No polling needed."""
return False
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id:
self.update_ha_state()
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""
Represents a stateless sensor which triggers events just 'On'
within Z-Wave.
"""
def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60):
super(ZWaveTriggerSensor, self).__init__(sensor_value, sensor_class)
self._hass = hass
self.re_arm_sec = re_arm_sec
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
seconds=self.re_arm_sec)
# If it's active make sure that we set the timeout tracker
if sensor_value.data:
track_point_in_time(
self._hass, self.update_ha_state,
self.invalidate_after)
def value_changed(self, value):
"""Called when a value has changed on the network."""
if self._value.value_id == value.value_id:
self.update_ha_state()
if value.data:
# only allow this value to be true for re_arm secs
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
seconds=self.re_arm_sec)
track_point_in_time(
self._hass, self.update_ha_state,
self.invalidate_after)
@property
def is_on(self):
"""Return True if movement has happened within the rearm time."""
return self._value.data and \
(self.invalidate_after is None or
self.invalidate_after > dt_util.utcnow())

View File

@ -8,10 +8,13 @@ https://home-assistant.io/components/bloomsky/
"""
import logging
from datetime import timedelta
import requests
from homeassistant.util import Throttle
from homeassistant.helpers import validate_config
from homeassistant.components import discovery
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
DOMAIN = "bloomsky"
BLOOMSKY = None
@ -22,6 +25,10 @@ _LOGGER = logging.getLogger(__name__)
# no point in polling the API more frequently
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=300)
DISCOVER_SENSORS = 'bloomsky.sensors'
DISCOVER_BINARY_SENSORS = 'bloomsky.binary_sensor'
DISCOVER_CAMERAS = 'bloomsky.camera'
# pylint: disable=unused-argument,too-few-public-methods
def setup(hass, config):
@ -40,6 +47,12 @@ def setup(hass, config):
except RuntimeError:
return False
for component, discovery_service in (
('camera', DISCOVER_CAMERAS), ('sensor', DISCOVER_SENSORS),
('binary_sensor', DISCOVER_BINARY_SENSORS)):
discovery.discover(hass, discovery_service, component=component,
hass_config=config)
return True

View File

@ -1,21 +1,18 @@
"""
homeassistant.components.browser
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to launch a webbrowser on the host machine.
Provides functionality to launch a web browser on the host machine.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/browser/
"""
DOMAIN = "browser"
SERVICE_BROWSE_URL = "browse_url"
def setup(hass, config):
""" Listen for browse_url events and open
the url in the default webbrowser. """
"""
Listen for browse_url events and open the url in the default web browser.
"""
import webbrowser
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,

View File

@ -1,8 +1,6 @@
# pylint: disable=too-many-lines
"""
homeassistant.components.camera
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with various cameras.
Component to interface with cameras.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera/
@ -15,8 +13,8 @@ import requests
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import bloomsky
from homeassistant.const import (
ATTR_ENTITY_PICTURE,
HTTP_NOT_FOUND,
ATTR_ENTITY_ID,
)
@ -24,33 +22,19 @@ from homeassistant.const import (
DOMAIN = 'camera'
DEPENDENCIES = ['http']
GROUP_NAME_ALL_CAMERAS = 'all_cameras'
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SWITCH_ACTION_RECORD = 'record'
SWITCH_ACTION_SNAPSHOT = 'snapshot'
SERVICE_CAMERA = 'camera_service'
DEFAULT_RECORDING_SECONDS = 30
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {}
FILE_DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S-%f'
DIR_DATETIME_FORMAT = '%Y-%m-%d_%H-%M-%S'
REC_DIR_PREFIX = 'recording-'
REC_IMG_PREFIX = 'recording_image-'
DISCOVERY_PLATFORMS = {
bloomsky.DISCOVER_CAMERAS: 'bloomsky',
}
STATE_RECORDING = 'recording'
STATE_STREAMING = 'streaming'
STATE_IDLE = 'idle'
CAMERA_PROXY_URL = '/api/camera_proxy_stream/{0}'
CAMERA_STILL_URL = '/api/camera_proxy/{0}'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?time={1}'
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}'
MULTIPART_BOUNDARY = '--jpegboundary'
MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n'
@ -58,8 +42,7 @@ MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n'
# pylint: disable=too-many-branches
def setup(hass, config):
""" Track states and offer events for cameras. """
"""Initialize camera component."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
@ -78,7 +61,7 @@ def setup(hass, config):
# pylint: disable=unused-argument
def _proxy_camera_image(handler, path_match, data):
""" Proxies the camera image via the HA server. """
"""Serve the camera image via the HA server."""
entity_id = path_match.group(ATTR_ENTITY_ID)
camera = component.entities.get(entity_id)
@ -104,7 +87,8 @@ def setup(hass, config):
# pylint: disable=unused-argument
def _proxy_camera_mjpeg_stream(handler, path_match, data):
"""
Proxies the camera image as an mjpeg stream via the HA server.
Proxy the camera image as an mjpeg stream via the HA server.
This function takes still images from the IP camera and turns them
into an MJPEG stream. This means that HA can return a live video
stream even with only a still image URL available.
@ -136,35 +120,46 @@ def setup(hass, config):
class Camera(Entity):
""" The base class for camera components. """
"""The base class for camera entities."""
def __init__(self):
"""Initialize a camera."""
self.is_streaming = False
@property
def should_poll(self):
"""No need to poll cameras."""
return False
@property
def entity_picture(self):
"""Return a link to the camera feed as entity picture."""
return ENTITY_IMAGE_URL.format(self.entity_id)
@property
# pylint: disable=no-self-use
def is_recording(self):
""" Returns true if the device is recording. """
"""Return true if the device is recording."""
return False
@property
# pylint: disable=no-self-use
def brand(self):
""" Should return a string of the camera brand. """
"""Camera brand."""
return None
@property
# pylint: disable=no-self-use
def model(self):
""" Returns string of camera model. """
"""Camera model."""
return None
def camera_image(self):
""" Return bytes of camera image. """
"""Return bytes of camera image."""
raise NotImplementedError()
def mjpeg_stream(self, handler):
""" Generate an HTTP MJPEG stream from camera images. """
"""Generate an HTTP MJPEG stream from camera images."""
handler.request.sendall(bytes('HTTP/1.1 200 OK\r\n', 'utf-8'))
handler.request.sendall(bytes(
'Content-type: multipart/x-mixed-replace; \
@ -193,7 +188,7 @@ class Camera(Entity):
@property
def state(self):
""" Returns the state of the entity. """
"""Camera state."""
if self.is_recording:
return STATE_RECORDING
elif self.is_streaming:
@ -203,11 +198,8 @@ class Camera(Entity):
@property
def state_attributes(self):
""" Returns optional state attributes. """
attr = {
ATTR_ENTITY_PICTURE: ENTITY_IMAGE_URL.format(
self.entity_id, time.time()),
}
"""Camera state attributes."""
attr = {}
if self.model:
attr['model_name'] = self.model

View File

@ -7,9 +7,11 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/camera.bloomsky/
"""
import logging
import requests
import homeassistant.components.bloomsky as bloomsky
from homeassistant.components.camera import Camera
from homeassistant.loader import get_component
DEPENDENCIES = ["bloomsky"]
@ -17,6 +19,7 @@ DEPENDENCIES = ["bloomsky"]
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" set up access to BloomSky cameras """
bloomsky = get_component('bloomsky')
for device in bloomsky.BLOOMSKY.devices.values():
add_devices_callback([BloomSkyCamera(bloomsky.BLOOMSKY, device)])

View File

@ -1,29 +1,31 @@
"""
homeassistant.components.camera.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has a fake camera.
Demo camera platform that has a fake camera.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import os
from homeassistant.components.camera import Camera
import homeassistant.util.dt as dt_util
from homeassistant.components.camera import Camera
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo camera. """
"""Setup the Demo camera platform."""
add_devices([
DemoCamera('Demo camera')
])
class DemoCamera(Camera):
""" A Demo camera. """
"""A Demo camera."""
def __init__(self, name):
super().__init__()
self._name = name
def camera_image(self):
""" Return a faked still image response. """
"""Return a faked still image response."""
now = dt_util.utcnow()
image_path = os.path.join(os.path.dirname(__file__),
@ -33,5 +35,5 @@ class DemoCamera(Camera):
@property
def name(self):
""" Return the name of this device. """
"""Return the name of this camera."""
return self._name

View File

@ -10,8 +10,8 @@ import logging
import requests
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)

View File

@ -11,8 +11,8 @@ import logging
import requests
from requests.auth import HTTPBasicAuth
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)

View File

@ -6,15 +6,15 @@ Support for IP Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.mjpeg/
"""
from contextlib import closing
import logging
from contextlib import closing
import requests
from requests.auth import HTTPBasicAuth
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.const import HTTP_OK
from homeassistant.helpers import validate_config
CONTENT_TYPE_HEADER = 'Content-Type'

View File

@ -11,10 +11,10 @@ import socket
import requests
from homeassistant.helpers import validate_config
from homeassistant.components.camera import DOMAIN, Camera
from homeassistant.helpers import validate_config
REQUIREMENTS = ['uvcclient==0.5']
REQUIREMENTS = ['uvcclient==0.8']
_LOGGER = logging.getLogger(__name__)
@ -26,8 +26,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return None
addr = config.get('nvr')
port = int(config.get('port', 7080))
key = config.get('key')
try:
port = int(config.get('port', 7080))
except ValueError:
_LOGGER.error('Invalid port number provided')
return False
from uvcclient import nvr
nvrconn = nvr.UVCRemote(addr, port, key)
@ -43,10 +47,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.error('Unable to connect to NVR: %s', str(ex))
return False
for camera in cameras:
add_devices([UnifiVideoCamera(nvrconn,
camera['uuid'],
camera['name'])])
# Filter out airCam models, which are not supported in the latest
# version of UnifiVideo and which are EOL by Ubiquiti
cameras = [camera for camera in cameras
if 'airCam' not in nvrconn.get_camera(camera['uuid'])['model']]
add_devices([UnifiVideoCamera(nvrconn,
camera['uuid'],
camera['name'])
for camera in cameras])
return True
class UnifiVideoCamera(Camera):
@ -58,6 +68,8 @@ class UnifiVideoCamera(Camera):
self._uuid = uuid
self._name = name
self.is_streaming = False
self._connect_addr = None
self._camera = None
@property
def name(self):
@ -68,24 +80,74 @@ class UnifiVideoCamera(Camera):
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['recordingSettings']['fullTimeRecordEnabled']
def camera_image(self):
@property
def brand(self):
return 'Ubiquiti'
@property
def model(self):
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['model']
def _login(self):
from uvcclient import camera as uvc_camera
from uvcclient import store as uvc_store
caminfo = self._nvr.get_camera(self._uuid)
if self._connect_addr:
addrs = [self._connect_addr]
else:
addrs = [caminfo['host'], caminfo['internalHost']]
store = uvc_store.get_info_store()
password = store.get_camera_password(self._uuid)
if password is None:
_LOGGER.debug('Logging into camera %(name)s with default password',
dict(name=self._name))
password = 'ubnt'
camera = None
for addr in [caminfo['host'], caminfo['internalHost']]:
for addr in addrs:
try:
camera = uvc_camera.UVCCameraClient(addr,
caminfo['username'],
'ubnt')
password)
camera.login()
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
dict(name=self._name, addr=addr))
self._connect_addr = addr
break
except socket.error:
pass
if not camera:
except uvc_camera.CameraConnectError:
pass
except uvc_camera.CameraAuthError:
pass
if not self._connect_addr:
_LOGGER.error('Unable to login to camera')
return None
camera.login()
return camera.get_snapshot()
self._camera = camera
return True
def camera_image(self):
from uvcclient import camera as uvc_camera
if not self._camera:
if not self._login():
return
def _get_image(retry=True):
try:
return self._camera.get_snapshot()
except uvc_camera.CameraConnectError:
_LOGGER.error('Unable to contact camera')
except uvc_camera.CameraAuthError:
if retry:
self._login()
return _get_image(retry=False)
else:
_LOGGER.error('Unable to log into camera, unable '
'to get snapshot')
raise
return _get_image()

View File

@ -11,8 +11,8 @@ the user has submitted configuration information.
"""
import logging
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.const import EVENT_TIME_CHANGED
from homeassistant.helpers.entity import generate_entity_id
DOMAIN = "configurator"
ENTITY_ID_FORMAT = DOMAIN + ".{}"

View File

@ -9,10 +9,9 @@ https://home-assistant.io/components/conversation/
import logging
import re
from homeassistant import core
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
DOMAIN = "conversation"

View File

@ -1,16 +1,15 @@
"""
homeassistant.components.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets up a demo environment that mimics interaction with devices.
For more details about this component, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import time
import homeassistant.core as ha
import homeassistant.bootstrap as bootstrap
import homeassistant.core as ha
import homeassistant.loader as loader
from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_ID)
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
DOMAIN = "demo"
@ -34,7 +33,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
def setup(hass, config):
""" Setup a demo environment. """
"""Setup a demo environment."""
group = loader.get_component('group')
configurator = loader.get_component('configurator')
@ -64,14 +63,29 @@ def setup(hass, config):
switches = sorted(hass.states.entity_ids('switch'))
media_players = sorted(hass.states.entity_ids('media_player'))
group.Group(hass, 'living room', [
lights[2], lights[1], switches[0], media_players[1],
lights[1], switches[0], 'input_select.living_room_preset',
'rollershutter.living_room_window', media_players[1],
'scene.romantic_lights'])
group.Group(hass, 'bedroom', [lights[0], switches[1],
media_players[0]])
group.Group(hass, 'Rooms', [
'group.living_room', 'group.bedroom',
group.Group(hass, 'bedroom', [lights[0], switches[1], media_players[0]])
group.Group(hass, 'kitchen', [
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door'])
group.Group(hass, 'doors', [
'lock.front_door', 'lock.kitchen_door',
'garage_door.right_garage_door', 'garage_door.left_garage_door'])
group.Group(hass, 'automations', [
'input_select.who_cooks', 'input_boolean.notify', ])
group.Group(hass, 'people', [
'device_tracker.demo_anne_therese', 'device_tracker.demo_home_boy',
'device_tracker.demo_paulus'])
group.Group(hass, 'thermostats', [
'thermostat.nest', 'thermostat.thermostat'])
group.Group(hass, 'downstairs', [
'group.living_room', 'group.kitchen',
'scene.romantic_lights', 'rollershutter.kitchen_window',
'rollershutter.living_room_window',
'rollershutter.living_room_window', 'group.doors', 'thermostat.nest',
], view=True)
group.Group(hass, 'Upstairs', [
'thermostat.thermostat', 'group.bedroom',
], view=True)
# Setup scripts
@ -113,11 +127,28 @@ def setup(hass, config):
}},
]})
# Set up input select
bootstrap.setup_component(
hass, 'input_select',
{'input_select':
{'living_room_preset': {'options': ['Visitors',
'Visitors with kids',
'Home Alone']},
'who_cooks': {'icon': 'mdi:panda',
'initial': 'Anne Therese',
'name': 'Who cooks today',
'options': ['Paulus', 'Anne Therese']}}})
# Set up input boolean
bootstrap.setup_component(
hass, 'input_boolean',
{'input_boolean': {'notify': {'icon': 'mdi:car',
'initial': False,
'name': 'Notify Anne Therese is home'}}})
# Setup configurator
configurator_ids = []
def hue_configuration_callback(data):
""" Fake callback, mark config as done. """
"""Fake callback, mark config as done."""
time.sleep(2)
# First time it is called, pretend it failed.

View File

@ -10,10 +10,11 @@ https://home-assistant.io/components/device_sun_light_trigger/
import logging
from datetime import timedelta
from homeassistant.helpers.event import track_point_in_time, track_state_change
import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from . import light, sun, device_tracker, group
from homeassistant.helpers.event import track_point_in_time, track_state_change
from . import device_tracker, group, light, sun
DOMAIN = "device_sun_light_trigger"
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']

View File

@ -25,7 +25,7 @@ import homeassistant.util.dt as dt_util
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.const import (
ATTR_ENTITY_PICTURE, ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE,
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE,
DEVICE_DEFAULT_NAME, STATE_HOME, STATE_NOT_HOME)
DOMAIN = "device_tracker"
@ -297,14 +297,16 @@ class Device(Entity):
""" State of the device. """
return self._state
@property
def entity_picture(self):
"""Picture of the device."""
return self.config_picture
@property
def state_attributes(self):
""" Device state attributes. """
attr = {}
if self.config_picture:
attr[ATTR_ENTITY_PICTURE] = self.config_picture
if self.gps:
attr[ATTR_LATITUDE] = self.gps[0]
attr[ATTR_LONGITUDE] = self.gps[1]

View File

@ -8,17 +8,17 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.actiontec/
"""
import logging
from datetime import timedelta
from collections import namedtuple
import re
import threading
import telnetlib
import threading
from collections import namedtuple
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -8,14 +8,14 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.aruba/
"""
import logging
from datetime import timedelta
import re
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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=10)

View File

@ -8,15 +8,15 @@ 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
import re
import threading
import telnetlib
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -8,15 +8,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ddwrt/
"""
import logging
from datetime import timedelta
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -10,10 +10,10 @@ https://home-assistant.io/components/device_tracker.fritz/
import logging
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
REQUIREMENTS = ['fritzconnection==0.4.6']

View File

@ -7,9 +7,9 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.icloud/
"""
import logging
import re
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.event import track_utc_time_change
_LOGGER = logging.getLogger(__name__)

View File

@ -9,9 +9,8 @@ https://home-assistant.io/components/device_tracker.locative/
import logging
from functools import partial
from homeassistant.const import (
HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME)
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME
_LOGGER = logging.getLogger(__name__)

View File

@ -7,17 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.luci/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -7,8 +7,9 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.mqtt/
"""
import logging
from homeassistant import util
import homeassistant.components.mqtt as mqtt
from homeassistant import util
DEPENDENCIES = ['mqtt']

View File

@ -8,12 +8,12 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.netgear/
"""
import logging
from datetime import timedelta
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.util import Throttle
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.util import Throttle
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)

View File

@ -7,16 +7,16 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.nmap_scanner/
"""
import logging
from datetime import timedelta
from collections import namedtuple
import subprocess
import re
import subprocess
from collections import namedtuple
from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOSTS
from homeassistant.helpers import validate_config
from homeassistant.util import Throttle, convert
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)

View File

@ -62,7 +62,7 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
def owntracks_event_update(topic, payload, qos):
# pylint: disable=too-many-branches
# pylint: disable=too-many-branches, too-many-statements
""" MQTT event (geofences) received. """
# Docs on available data:
@ -92,7 +92,10 @@ def setup_scanner(hass, config, see):
if zone is None:
if data['t'] == 'b':
# Not a HA zone, and a beacon so assume mobile
MOBILE_BEACONS_ACTIVE[dev_id].append(location)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location not in beacons:
beacons.append(location)
_LOGGER.info("Added beacon %s", location)
else:
# Normal region
if not zone.attributes.get('passive'):
@ -108,28 +111,30 @@ def setup_scanner(hass, config, see):
see_beacons(dev_id, kwargs)
elif data['event'] == 'leave':
regions = REGIONS_ENTERED[dev_id]
if location in regions:
regions.remove(location)
new_region = regions[-1] if regions else None
with LOCK:
regions = REGIONS_ENTERED[dev_id]
if location in regions:
regions.remove(location)
new_region = regions[-1] if regions else None
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
if not zone.attributes.get('passive'):
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_LOGGER.info("Exit from to %s", new_region)
if new_region:
# Exit to previous region
zone = hass.states.get("zone.{}".format(new_region))
if not zone.attributes.get('passive'):
kwargs['location_name'] = new_region
_set_gps_from_zone(kwargs, zone)
_LOGGER.info("Exit to %s", new_region)
else:
_LOGGER.info("Exit to GPS")
else:
_LOGGER.info("Exit to GPS")
see(**kwargs)
see_beacons(dev_id, kwargs)
see(**kwargs)
see_beacons(dev_id, kwargs)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location in beacons:
beacons.remove(location)
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
if location in beacons:
beacons.remove(location)
_LOGGER.info("Remove beacon %s", location)
else:
_LOGGER.error(
@ -141,6 +146,8 @@ def setup_scanner(hass, config, see):
""" Set active beacons to the current location """
kwargs = kwargs_param.copy()
# the battery state applies to the tracking device, not the beacon
kwargs.pop('battery', None)
for beacon in MOBILE_BEACONS_ACTIVE[dev_id]:
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
kwargs['host_name'] = beacon

View File

@ -7,15 +7,15 @@ through SNMP.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.snmp/
"""
import logging
from datetime import timedelta
import threading
import binascii
import logging
import threading
from datetime import timedelta
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST
from homeassistant.helpers import validate_config
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=10)

View File

@ -8,15 +8,15 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.thomson/
"""
import logging
from datetime import timedelta
import re
import threading
import telnetlib
import threading
from datetime import timedelta
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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=10)

View File

@ -7,18 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.tomato/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -9,15 +9,16 @@ https://home-assistant.io/components/device_tracker.tplink/
"""
import base64
import logging
from datetime import timedelta
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -7,17 +7,18 @@ presence.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.ubus/
"""
import logging
import json
from datetime import timedelta
import logging
import re
import threading
from datetime import timedelta
import requests
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import validate_config
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)

View File

@ -0,0 +1,79 @@
"""
homeassistant.components.device_tracker.unifi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device tracker platform that supports scanning a Unifi WAP controller
"""
import logging
import urllib
from homeassistant.components.device_tracker import DOMAIN
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
from homeassistant.helpers import validate_config
# Unifi package doesn't list urllib3 as a requirement
REQUIREMENTS = ['urllib3', 'unifi==1.2.4']
_LOGGER = logging.getLogger(__name__)
CONF_PORT = 'port'
def get_scanner(hass, config):
""" Sets up unifi device_tracker """
from unifi.controller import Controller
if not validate_config(config, {DOMAIN: [CONF_USERNAME,
CONF_PASSWORD]},
_LOGGER):
_LOGGER.error('Invalid configuration')
return False
this_config = config[DOMAIN]
host = this_config.get(CONF_HOST, 'localhost')
username = this_config.get(CONF_USERNAME)
password = this_config.get(CONF_PASSWORD)
try:
port = int(this_config.get(CONF_PORT, 8443))
except ValueError:
_LOGGER.error('Invalid port (must be numeric like 8443)')
return False
try:
ctrl = Controller(host, username, password, port, 'v4')
except urllib.error.HTTPError as ex:
_LOGGER.error('Failed to connect to unifi: %s', ex)
return False
return UnifiScanner(ctrl)
class UnifiScanner(object):
"""Provide device_tracker support from Unifi WAP client data."""
def __init__(self, controller):
self._controller = controller
self._update()
def _update(self):
try:
clients = self._controller.get_clients()
except urllib.error.HTTPError as ex:
_LOGGER.error('Failed to scan clients: %s', ex)
clients = []
self._clients = {client['mac']: client for client in clients}
def scan_devices(self):
""" Scans for devices. """
self._update()
return self._clients.keys()
def get_device_name(self, mac):
""" Returns the name (if known) of the device.
If a name has been set in Unifi, then return that, else
return the hostname if it has been detected.
"""
client = self._clients.get(mac, {})
name = client.get('name') or client.get('hostname')
_LOGGER.debug('Device %s name %s', mac, name)
return name

View File

@ -1,6 +1,4 @@
"""
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.
@ -13,8 +11,8 @@ import threading
from homeassistant import bootstrap
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_PLATFORM_DISCOVERED,
ATTR_SERVICE, ATTR_DISCOVERED)
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
EVENT_PLATFORM_DISCOVERED)
DOMAIN = "discovery"
REQUIREMENTS = ['netdisco==0.5.2']
@ -29,7 +27,7 @@ SERVICE_SONOS = 'sonos'
SERVICE_PLEX = 'plex_mediaserver'
SERVICE_HANDLERS = {
SERVICE_WEMO: "switch",
SERVICE_WEMO: "wemo",
SERVICE_CAST: "media_player",
SERVICE_HUE: "light",
SERVICE_NETGEAR: 'device_tracker',
@ -39,24 +37,41 @@ SERVICE_HANDLERS = {
def listen(hass, service, callback):
"""
Setup listener for discovery of specific service.
"""Setup listener for discovery of specific service.
Service can be a string or a list/tuple.
"""
if isinstance(service, str):
service = (service,)
else:
service = tuple(service)
def discovery_event_listener(event):
""" Listens for discovery events. """
"""Listen for discovery events."""
if event.data[ATTR_SERVICE] in service:
callback(event.data[ATTR_SERVICE], event.data[ATTR_DISCOVERED])
callback(event.data[ATTR_SERVICE], event.data.get(ATTR_DISCOVERED))
hass.bus.listen(EVENT_PLATFORM_DISCOVERED, discovery_event_listener)
def discover(hass, service, discovered=None, component=None, hass_config=None):
"""Fire discovery event.
Can ensure a component is loaded.
"""
if component is not None:
bootstrap.setup_component(hass, component, hass_config)
data = {
ATTR_SERVICE: service
}
if discovered is not None:
data[ATTR_DISCOVERED] = discovered
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, data)
def setup(hass, config):
""" Starts a discovery service. """
logger = logging.getLogger(__name__)

View File

@ -6,8 +6,8 @@ Provides functionality to download files.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/downloader/
"""
import os
import logging
import os
import re
import threading

View File

@ -1,40 +1,20 @@
"""
homeassistant.components.ecobee
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ecobee Component
This component adds support for Ecobee3 Wireless Thermostats.
You will need to setup developer access to your thermostat,
and create and API key on the ecobee website.
The first time you run this component you will see a configuration
component card in Home Assistant. This card will contain a PIN code
that you will need to use to authorize access to your thermostat. You
can do this at https://www.ecobee.com/consumerportal/index.html
Click My Apps, Add application, Enter Pin and click Authorize.
After authorizing the application click the button in the configuration
card. Now your thermostat and sensors should shown in home-assistant.
You can use the optional hold_temp parameter to set whether or not holds
are set indefintely or until the next scheduled event.
ecobee:
api_key: asdfasdfasdfasdfasdfaasdfasdfasdfasdf
hold_temp: True
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ecobee component
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ecobee/
"""
from datetime import timedelta
import logging
import os
from datetime import timedelta
from homeassistant.loader import get_component
from homeassistant import bootstrap
from homeassistant.util import Throttle
from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED, CONF_API_KEY)
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, EVENT_PLATFORM_DISCOVERED)
from homeassistant.loader import get_component
from homeassistant.util import Throttle
DOMAIN = "ecobee"
DISCOVER_THERMOSTAT = "ecobee.thermostat"
@ -82,7 +62,7 @@ def request_configuration(network, hass, config):
def setup_ecobee(hass, network, config):
""" Setup ecobee thermostat """
""" Setup Ecobee thermostat. """
# If ecobee has a PIN then it needs to be configured.
if network.pin is not None:
request_configuration(network, hass, config)

View File

@ -11,6 +11,7 @@ import logging
from . import version, mdi_version
import homeassistant.util as util
from homeassistant.const import URL_ROOT, HTTP_OK
from homeassistant.components import api
DOMAIN = 'frontend'
DEPENDENCIES = ['api']
@ -25,21 +26,23 @@ FRONTEND_URLS = [
re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)'),
]
URL_API_BOOTSTRAP = "/api/bootstrap"
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
def setup(hass, config):
""" Setup serving the frontend. """
if 'http' not in hass.config.components:
_LOGGER.error('Dependency http is not loaded')
return False
for url in FRONTEND_URLS:
hass.http.register_path('GET', url, _handle_get_root, False)
hass.http.register_path('GET', '/service_worker.js',
_handle_get_service_worker, False)
# Bootstrap API
hass.http.register_path(
'GET', URL_API_BOOTSTRAP, _handle_get_api_bootstrap)
# Static files
hass.http.register_path(
'GET', re.compile(r'/static/(?P<file>[a-zA-Z\._\-0-9/]+)'),
@ -54,6 +57,18 @@ def setup(hass, config):
return True
def _handle_get_api_bootstrap(handler, path_match, data):
""" Returns all data needed to bootstrap Home Assistant. """
hass = handler.server.hass
handler.write_json({
'config': hass.config.as_dict(),
'states': hass.states.all(),
'events': api.events_json(hass),
'services': api.services_json(hass),
})
def _handle_get_root(handler, path_match, data):
""" Renders the frontend. """
handler.send_response(HTTP_OK)

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by update_mdi script """
VERSION = "a1a203680639ff1abcc7b68cdb29c57a"
VERSION = "2f4adc5d3ad6d2f73bf69ed29b7594fd"

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "833d09737fec24f9219efae87c5bfd2a"
VERSION = "a4d021cb50ed079fcfda7369ed2f0d4a"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 1380e59e182c7d5468c55c67c8c363ff9248349a
Subproject commit 81ae753eb06a32bcac62cbee0981b1d24580e878

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.garage_door
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with garage doors that can be controlled remotely.
For more details about this component, please refer to the documentation
@ -35,32 +33,32 @@ _LOGGER = logging.getLogger(__name__)
def is_closed(hass, entity_id=None):
""" Returns if the garage door is closed based on the statemachine. """
"""Returns if the garage door is closed based on the statemachine."""
entity_id = entity_id or ENTITY_ID_ALL_GARAGE_DOORS
return hass.states.is_state(entity_id, STATE_CLOSED)
def close_door(hass, entity_id=None):
""" Closes all or specified garage door. """
"""Closes all or specified garage door."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_CLOSE, data)
def open_door(hass, entity_id=None):
""" Open all or specified garage door. """
"""Open all or specified garage door."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_OPEN, data)
def setup(hass, config):
""" Track states and offer events for garage door. """
"""Track states and offer events for garage door."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_GARAGE_DOORS)
component.setup(config)
def handle_garage_door_service(service):
""" Handles calls to the garage door services. """
"""Handles calls to the garage door services."""
target_locks = component.extract_from_service(service)
for item in target_locks:
@ -83,25 +81,24 @@ def setup(hass, config):
class GarageDoorDevice(Entity):
""" Represents a garage door. """
"""Represents a garage door."""
# pylint: disable=no-self-use
@property
def is_closed(self):
""" Is the garage door closed or opened. """
"""Return true if door is closed."""
return None
def close_door(self):
""" Closes the garage door. """
"""Close the garage door."""
raise NotImplementedError()
def open_door(self):
""" Opens the garage door. """
"""Open the garage door."""
raise NotImplementedError()
@property
def state(self):
""" State of the garage door. """
"""Returns the state of the garage door."""
closed = self.is_closed
if closed is None:
return STATE_UNKNOWN

View File

@ -1,7 +1,8 @@
"""
homeassistant.components.garage_door.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has two fake garage doors.
Demo garage door platform that has two fake doors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import STATE_CLOSED, STATE_OPEN
@ -9,7 +10,7 @@ from homeassistant.const import STATE_CLOSED, STATE_OPEN
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return demo garage doors. """
"""Setup demo garage door platform."""
add_devices_callback([
DemoGarageDoor('Left Garage Door', STATE_CLOSED),
DemoGarageDoor('Right Garage Door', STATE_OPEN)
@ -17,32 +18,32 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class DemoGarageDoor(GarageDoorDevice):
""" Provides a demo garage door. """
"""Provides a demo garage door."""
def __init__(self, name, state):
self._name = name
self._state = state
@property
def should_poll(self):
""" No polling needed for a demo garage door. """
"""No polling needed for a demo garage door."""
return False
@property
def name(self):
""" Returns the name of the device if any. """
"""Return the name of the device if any."""
return self._name
@property
def is_closed(self):
""" True if device is closed. """
"""Return true if garage door is closed."""
return self._state == STATE_CLOSED
def close_door(self, **kwargs):
""" Close the device. """
"""Close the garage door."""
self._state = STATE_CLOSED
self.update_ha_state()
def open_door(self, **kwargs):
""" Open the device. """
"""Open the garage door."""
self._state = STATE_OPEN
self.update_ha_state()

View File

@ -1,6 +1,4 @@
"""
homeassistant.components.garage_door.wink
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Wink garage doors.
For more details about this platform, please refer to the documentation at
@ -11,11 +9,11 @@ import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.6.0']
REQUIREMENTS = ['python-wink==0.6.2']
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Wink platform. """
"""Sets up the Wink garage door platform."""
import pywink
if discovery_info is None:
@ -34,34 +32,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class WinkGarageDoorDevice(GarageDoorDevice):
""" Represents a Wink garage door. """
"""Represents a Wink garage door."""
def __init__(self, wink):
self.wink = wink
@property
def unique_id(self):
""" Returns the id of this wink garage door """
"""Returns the id of this wink garage door."""
return "{}.{}".format(self.__class__, self.wink.device_id())
@property
def name(self):
""" Returns the name of the garage door if any. """
"""Returns the name of the garage door if any."""
return self.wink.name()
def update(self):
""" Update the state of the garage door. """
"""Update the state of the garage door."""
self.wink.update_state()
@property
def is_closed(self):
""" True if device is closed. """
"""Returns true if door is closed."""
return self.wink.state() == 0
def close_door(self):
""" Close the device. """
"""Closes the door."""
self.wink.set_state(0)
def open_door(self):
""" Open the device. """
"""Open the door."""
self.wink.set_state(1)

View File

@ -1,19 +1,9 @@
"""
homeassistant.components.graphite
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component that records all events and state changes and feeds the data to
a graphite installation.
a Graphite installation.
Example configuration:
graphite:
host: foobar
port: 2003
prefix: ha
All config elements are optional, and assumed to be on localhost at the
default port if not specified. Prefix is the metric prefix in graphite,
and defaults to 'ha'.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/graphite/
"""
import logging
import queue
@ -22,8 +12,7 @@ import threading
import time
from homeassistant.const import (
EVENT_STATE_CHANGED,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED)
from homeassistant.helpers import state
DOMAIN = "graphite"
@ -31,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Setup graphite feeder. """
"""Setup the Graphite feeder."""
graphite_config = config.get('graphite', {})
host = graphite_config.get('host', 'localhost')
prefix = graphite_config.get('prefix', 'ha')
@ -46,42 +35,48 @@ def setup(hass, config):
class GraphiteFeeder(threading.Thread):
""" Feeds data to graphite. """
"""Feeds data to Graphite."""
def __init__(self, hass, host, port, prefix):
super(GraphiteFeeder, self).__init__(daemon=True)
self._hass = hass
self._host = host
self._port = port
# rstrip any trailing dots in case they think they
# need it
# rstrip any trailing dots in case they think they need it
self._prefix = prefix.rstrip('.')
self._queue = queue.Queue()
self._quit_object = object()
self._we_started = False
hass.bus.listen_once(EVENT_HOMEASSISTANT_START,
self.start_listen)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
self.shutdown)
hass.bus.listen(EVENT_STATE_CHANGED, self.event_listener)
_LOGGER.debug('Graphite feeding to %s:%i initialized',
self._host, self._port)
def start_listen(self, event):
""" Start event-processing thread. """
"""Start event-processing thread."""
_LOGGER.debug('Event processing thread started')
self._we_started = True
self.start()
def shutdown(self, event):
""" Tell the thread that we are done.
This does not block because there is nothing to
clean up (and no penalty for killing in-process
connections to graphite.
"""
"""Signal shutdown of processing event."""
_LOGGER.debug('Event processing signaled exit')
self._queue.put(self._quit_object)
def event_listener(self, event):
""" Queue an event for processing. """
self._queue.put(event)
"""Queue an event for processing."""
if self.is_alive() or not self._we_started:
_LOGGER.debug('Received event')
self._queue.put(event)
else:
_LOGGER.error('Graphite feeder thread has died, not '
'queuing event!')
def _send_to_graphite(self, data):
"""Send data to Graphite."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((self._host, self._port))
@ -90,6 +85,7 @@ class GraphiteFeeder(threading.Thread):
sock.close()
def _report_attributes(self, entity_id, new_state):
"""Report the attributes."""
now = time.time()
things = dict(new_state.attributes)
try:
@ -106,17 +102,33 @@ class GraphiteFeeder(threading.Thread):
_LOGGER.debug('Sending to graphite: %s', lines)
try:
self._send_to_graphite('\n'.join(lines))
except socket.gaierror:
_LOGGER.error('Unable to connect to host %s', self._host)
except socket.error:
_LOGGER.exception('Failed to send data to graphite')
def run(self):
"""Run the process to export the data."""
while True:
event = self._queue.get()
if event == self._quit_object:
_LOGGER.debug('Event processing thread stopped')
self._queue.task_done()
return
elif (event.event_type == EVENT_STATE_CHANGED and
'new_state' in event.data):
self._report_attributes(event.data['entity_id'],
event.data['new_state'])
event.data.get('new_state')):
_LOGGER.debug('Processing STATE_CHANGED event for %s',
event.data['entity_id'])
try:
self._report_attributes(event.data['entity_id'],
event.data['new_state'])
# pylint: disable=broad-except
except Exception:
# Catch this so we can avoid the thread dying and
# make it visible.
_LOGGER.exception('Failed to process STATE_CHANGED event')
else:
_LOGGER.warning('Processing unexpected event type %s',
event.event_type)
self._queue.task_done()

View File

@ -7,13 +7,13 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/group/
"""
import homeassistant.core as ha
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.entity import (
Entity, split_entity_id, generate_entity_id)
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
STATE_UNKNOWN, CONF_NAME, CONF_ICON)
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
STATE_NOT_HOME, STATE_OFF, STATE_ON, STATE_OPEN, STATE_UNKNOWN,
ATTR_ASSUMED_STATE, )
from homeassistant.helpers.entity import (
Entity, generate_entity_id, split_entity_id)
from homeassistant.helpers.event import track_state_change
DOMAIN = 'group'
@ -145,6 +145,7 @@ class Group(Entity):
self.tracking = []
self.group_on = None
self.group_off = None
self._assumed_state = False
if entity_ids is not None:
self.update_tracked_entity_ids(entity_ids)
@ -183,6 +184,11 @@ class Group(Entity):
data[ATTR_VIEW] = True
return data
@property
def assumed_state(self):
"""Return True if unable to access real state of entity."""
return self._assumed_state
def update_tracked_entity_ids(self, entity_ids):
""" Update the tracked entity IDs. """
self.stop()
@ -208,47 +214,77 @@ class Group(Entity):
def update(self):
""" Query all the tracked states and determine current group state. """
self._state = STATE_UNKNOWN
self._update_group_state()
def _state_changed_listener(self, entity_id, old_state, new_state):
""" Listener to receive state changes of tracked entities. """
self._update_group_state(new_state)
self.update_ha_state()
@property
def _tracking_states(self):
"""States that the group is tracking."""
states = []
for entity_id in self.tracking:
state = self.hass.states.get(entity_id)
if state is not None:
self._process_tracked_state(state)
states.append(state)
def _state_changed_listener(self, entity_id, old_state, new_state):
""" Listener to receive state changes of tracked entities. """
self._process_tracked_state(new_state)
self.update_ha_state()
return states
def _process_tracked_state(self, tr_state):
""" Updates group state based on a new state of a tracked entity. """
def _update_group_state(self, tr_state=None):
"""Update group state.
Optionally you can provide the only state changed since last update
allowing this method to take shortcuts.
"""
# pylint: disable=too-many-branches
# To store current states of group entities. Might not be needed.
states = None
gr_state, gr_on, gr_off = self._state, self.group_on, self.group_off
# We have not determined type of group yet
if self.group_on is None:
self.group_on, self.group_off = _get_group_on_off(tr_state.state)
if gr_on is None:
if tr_state is None:
states = self._tracking_states
if self.group_on is not None:
# New state of the group is going to be based on the first
# state that we can recognize
self._state = tr_state.state
for state in states:
gr_on, gr_off = \
_get_group_on_off(state.state)
if gr_on is not None:
break
else:
gr_on, gr_off = _get_group_on_off(tr_state.state)
if gr_on is not None:
self.group_on, self.group_off = gr_on, gr_off
# We cannot determine state of the group
if gr_on is None:
return
# There is already a group state
cur_gr_state = self._state
group_on, group_off = self.group_on, self.group_off
if tr_state is None or (gr_state == gr_on and
tr_state.state == gr_off):
if states is None:
states = self._tracking_states
# if cur_gr_state = OFF and tr_state = ON: set ON
# if cur_gr_state = ON and tr_state = OFF: research
# else: ignore
if any(state.state == gr_on for state in states):
self._state = gr_on
else:
self._state = gr_off
if cur_gr_state == group_off and tr_state.state == group_on:
self._state = group_on
elif tr_state.state in (gr_on, gr_off):
self._state = tr_state.state
elif cur_gr_state == group_on and tr_state.state == group_off:
if tr_state is None or self._assumed_state and \
not tr_state.attributes.get(ATTR_ASSUMED_STATE):
if states is None:
states = self._tracking_states
# Set to off if no other states are on
if not any(self.hass.states.is_state(ent_id, group_on)
for ent_id in self.tracking
if tr_state.entity_id != ent_id):
self._state = group_off
self._assumed_state = any(state.attributes.get(ATTR_ASSUMED_STATE)
for state in states)
elif tr_state.attributes.get(ATTR_ASSUMED_STATE):
self._assumed_state = True

View File

@ -7,12 +7,12 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/history/
"""
import re
from collections import defaultdict
from datetime import timedelta
from itertools import groupby
from collections import defaultdict
import homeassistant.util.dt as dt_util
import homeassistant.components.recorder as recorder
import homeassistant.util.dt as dt_util
from homeassistant.const import HTTP_BAD_REQUEST
DOMAIN = 'history'

View File

@ -6,30 +6,31 @@ 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/
"""
from datetime import timedelta
import gzip
from http import cookies
from http.server import SimpleHTTPRequestHandler, HTTPServer
import json
import logging
import os
from socketserver import ThreadingMixIn
import ssl
import threading
import time
from urllib.parse import urlparse, parse_qs
from datetime import timedelta
from http import cookies
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
from urllib.parse import parse_qs, urlparse
import homeassistant.bootstrap as bootstrap
import homeassistant.core as ha
from homeassistant.const import (
SERVER_PORT, CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT_PLAIN,
HTTP_HEADER_HA_AUTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_ACCEPT_ENCODING,
HTTP_HEADER_CONTENT_ENCODING, HTTP_HEADER_VARY, HTTP_HEADER_CONTENT_LENGTH,
HTTP_HEADER_CACHE_CONTROL, HTTP_HEADER_EXPIRES, HTTP_OK, HTTP_UNAUTHORIZED,
HTTP_NOT_FOUND, HTTP_METHOD_NOT_ALLOWED, HTTP_UNPROCESSABLE_ENTITY)
import homeassistant.remote as rem
import homeassistant.util as util
import homeassistant.util.dt as date_util
import homeassistant.bootstrap as bootstrap
from homeassistant.const import (
CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT_PLAIN, HTTP_HEADER_ACCEPT_ENCODING,
HTTP_HEADER_CACHE_CONTROL, HTTP_HEADER_CONTENT_ENCODING,
HTTP_HEADER_CONTENT_LENGTH, HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_EXPIRES,
HTTP_HEADER_HA_AUTH, HTTP_HEADER_VARY, HTTP_METHOD_NOT_ALLOWED,
HTTP_NOT_FOUND, HTTP_OK, HTTP_UNAUTHORIZED, HTTP_UNPROCESSABLE_ENTITY,
SERVER_PORT)
DOMAIN = "http"

View File

@ -7,6 +7,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/ifttt/
"""
import logging
import requests
from homeassistant.helpers import validate_config

View File

@ -1,16 +1,15 @@
"""
homeassistant.components.influxdb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InfluxDB component which allows you to send data to an Influx database.
A component which allows you to send data to an Influx database.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/influxdb/
"""
import logging
import homeassistant.util as util
from homeassistant.helpers import validate_config
from homeassistant.const import EVENT_STATE_CHANGED, STATE_UNKNOWN
from homeassistant.helpers import state as state_helper
from homeassistant.const import (EVENT_STATE_CHANGED, STATE_UNKNOWN)
from homeassistant.helpers import validate_config
_LOGGER = logging.getLogger(__name__)
@ -35,8 +34,7 @@ CONF_VERIFY_SSL = 'verify_ssl'
def setup(hass, config):
""" Setup the InfluxDB component. """
"""Setup the InfluxDB component."""
from influxdb import InfluxDBClient, exceptions
if not validate_config(config, {DOMAIN: ['host',
@ -62,13 +60,12 @@ def setup(hass, config):
influx.query("select * from /.*/ LIMIT 1;")
except exceptions.InfluxDBClientError as exc:
_LOGGER.error("Database host is not accessible due to '%s', please "
"check your entries in the configuration file and that"
" the database exists and is READ/WRITE.", exc)
"check your entries in the configuration file and that "
"the database exists and is READ/WRITE.", exc)
return False
def influx_event_listener(event):
""" Listen for new messages on the bus and sends them to Influx. """
"""Listen for new messages on the bus and sends them to Influx."""
state = event.data.get('new_state')
if state is None or state.state in (STATE_UNKNOWN, ''):
return

View File

@ -9,9 +9,9 @@ at https://home-assistant.io/components/input_boolean/
import logging
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.helpers.entity_component import EntityComponent
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
DOMAIN = 'input_boolean'

View File

@ -9,8 +9,8 @@ at https://home-assistant.io/components/input_select/
import logging
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.util import slugify
DOMAIN = 'input_select'

View File

@ -7,13 +7,14 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/insteon_hub/
"""
import logging
import homeassistant.bootstrap as bootstrap
from homeassistant.helpers import validate_config
from homeassistant.loader import get_component
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY, ATTR_DISCOVERED,
ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME,
EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.loader import get_component
DOMAIN = "insteon_hub"
REQUIREMENTS = ['insteon_hub==0.4.5']

View File

@ -11,13 +11,12 @@ import logging
from urllib.parse import urlparse
from homeassistant import bootstrap
from homeassistant.loader import get_component
from homeassistant.const import (
ATTR_DISCOVERED, ATTR_SERVICE, CONF_HOST, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED)
from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
CONF_HOST, CONF_USERNAME, CONF_PASSWORD, EVENT_PLATFORM_DISCOVERED,
EVENT_HOMEASSISTANT_STOP, ATTR_SERVICE, ATTR_DISCOVERED,
ATTR_FRIENDLY_NAME)
from homeassistant.loader import get_component
DOMAIN = "isy994"
REQUIREMENTS = ['PyISY==1.0.5']
@ -147,7 +146,7 @@ class ISYDeviceABC(ToggleEntity):
@property
def state_attributes(self):
""" Returns the state attributes for the node. """
attr = {ATTR_FRIENDLY_NAME: self.name}
attr = {}
for name, prop in self._attrs.items():
attr[name] = getattr(self, prop)
attr = self._attr_filter(attr)

View File

@ -7,10 +7,9 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/keyboard/
"""
from homeassistant.const import (
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK,
SERVICE_MEDIA_PLAY_PAUSE)
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_UP)
DOMAIN = "keyboard"
REQUIREMENTS = ['pyuserinput==0.1.9']

View File

@ -11,7 +11,7 @@ import os
import csv
from homeassistant.components import (
group, discovery, wink, isy994, zwave, insteon_hub, mysensors)
group, discovery, wemo, wink, isy994, zwave, insteon_hub, mysensors)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
@ -59,6 +59,7 @@ LIGHT_PROFILES_FILE = "light_profiles.csv"
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wemo.DISCOVER_LIGHTS: 'wemo',
wink.DISCOVER_LIGHTS: 'wink',
insteon_hub.DISCOVER_LIGHTS: 'insteon_hub',
isy994.DISCOVER_LIGHTS: 'isy994',

View File

@ -8,7 +8,7 @@ https://home-assistant.io/components/light.blinksticklight/
"""
import logging
from homeassistant.components.light import Light, ATTR_RGB_COLOR
from homeassistant.components.light import ATTR_RGB_COLOR, Light
_LOGGER = logging.getLogger(__name__)

Some files were not shown because too many files have changed in this diff Show More