mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
commit
50bf147d73
15
.coveragerc
15
.coveragerc
@ -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
31
.github/ISSUE_TEMPLATE.md
vendored
Normal 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
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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
|
||||
|
26
.travis.yml
26
.travis.yml
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 && \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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):
|
||||
|
@ -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 = {
|
||||
|
@ -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'
|
||||
|
||||
|
||||
|
@ -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),
|
||||
])
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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()]
|
||||
|
@ -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']
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
74
homeassistant/components/binary_sensor/bloomsky.py
Normal file
74
homeassistant/components/binary_sensor/bloomsky.py
Normal 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]
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
168
homeassistant/components/binary_sensor/mysensors.py
Normal file
168
homeassistant/components/binary_sensor/mysensors.py
Normal 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
|
@ -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))
|
||||
|
131
homeassistant/components/binary_sensor/nx584.py
Normal file
131
homeassistant/components/binary_sensor/nx584.py
Normal 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)
|
@ -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()
|
||||
|
@ -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
|
||||
|
31
homeassistant/components/binary_sensor/tcp.py
Normal file
31
homeassistant/components/binary_sensor/tcp.py
Normal 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]
|
122
homeassistant/components/binary_sensor/template.py
Normal file
122
homeassistant/components/binary_sensor/template.py
Normal 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'
|
81
homeassistant/components/binary_sensor/wink.py
Normal file
81
homeassistant/components/binary_sensor/wink.py
Normal 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()
|
@ -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))
|
||||
])
|
||||
|
131
homeassistant/components/binary_sensor/zwave.py
Normal file
131
homeassistant/components/binary_sensor/zwave.py
Normal 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())
|
@ -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
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)])
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 + ".{}"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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']
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
79
homeassistant/components/device_tracker/unifi.py
Normal file
79
homeassistant/components/device_tracker/unifi.py
Normal 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
|
@ -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__)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -1,2 +1,2 @@
|
||||
""" DO NOT MODIFY. Auto-generated by update_mdi script """
|
||||
VERSION = "a1a203680639ff1abcc7b68cdb29c57a"
|
||||
VERSION = "2f4adc5d3ad6d2f73bf69ed29b7594fd"
|
||||
|
@ -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
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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']
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
@ -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',
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user