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/*/verisure.py
|
homeassistant/components/*/verisure.py
|
||||||
|
|
||||||
|
homeassistant/components/wemo.py
|
||||||
|
homeassistant/components/*/wemo.py
|
||||||
|
|
||||||
homeassistant/components/wink.py
|
homeassistant/components/wink.py
|
||||||
homeassistant/components/*/wink.py
|
homeassistant/components/*/wink.py
|
||||||
|
|
||||||
@ -66,7 +69,10 @@ omit =
|
|||||||
homeassistant/components/binary_sensor/arest.py
|
homeassistant/components/binary_sensor/arest.py
|
||||||
homeassistant/components/binary_sensor/rest.py
|
homeassistant/components/binary_sensor/rest.py
|
||||||
homeassistant/components/browser.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/actiontec.py
|
||||||
homeassistant/components/device_tracker/aruba.py
|
homeassistant/components/device_tracker/aruba.py
|
||||||
homeassistant/components/device_tracker/asuswrt.py
|
homeassistant/components/device_tracker/asuswrt.py
|
||||||
@ -110,6 +116,7 @@ omit =
|
|||||||
homeassistant/components/notify/pushetta.py
|
homeassistant/components/notify/pushetta.py
|
||||||
homeassistant/components/notify/pushover.py
|
homeassistant/components/notify/pushover.py
|
||||||
homeassistant/components/notify/rest.py
|
homeassistant/components/notify/rest.py
|
||||||
|
homeassistant/components/notify/sendgrid.py
|
||||||
homeassistant/components/notify/slack.py
|
homeassistant/components/notify/slack.py
|
||||||
homeassistant/components/notify/smtp.py
|
homeassistant/components/notify/smtp.py
|
||||||
homeassistant/components/notify/syslog.py
|
homeassistant/components/notify/syslog.py
|
||||||
@ -119,6 +126,7 @@ omit =
|
|||||||
homeassistant/components/sensor/arest.py
|
homeassistant/components/sensor/arest.py
|
||||||
homeassistant/components/sensor/bitcoin.py
|
homeassistant/components/sensor/bitcoin.py
|
||||||
homeassistant/components/sensor/cpuspeed.py
|
homeassistant/components/sensor/cpuspeed.py
|
||||||
|
homeassistant/components/sensor/deutsche_bahn.py
|
||||||
homeassistant/components/sensor/dht.py
|
homeassistant/components/sensor/dht.py
|
||||||
homeassistant/components/sensor/dweet.py
|
homeassistant/components/sensor/dweet.py
|
||||||
homeassistant/components/sensor/efergy.py
|
homeassistant/components/sensor/efergy.py
|
||||||
@ -126,10 +134,12 @@ omit =
|
|||||||
homeassistant/components/sensor/forecast.py
|
homeassistant/components/sensor/forecast.py
|
||||||
homeassistant/components/sensor/glances.py
|
homeassistant/components/sensor/glances.py
|
||||||
homeassistant/components/sensor/netatmo.py
|
homeassistant/components/sensor/netatmo.py
|
||||||
|
homeassistant/components/sensor/neurio_energy.py
|
||||||
homeassistant/components/sensor/onewire.py
|
homeassistant/components/sensor/onewire.py
|
||||||
homeassistant/components/sensor/openweathermap.py
|
homeassistant/components/sensor/openweathermap.py
|
||||||
homeassistant/components/sensor/rest.py
|
homeassistant/components/sensor/rest.py
|
||||||
homeassistant/components/sensor/sabnzbd.py
|
homeassistant/components/sensor/sabnzbd.py
|
||||||
|
homeassistant/components/sensor/steam_online.py
|
||||||
homeassistant/components/sensor/speedtest.py
|
homeassistant/components/sensor/speedtest.py
|
||||||
homeassistant/components/sensor/swiss_public_transport.py
|
homeassistant/components/sensor/swiss_public_transport.py
|
||||||
homeassistant/components/sensor/systemmonitor.py
|
homeassistant/components/sensor/systemmonitor.py
|
||||||
@ -141,15 +151,14 @@ omit =
|
|||||||
homeassistant/components/sensor/worldclock.py
|
homeassistant/components/sensor/worldclock.py
|
||||||
homeassistant/components/switch/arest.py
|
homeassistant/components/switch/arest.py
|
||||||
homeassistant/components/switch/edimax.py
|
homeassistant/components/switch/edimax.py
|
||||||
|
homeassistant/components/switch/dlink.py
|
||||||
homeassistant/components/switch/hikvisioncam.py
|
homeassistant/components/switch/hikvisioncam.py
|
||||||
homeassistant/components/switch/mystrom.py
|
homeassistant/components/switch/mystrom.py
|
||||||
homeassistant/components/switch/orvibo.py
|
homeassistant/components/switch/orvibo.py
|
||||||
homeassistant/components/switch/rest.py
|
homeassistant/components/switch/rest.py
|
||||||
homeassistant/components/switch/transmission.py
|
homeassistant/components/switch/transmission.py
|
||||||
homeassistant/components/switch/wemo.py
|
|
||||||
homeassistant/components/thermostat/heatmiser.py
|
homeassistant/components/thermostat/heatmiser.py
|
||||||
homeassistant/components/thermostat/homematic.py
|
homeassistant/components/thermostat/homematic.py
|
||||||
homeassistant/components/thermostat/honeywell.py
|
|
||||||
homeassistant/components/thermostat/proliphix.py
|
homeassistant/components/thermostat/proliphix.py
|
||||||
homeassistant/components/thermostat/radiotherm.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
|
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:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.cache/pip
|
- $HOME/.cache/pip
|
||||||
# - "$HOME/virtualenv/python$TRAVIS_PYTHON_VERSION"
|
install: pip install -U tox coveralls
|
||||||
python:
|
language: python
|
||||||
- 3.4
|
script: tox
|
||||||
- 3.5
|
after_success: coveralls
|
||||||
install:
|
|
||||||
- "true"
|
|
||||||
script:
|
|
||||||
- script/cibuild
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
|
@ -6,7 +6,7 @@ The process is straight-forward.
|
|||||||
|
|
||||||
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
|
- Fork the Home Assistant [git repository](https://github.com/balloob/home-assistant).
|
||||||
- Write the code for your device, notification service, sensor, or IoT thing.
|
- 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.
|
- 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.
|
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:
|
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`.
|
- 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).
|
- 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.
|
- 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:
|
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.
|
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
|
### 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.
|
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
|
RUN mkdir -p /usr/src/app
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
RUN pip3 install --no-cache-dir colorlog
|
||||||
|
|
||||||
# For the nmap tracker
|
# For the nmap tracker
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends nmap net-tools && \
|
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:
|
Example component to target an entity_id to:
|
||||||
- turn it on at 7AM in the morning
|
- 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 device_tracker
|
||||||
from homeassistant.components import light
|
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"
|
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
|
# We depend on group because group will be loaded after all the components that
|
||||||
# initialize devices have been setup.
|
# initialize devices have been setup.
|
||||||
DEPENDENCIES = ['group', 'device_tracker', 'light']
|
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'
|
CONF_TARGET = 'target'
|
||||||
|
|
||||||
# Variable for storing configuration parameters
|
# Variable for storing configuration parameters.
|
||||||
TARGET_ID = None
|
TARGET_ID = None
|
||||||
|
|
||||||
# Name of the service that we expose
|
# Name of the service that we expose.
|
||||||
SERVICE_FLASH = 'flash'
|
SERVICE_FLASH = 'flash'
|
||||||
|
|
||||||
# Shortcut for the logger
|
# Shortcut for the logger
|
||||||
@ -59,16 +58,16 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup example component. """
|
"""Setup example component."""
|
||||||
global TARGET_ID
|
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):
|
if not validate_config(config, {DOMAIN: [CONF_TARGET]}, _LOGGER):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
TARGET_ID = config[DOMAIN][CONF_TARGET]
|
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:
|
if hass.states.get(TARGET_ID) is None:
|
||||||
_LOGGER.error("Target entity id %s does not exist",
|
_LOGGER.error("Target entity id %s does not exist",
|
||||||
TARGET_ID)
|
TARGET_ID)
|
||||||
@ -78,13 +77,13 @@ def setup(hass, config):
|
|||||||
TARGET_ID = None
|
TARGET_ID = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Tell the bootstrapper that we initialized successfully
|
# Tell the bootstrapper that we initialized successfully.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
|
@track_state_change(device_tracker.ENTITY_ID_ALL_DEVICES)
|
||||||
def track_devices(hass, entity_id, old_state, new_state):
|
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 the target id is not set, return
|
||||||
if not TARGET_ID:
|
if not TARGET_ID:
|
||||||
return
|
return
|
||||||
@ -94,7 +93,7 @@ def track_devices(hass, entity_id, old_state, new_state):
|
|||||||
|
|
||||||
core.turn_on(hass, TARGET_ID)
|
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):
|
elif new_state.state == STATE_NOT_HOME and core.is_on(hass, TARGET_ID):
|
||||||
|
|
||||||
core.turn_off(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)
|
@track_state_change(light.ENTITY_ID_ALL_LIGHTS, STATE_ON, STATE_OFF)
|
||||||
def all_lights_off(hass, entity_id, old_state, new_state):
|
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:
|
if not TARGET_ID:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
custom_components.hello_world
|
The "hello world" custom component.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Implements the bare minimum that a component should implement.
|
This component implements the bare minimum that a component should implement.
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
|
|
||||||
@ -11,18 +11,18 @@ configuration.yaml file.
|
|||||||
hello_world:
|
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"
|
DOMAIN = "hello_world"
|
||||||
|
|
||||||
# List of component names (string) your component depends upon
|
# List of component names (string) your component depends upon.
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
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!')
|
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
|
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
|
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.
|
state of an entity to the last message received on that topic.
|
||||||
|
|
||||||
@ -15,45 +15,41 @@ configuration.yaml file.
|
|||||||
|
|
||||||
mqtt_example:
|
mqtt_example:
|
||||||
topic: home-assistant/mqtt_example
|
topic: home-assistant/mqtt_example
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import homeassistant.loader as loader
|
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"
|
DOMAIN = "mqtt_example"
|
||||||
|
|
||||||
# List of component names (string) your component depends upon
|
# List of component names (string) your component depends upon.
|
||||||
DEPENDENCIES = ['mqtt']
|
DEPENDENCIES = ['mqtt']
|
||||||
|
|
||||||
|
|
||||||
CONF_TOPIC = 'topic'
|
CONF_TOPIC = 'topic'
|
||||||
DEFAULT_TOPIC = 'home-assistant/mqtt_example'
|
DEFAULT_TOPIC = 'home-assistant/mqtt_example'
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup our mqtt_example component. """
|
"""Setup the MQTT example component."""
|
||||||
mqtt = loader.get_component('mqtt')
|
mqtt = loader.get_component('mqtt')
|
||||||
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
|
topic = config[DOMAIN].get('topic', DEFAULT_TOPIC)
|
||||||
entity_id = 'mqtt_example.last_message'
|
entity_id = 'mqtt_example.last_message'
|
||||||
|
|
||||||
# Listen to a message on MQTT
|
# Listen to a message on MQTT.
|
||||||
|
|
||||||
def message_received(topic, payload, qos):
|
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)
|
hass.states.set(entity_id, payload)
|
||||||
|
|
||||||
mqtt.subscribe(hass, topic, message_received)
|
mqtt.subscribe(hass, topic, message_received)
|
||||||
|
|
||||||
hass.states.set(entity_id, 'No messages')
|
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):
|
def set_state_service(call):
|
||||||
""" Service to send a message. """
|
"""Service to send a message."""
|
||||||
mqtt.publish(hass, topic, call.data.get('new_state'))
|
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)
|
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
|
return True
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
""" Starts home assistant. """
|
""" Starts home assistant. """
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from multiprocessing import Process
|
import argparse
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import os
|
|
||||||
import argparse
|
|
||||||
import time
|
import time
|
||||||
|
from multiprocessing import Process
|
||||||
|
|
||||||
from homeassistant import bootstrap
|
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
from homeassistant.const import (__version__, EVENT_HOMEASSISTANT_START,
|
from homeassistant import bootstrap
|
||||||
RESTART_EXIT_CODE)
|
from homeassistant.const import (
|
||||||
|
EVENT_HOMEASSISTANT_START, RESTART_EXIT_CODE, __version__)
|
||||||
|
|
||||||
|
|
||||||
def validate_python():
|
def validate_python():
|
||||||
|
@ -1,37 +1,31 @@
|
|||||||
"""
|
"""Provides methods to bootstrap a home assistant instance."""
|
||||||
homeassistant.bootstrap
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
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
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
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 as core_components
|
||||||
import homeassistant.components.group as group
|
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 import event_decorators, service
|
||||||
from homeassistant.helpers.entity import Entity
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
_SETUP_LOCK = RLock()
|
||||||
|
_CURRENT_SETUP = []
|
||||||
|
|
||||||
ATTR_COMPONENT = 'component'
|
ATTR_COMPONENT = 'component'
|
||||||
|
|
||||||
@ -78,42 +72,57 @@ def _handle_requirements(hass, component, name):
|
|||||||
|
|
||||||
|
|
||||||
def _setup_component(hass, domain, config):
|
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:
|
if domain in hass.config.components:
|
||||||
return True
|
return True
|
||||||
component = loader.get_component(domain)
|
|
||||||
|
|
||||||
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', [])
|
with _SETUP_LOCK:
|
||||||
if dep not in hass.config.components]
|
# It might have been loaded while waiting for lock
|
||||||
|
if domain in hass.config.components:
|
||||||
|
return True
|
||||||
|
|
||||||
if missing_deps:
|
if domain in _CURRENT_SETUP:
|
||||||
_LOGGER.error(
|
_LOGGER.error('Attempt made to setup %s during setup of %s',
|
||||||
'Not initializing %s because not all dependencies loaded: %s',
|
domain, domain)
|
||||||
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)
|
|
||||||
return False
|
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
|
if missing_deps:
|
||||||
# it communicates with devices
|
_LOGGER.error(
|
||||||
if group.DOMAIN not in getattr(component, 'DEPENDENCIES', []):
|
'Not initializing %s because not all dependencies loaded: %s',
|
||||||
hass.pool.add_worker()
|
domain, ", ".join(missing_deps))
|
||||||
|
return False
|
||||||
|
|
||||||
hass.bus.fire(
|
if not _handle_requirements(hass, component, domain):
|
||||||
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: 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):
|
def prepare_setup_platform(hass, config, domain, platform_name):
|
||||||
|
@ -21,7 +21,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
|||||||
|
|
||||||
# Maps discovered services to their platforms
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
verisure.DISCOVER_SENSORS: 'verisure'
|
verisure.DISCOVER_ALARMS: 'verisure'
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVICE_TO_METHOD = {
|
SERVICE_TO_METHOD = {
|
||||||
|
@ -9,18 +9,16 @@ https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.components.alarm_control_panel as alarm
|
import homeassistant.components.alarm_control_panel as alarm
|
||||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_UNKNOWN,
|
CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
|
||||||
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
|
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
REQUIREMENTS = ['https://github.com/Xorso/pyalarmdotcom'
|
REQUIREMENTS = ['https://github.com/Xorso/pyalarmdotcom'
|
||||||
'/archive/0.0.7.zip'
|
'/archive/0.1.1.zip'
|
||||||
'#pyalarmdotcom==0.0.7']
|
'#pyalarmdotcom==0.1.1']
|
||||||
DEFAULT_NAME = 'Alarm.com'
|
DEFAULT_NAME = 'Alarm.com'
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.demo
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Demo platform that has two fake alarm control panels.
|
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
|
import homeassistant.components.alarm_control_panel.manual as manual
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
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([
|
add_devices([
|
||||||
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10),
|
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
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/alarm_control_panel.manual/
|
https://home-assistant.io/components/alarm_control_panel.manual/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
import datetime
|
import datetime
|
||||||
import homeassistant.components.alarm_control_panel as alarm
|
import logging
|
||||||
from homeassistant.helpers.event import track_point_in_time
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
|
import homeassistant.components.alarm_control_panel as alarm
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.const import (
|
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_ALARM_PENDING, STATE_ALARM_TRIGGERED)
|
||||||
|
from homeassistant.helpers.event import track_point_in_time
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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/
|
https://home-assistant.io/components/alarm_control_panel.mqtt/
|
||||||
"""
|
"""
|
||||||
import logging
|
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 (
|
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)
|
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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/
|
https://home-assistant.io/components/alarm_control_panel.nx584/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
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
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ https://home-assistant.io/components/verisure/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.components.verisure as verisure
|
|
||||||
import homeassistant.components.alarm_control_panel as alarm
|
import homeassistant.components.alarm_control_panel as alarm
|
||||||
|
from homeassistant.components.verisure import HUB as hub
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_UNKNOWN,
|
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
|
||||||
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
|
STATE_UNKNOWN)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -21,18 +21,13 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the Verisure platform. """
|
""" 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 = []
|
||||||
|
if int(hub.config.get('alarm', '1')):
|
||||||
alarms.extend([
|
hub.update_alarms()
|
||||||
VerisureAlarm(value)
|
alarms.extend([
|
||||||
for value in verisure.ALARM_STATUS.values()
|
VerisureAlarm(value.id)
|
||||||
if verisure.SHOW_ALARM
|
for value in hub.alarm_status.values()
|
||||||
])
|
])
|
||||||
|
|
||||||
add_devices(alarms)
|
add_devices(alarms)
|
||||||
|
|
||||||
|
|
||||||
@ -40,9 +35,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
class VerisureAlarm(alarm.AlarmControlPanel):
|
class VerisureAlarm(alarm.AlarmControlPanel):
|
||||||
""" Represents a Verisure alarm status. """
|
""" Represents a Verisure alarm status. """
|
||||||
|
|
||||||
def __init__(self, alarm_status):
|
def __init__(self, device_id):
|
||||||
self._id = alarm_status.id
|
self._id = device_id
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
|
self._digits = int(hub.config.get('code_digits', '4'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -56,41 +52,41 @@ class VerisureAlarm(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self):
|
def code_format(self):
|
||||||
""" Four digit code required. """
|
""" code format as regex """
|
||||||
return '^\\d{%s}$' % verisure.CODE_DIGITS
|
return '^\\d{%s}$' % self._digits
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Update alarm status """
|
""" 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
|
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
|
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
|
self._state = STATE_ALARM_ARMED_AWAY
|
||||||
elif verisure.ALARM_STATUS[self._id].status != 'pending':
|
elif hub.alarm_status[self._id].status != 'pending':
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'Unknown alarm state %s',
|
'Unknown alarm state %s',
|
||||||
verisure.ALARM_STATUS[self._id].status)
|
hub.alarm_status[self._id].status)
|
||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
def alarm_disarm(self, code=None):
|
||||||
""" Send disarm command. """
|
""" Send disarm command. """
|
||||||
verisure.MY_PAGES.alarm.set(code, 'DISARMED')
|
hub.my_pages.alarm.set(code, 'DISARMED')
|
||||||
_LOGGER.info('verisure alarm disarming')
|
_LOGGER.info('verisure alarm disarming')
|
||||||
verisure.MY_PAGES.alarm.wait_while_pending()
|
hub.my_pages.alarm.wait_while_pending()
|
||||||
verisure.update_alarm()
|
self.update()
|
||||||
|
|
||||||
def alarm_arm_home(self, code=None):
|
def alarm_arm_home(self, code=None):
|
||||||
""" Send arm home command. """
|
""" 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')
|
_LOGGER.info('verisure alarm arming home')
|
||||||
verisure.MY_PAGES.alarm.wait_while_pending()
|
hub.my_pages.alarm.wait_while_pending()
|
||||||
verisure.update_alarm()
|
self.update()
|
||||||
|
|
||||||
def alarm_arm_away(self, code=None):
|
def alarm_arm_away(self, code=None):
|
||||||
""" Send arm away command. """
|
""" 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')
|
_LOGGER.info('verisure alarm arming away')
|
||||||
verisure.MY_PAGES.alarm.wait_while_pending()
|
hub.my_pages.alarm.wait_while_pending()
|
||||||
verisure.update_alarm()
|
self.update()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
components.alexa
|
Support for Alexa skill service end point.
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
Component to offer a service end point for an Alexa skill.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/alexa/
|
https://home-assistant.io/components/alexa/
|
||||||
@ -10,8 +8,8 @@ import enum
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY
|
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.service import call_from_config
|
||||||
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
DOMAIN = 'alexa'
|
DOMAIN = 'alexa'
|
||||||
DEPENDENCIES = ['http']
|
DEPENDENCIES = ['http']
|
||||||
@ -28,7 +26,7 @@ CONF_ACTION = 'action'
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Activate Alexa component. """
|
"""Activate Alexa component."""
|
||||||
_CONFIG.update(config[DOMAIN].get(CONF_INTENTS, {}))
|
_CONFIG.update(config[DOMAIN].get(CONF_INTENTS, {}))
|
||||||
|
|
||||||
hass.http.register_path('POST', API_ENDPOINT, _handle_alexa, True)
|
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):
|
def _handle_alexa(handler, path_match, data):
|
||||||
""" Handle Alexa. """
|
"""Handle Alexa."""
|
||||||
_LOGGER.debug('Received Alexa request: %s', data)
|
_LOGGER.debug('Received Alexa request: %s', data)
|
||||||
|
|
||||||
req = data.get('request')
|
req = data.get('request')
|
||||||
@ -99,19 +97,19 @@ def _handle_alexa(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
class SpeechType(enum.Enum):
|
class SpeechType(enum.Enum):
|
||||||
""" Alexa speech types. """
|
"""Alexa speech types."""
|
||||||
plaintext = "PlainText"
|
plaintext = "PlainText"
|
||||||
ssml = "SSML"
|
ssml = "SSML"
|
||||||
|
|
||||||
|
|
||||||
class CardType(enum.Enum):
|
class CardType(enum.Enum):
|
||||||
""" Alexa card types. """
|
"""Alexa card types."""
|
||||||
simple = "Simple"
|
simple = "Simple"
|
||||||
link_account = "LinkAccount"
|
link_account = "LinkAccount"
|
||||||
|
|
||||||
|
|
||||||
class AlexaResponse(object):
|
class AlexaResponse(object):
|
||||||
""" Helps generating the response for Alexa. """
|
"""Helps generating the response for Alexa."""
|
||||||
|
|
||||||
def __init__(self, hass, intent=None):
|
def __init__(self, hass, intent=None):
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
@ -154,7 +152,7 @@ class AlexaResponse(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def add_reprompt(self, speech_type, text):
|
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
|
assert self.reprompt is None
|
||||||
|
|
||||||
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
|
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
|
||||||
@ -165,7 +163,7 @@ class AlexaResponse(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
""" Returns response in an Alexa valid dict. """
|
"""Returns response in an Alexa valid dict."""
|
||||||
response = {
|
response = {
|
||||||
'shouldEndSession': self.should_end_session
|
'shouldEndSession': self.should_end_session
|
||||||
}
|
}
|
||||||
@ -188,5 +186,5 @@ class AlexaResponse(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _render(self, template_string):
|
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)
|
return template.render(self.hass, template_string, self.variables)
|
||||||
|
@ -1,31 +1,27 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.api
|
Rest API for Home Assistant.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides a Rest API for Home Assistant.
|
|
||||||
|
|
||||||
For more details about the RESTful API, please refer to the documentation at
|
For more details about the RESTful API, please refer to the documentation at
|
||||||
https://home-assistant.io/developers/api/
|
https://home-assistant.io/developers/api/
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
import logging
|
|
||||||
import threading
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
from homeassistant.exceptions import TemplateError
|
|
||||||
from homeassistant.helpers.state import TrackStates
|
|
||||||
import homeassistant.remote as rem
|
import homeassistant.remote as rem
|
||||||
from homeassistant.util import template
|
|
||||||
from homeassistant.bootstrap import ERROR_LOG_FILENAME
|
from homeassistant.bootstrap import ERROR_LOG_FILENAME
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM,
|
CONTENT_TYPE_TEXT_PLAIN, EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
|
||||||
URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, URL_API_COMPONENTS,
|
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_HEADER_CONTENT_TYPE, HTTP_NOT_FOUND,
|
||||||
URL_API_CONFIG, URL_API_BOOTSTRAP, URL_API_ERROR_LOG, URL_API_LOG_OUT,
|
HTTP_OK, HTTP_UNPROCESSABLE_ENTITY, MATCH_ALL, URL_API, URL_API_COMPONENTS,
|
||||||
URL_API_TEMPLATE, EVENT_TIME_CHANGED, EVENT_HOMEASSISTANT_STOP, MATCH_ALL,
|
URL_API_CONFIG, URL_API_ERROR_LOG, URL_API_EVENT_FORWARD, URL_API_EVENTS,
|
||||||
HTTP_OK, HTTP_CREATED, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
|
URL_API_LOG_OUT, URL_API_SERVICES, URL_API_STATES, URL_API_STATES_ENTITY,
|
||||||
HTTP_UNPROCESSABLE_ENTITY, HTTP_HEADER_CONTENT_TYPE,
|
URL_API_STREAM, URL_API_TEMPLATE)
|
||||||
CONTENT_TYPE_TEXT_PLAIN)
|
from homeassistant.exceptions import TemplateError
|
||||||
|
from homeassistant.helpers.state import TrackStates
|
||||||
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
DOMAIN = 'api'
|
DOMAIN = 'api'
|
||||||
DEPENDENCIES = ['http']
|
DEPENDENCIES = ['http']
|
||||||
@ -37,7 +33,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Register the API with the HTTP interface. """
|
"""Register the API with the HTTP interface."""
|
||||||
|
|
||||||
# /api - for validation purposes
|
# /api - for validation purposes
|
||||||
hass.http.register_path('GET', URL_API, _handle_get_api)
|
hass.http.register_path('GET', URL_API, _handle_get_api)
|
||||||
@ -48,10 +44,6 @@ def setup(hass, config):
|
|||||||
# /api/config
|
# /api/config
|
||||||
hass.http.register_path('GET', URL_API_CONFIG, _handle_get_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
|
# /states
|
||||||
hass.http.register_path('GET', URL_API_STATES, _handle_get_api_states)
|
hass.http.register_path('GET', URL_API_STATES, _handle_get_api_states)
|
||||||
hass.http.register_path(
|
hass.http.register_path(
|
||||||
@ -63,6 +55,9 @@ def setup(hass, config):
|
|||||||
hass.http.register_path(
|
hass.http.register_path(
|
||||||
'PUT', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
|
'PUT', re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
|
||||||
_handle_post_state_entity)
|
_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
|
# /events
|
||||||
hass.http.register_path('GET', URL_API_EVENTS, _handle_get_api_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):
|
def _handle_get_api(handler, path_match, data):
|
||||||
""" Renders the debug interface. """
|
"""Renders the debug interface."""
|
||||||
handler.write_json_message("API running.")
|
handler.write_json_message("API running.")
|
||||||
|
|
||||||
|
|
||||||
def _handle_get_api_stream(handler, path_match, data):
|
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
|
gracefully_closed = False
|
||||||
hass = handler.server.hass
|
hass = handler.server.hass
|
||||||
wfile = handler.wfile
|
wfile = handler.wfile
|
||||||
@ -119,7 +114,7 @@ def _handle_get_api_stream(handler, path_match, data):
|
|||||||
restrict = restrict.split(',')
|
restrict = restrict.split(',')
|
||||||
|
|
||||||
def write_message(payload):
|
def write_message(payload):
|
||||||
""" Writes a message to the output. """
|
"""Writes a message to the output."""
|
||||||
with write_lock:
|
with write_lock:
|
||||||
msg = "data: {}\n\n".format(payload)
|
msg = "data: {}\n\n".format(payload)
|
||||||
|
|
||||||
@ -132,7 +127,7 @@ def _handle_get_api_stream(handler, path_match, data):
|
|||||||
block.set()
|
block.set()
|
||||||
|
|
||||||
def forward_events(event):
|
def forward_events(event):
|
||||||
""" Forwards events to the open request. """
|
"""Forwards events to the open request."""
|
||||||
nonlocal gracefully_closed
|
nonlocal gracefully_closed
|
||||||
|
|
||||||
if block.is_set() or event.event_type == EVENT_TIME_CHANGED:
|
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):
|
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())
|
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):
|
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())
|
handler.write_json(handler.server.hass.states.all())
|
||||||
|
|
||||||
|
|
||||||
def _handle_get_api_states_entity(handler, path_match, data):
|
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')
|
entity_id = path_match.group('entity_id')
|
||||||
|
|
||||||
state = handler.server.hass.states.get(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):
|
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:
|
This handles the following paths:
|
||||||
/api/states/<entity_id>
|
/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))
|
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):
|
def _handle_get_api_events(handler, path_match, data):
|
||||||
""" Handles getting overview of event listeners. """
|
"""Handles getting overview of event listeners."""
|
||||||
handler.write_json(_events_json(handler.server.hass))
|
handler.write_json(events_json(handler.server.hass))
|
||||||
|
|
||||||
|
|
||||||
def _handle_api_post_events_event(handler, path_match, event_data):
|
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:
|
This handles the following paths:
|
||||||
/api/events/<event_type>
|
/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):
|
if event_data is not None and not isinstance(event_data, dict):
|
||||||
handler.write_json_message(
|
handler.write_json_message(
|
||||||
"event_data should be an object", HTTP_UNPROCESSABLE_ENTITY)
|
"event_data should be an object", HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
return
|
||||||
|
|
||||||
event_origin = ha.EventOrigin.remote
|
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):
|
def _handle_get_api_services(handler, path_match, data):
|
||||||
""" Handles getting overview of services. """
|
"""Handles getting overview of services."""
|
||||||
handler.write_json(_services_json(handler.server.hass))
|
handler.write_json(services_json(handler.server.hass))
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def _handle_post_api_services_domain_service(handler, path_match, data):
|
def _handle_post_api_services_domain_service(handler, path_match, data):
|
||||||
""" Handles calling a service.
|
"""Handles calling a service.
|
||||||
|
|
||||||
This handles the following paths:
|
This handles the following paths:
|
||||||
/api/services/<domain>/<service>
|
/api/services/<domain>/<service>
|
||||||
@ -298,8 +298,7 @@ def _handle_post_api_services_domain_service(handler, path_match, data):
|
|||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def _handle_post_api_event_forward(handler, path_match, data):
|
def _handle_post_api_event_forward(handler, path_match, data):
|
||||||
""" Handles adding an event forwarding target. """
|
"""Handles adding an event forwarding target."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
host = data['host']
|
host = data['host']
|
||||||
api_password = data['api_password']
|
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):
|
def _handle_delete_api_event_forward(handler, path_match, data):
|
||||||
""" Handles deleting an event forwarding target. """
|
"""Handles deleting an event forwarding target."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
host = data['host']
|
host = data['host']
|
||||||
except KeyError:
|
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):
|
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)
|
handler.write_json(handler.server.hass.config.components)
|
||||||
|
|
||||||
|
|
||||||
def _handle_get_api_error_log(handler, path_match, data):
|
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),
|
handler.write_file(handler.server.hass.config.path(ERROR_LOG_FILENAME),
|
||||||
False)
|
False)
|
||||||
|
|
||||||
|
|
||||||
def _handle_post_api_log_out(handler, path_match, data):
|
def _handle_post_api_log_out(handler, path_match, data):
|
||||||
""" Log user out. """
|
"""Log user out."""
|
||||||
handler.send_response(HTTP_OK)
|
handler.send_response(HTTP_OK)
|
||||||
handler.destroy_session()
|
handler.destroy_session()
|
||||||
handler.end_headers()
|
handler.end_headers()
|
||||||
|
|
||||||
|
|
||||||
def _handle_post_api_template(handler, path_match, data):
|
def _handle_post_api_template(handler, path_match, data):
|
||||||
""" Log user out. """
|
"""Log user out."""
|
||||||
template_string = data.get('template', '')
|
template_string = data.get('template', '')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -390,13 +387,13 @@ def _handle_post_api_template(handler, path_match, data):
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def _services_json(hass):
|
def services_json(hass):
|
||||||
""" Generate services data to JSONify. """
|
"""Generate services data to JSONify."""
|
||||||
return [{"domain": key, "services": value}
|
return [{"domain": key, "services": value}
|
||||||
for key, value in hass.services.services.items()]
|
for key, value in hass.services.services.items()]
|
||||||
|
|
||||||
|
|
||||||
def _events_json(hass):
|
def events_json(hass):
|
||||||
""" Generate event data to JSONify. """
|
"""Generate event data to JSONify."""
|
||||||
return [{"event": key, "listener_count": value}
|
return [{"event": key, "listener_count": value}
|
||||||
for key, value in hass.bus.listeners.items()]
|
for key, value in hass.bus.listeners.items()]
|
||||||
|
@ -9,9 +9,9 @@ https://home-assistant.io/components/arduino/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
from homeassistant.const import (EVENT_HOMEASSISTANT_START,
|
|
||||||
EVENT_HOMEASSISTANT_STOP)
|
|
||||||
|
|
||||||
DOMAIN = "arduino"
|
DOMAIN = "arduino"
|
||||||
REQUIREMENTS = ['PyMata==2.07a']
|
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
|
For more details about this automation rule, please refer to the documentation
|
||||||
at https://home-assistant.io/components/automation/#numeric-state-trigger
|
at https://home-assistant.io/components/automation/#numeric-state-trigger
|
||||||
"""
|
"""
|
||||||
from functools import partial
|
|
||||||
import logging
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||||
from homeassistant.helpers.event import track_state_change
|
from homeassistant.helpers.event import track_state_change
|
||||||
from homeassistant.util import template
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
|
|
||||||
CONF_ENTITY_ID = "entity_id"
|
CONF_ENTITY_ID = "entity_id"
|
||||||
CONF_BELOW = "below"
|
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
|
at https://home-assistant.io/components/automation/#state-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.helpers.event import track_state_change
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.const import MATCH_ALL
|
|
||||||
|
|
||||||
|
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_ENTITY_ID = "entity_id"
|
||||||
CONF_FROM = "from"
|
CONF_FROM = "from"
|
||||||
CONF_TO = "to"
|
CONF_TO = "to"
|
||||||
CONF_STATE = "state"
|
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):
|
def trigger(hass, config, action):
|
||||||
@ -25,19 +57,47 @@ def trigger(hass, config, action):
|
|||||||
if entity_id is None:
|
if entity_id is None:
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
"Missing trigger configuration key %s", CONF_ENTITY_ID)
|
"Missing trigger configuration key %s", CONF_ENTITY_ID)
|
||||||
return False
|
return None
|
||||||
|
|
||||||
from_state = config.get(CONF_FROM, MATCH_ALL)
|
from_state = config.get(CONF_FROM, MATCH_ALL)
|
||||||
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or 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):
|
if isinstance(from_state, bool) or isinstance(to_state, bool):
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
'Config error. Surround to/from values with quotes.')
|
'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):
|
def state_automation_listener(entity, from_s, to_s):
|
||||||
""" Listens for state changes and calls action. """
|
""" 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(
|
track_state_change(
|
||||||
hass, entity_id, state_automation_listener, from_state, to_state)
|
hass, entity_id, state_automation_listener, from_state, to_state)
|
||||||
@ -56,10 +116,18 @@ def if_action(hass, config):
|
|||||||
CONF_STATE)
|
CONF_STATE)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
time_delta = get_time_config(config)
|
||||||
|
if CONF_FOR in config and time_delta is None:
|
||||||
|
return None
|
||||||
|
|
||||||
state = str(state)
|
state = str(state)
|
||||||
|
|
||||||
def if_state():
|
def if_state():
|
||||||
""" Test if condition. """
|
""" 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
|
return if_state
|
||||||
|
@ -9,9 +9,9 @@ at https://home-assistant.io/components/automation/#sun-trigger
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.components import sun
|
from homeassistant.components import sun
|
||||||
from homeassistant.helpers.event import track_sunrise, track_sunset
|
from homeassistant.helpers.event import track_sunrise, track_sunset
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
DEPENDENCIES = ['sun']
|
DEPENDENCIES = ['sun']
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import logging
|
|||||||
|
|
||||||
from homeassistant.const import CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED
|
from homeassistant.const import CONF_VALUE_TEMPLATE, EVENT_STATE_CHANGED
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import TemplateError
|
||||||
from homeassistant.util import template
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -9,10 +9,9 @@ at https://home-assistant.io/components/automation/#zone-trigger
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components import zone
|
from homeassistant.components import zone
|
||||||
from homeassistant.helpers.event import track_state_change
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL)
|
ATTR_GPS_ACCURACY, ATTR_LATITUDE, ATTR_LONGITUDE, MATCH_ALL)
|
||||||
|
from homeassistant.helpers.event import track_state_change
|
||||||
|
|
||||||
CONF_ENTITY_ID = "entity_id"
|
CONF_ENTITY_ID = "entity_id"
|
||||||
CONF_ZONE = "zone"
|
CONF_ZONE = "zone"
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Component to interface with binary sensors (sensors which only know two states)
|
Component to interface with binary sensors (sensors which only know two states)
|
||||||
that can be monitored.
|
that can be monitored.
|
||||||
|
|
||||||
@ -12,17 +10,43 @@ import logging
|
|||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.const import (STATE_ON, STATE_OFF)
|
from homeassistant.const import (STATE_ON, STATE_OFF)
|
||||||
|
from homeassistant.components import (bloomsky, mysensors, zwave, wink)
|
||||||
|
|
||||||
DOMAIN = 'binary_sensor'
|
DOMAIN = 'binary_sensor'
|
||||||
SCAN_INTERVAL = 30
|
SCAN_INTERVAL = 30
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
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):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for binary sensors. """
|
"""Track states and offer events for binary sensors."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
||||||
|
DISCOVERY_PLATFORMS)
|
||||||
|
|
||||||
component.setup(config)
|
component.setup(config)
|
||||||
|
|
||||||
@ -31,19 +55,29 @@ def setup(hass, config):
|
|||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
class BinarySensorDevice(Entity):
|
class BinarySensorDevice(Entity):
|
||||||
""" Represents a binary sensor. """
|
"""Represent a binary sensor."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if the binary sensor is on. """
|
"""Return True if the binary sensor is on."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
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
|
return STATE_ON if self.is_on else STATE_OFF
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def friendly_state(self):
|
def sensor_class(self):
|
||||||
""" Returns the friendly state of the binary sensor. """
|
"""Return the class of this sensor, from SENSOR_CLASSES."""
|
||||||
return None
|
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.
|
Provides a binary sensor to track online status of a UPS.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/binary_sensor.apcupsd/
|
https://home-assistant.io/components/binary_sensor.apcupsd/
|
||||||
"""
|
"""
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
|
||||||
from homeassistant.components import apcupsd
|
from homeassistant.components import apcupsd
|
||||||
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
|
||||||
DEPENDENCIES = [apcupsd.DOMAIN]
|
DEPENDENCIES = [apcupsd.DOMAIN]
|
||||||
DEFAULT_NAME = "UPS Online Status"
|
DEFAULT_NAME = "UPS Online Status"
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
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),))
|
add_entities((OnlineStatus(config, apcupsd.DATA),))
|
||||||
|
|
||||||
|
|
||||||
class OnlineStatus(BinarySensorDevice):
|
class OnlineStatus(BinarySensorDevice):
|
||||||
""" Binary sensor to represent UPS online status. """
|
"""Binary sensor to represent UPS online status."""
|
||||||
def __init__(self, config, data):
|
def __init__(self, config, data):
|
||||||
self._config = config
|
self._config = config
|
||||||
self._data = data
|
self._data = data
|
||||||
@ -33,7 +31,7 @@ class OnlineStatus(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
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
|
return self._state == apcupsd.VALUE_ONLINE
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor.arest
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
The arest sensor will consume an exposed aREST API of a device.
|
The arest sensor will consume an exposed aREST API of a device.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/binary_sensor.arest/
|
https://home-assistant.io/components/binary_sensor.arest/
|
||||||
"""
|
"""
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.util import Throttle
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -24,8 +22,7 @@ CONF_PIN = 'pin'
|
|||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
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)
|
resource = config.get(CONF_RESOURCE)
|
||||||
pin = config.get(CONF_PIN)
|
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
|
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||||
class ArestBinarySensor(BinarySensorDevice):
|
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):
|
def __init__(self, arest, resource, name, pin):
|
||||||
self.arest = arest
|
self.arest = arest
|
||||||
@ -73,23 +70,22 @@ class ArestBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the binary sensor. """
|
"""The name of the binary sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
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'))
|
return bool(self.arest.data.get('state'))
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data from aREST API. """
|
"""Gets the latest data from aREST API."""
|
||||||
self.arest.update()
|
self.arest.update()
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
class ArestData(object):
|
class ArestData(object):
|
||||||
""" Class for handling the data retrieval for pins. """
|
"""Class for handling the data retrieval for pins."""
|
||||||
|
|
||||||
def __init__(self, resource, pin):
|
def __init__(self, resource, pin):
|
||||||
self._resource = resource
|
self._resource = resource
|
||||||
self._pin = pin
|
self._pin = pin
|
||||||
@ -97,7 +93,7 @@ class ArestData(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data from aREST device. """
|
"""Gets the latest data from aREST device."""
|
||||||
try:
|
try:
|
||||||
response = requests.get('{}/digital/{}'.format(
|
response = requests.get('{}/digital/{}'.format(
|
||||||
self._resource, self._pin), timeout=10)
|
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
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/binary_sensor.command/
|
https://home-assistant.io/components/binary_sensor.command/
|
||||||
@ -10,10 +8,10 @@ https://home-assistant.io/components/binary_sensor.command/
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
from homeassistant.components.sensor.command_sensor import CommandSensorData
|
from homeassistant.components.sensor.command_line import CommandSensorData
|
||||||
from homeassistant.util import template
|
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||||
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -27,8 +25,7 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Add the Command Sensor. """
|
"""Add the Command Sensor."""
|
||||||
|
|
||||||
if config.get('command') is None:
|
if config.get('command') is None:
|
||||||
_LOGGER.error('Missing required variable: "command"')
|
_LOGGER.error('Missing required variable: "command"')
|
||||||
return False
|
return False
|
||||||
@ -47,8 +44,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
class CommandBinarySensor(BinarySensorDevice):
|
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,
|
def __init__(self, hass, data, name, payload_on,
|
||||||
payload_off, value_template):
|
payload_off, value_template):
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
@ -62,16 +60,16 @@ class CommandBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the sensor. """
|
"""The name of the sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if the binary sensor is on. """
|
"""True if the binary sensor is on."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data and updates the state. """
|
"""Gets the latest data and updates the state."""
|
||||||
self.data.update()
|
self.data.update()
|
||||||
value = self.data.value
|
value = self.data.value
|
||||||
|
|
@ -1,37 +1,43 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor.demo
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Demo platform that has two fake binary sensors.
|
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
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the Demo binary sensors. """
|
"""Setup the Demo binary sensor platform."""
|
||||||
add_devices([
|
add_devices([
|
||||||
DemoBinarySensor('Basement Floor Wet', False),
|
DemoBinarySensor('Basement Floor Wet', False, 'moisture'),
|
||||||
DemoBinarySensor('Movement Backyard', True),
|
DemoBinarySensor('Movement Backyard', True, 'motion'),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class DemoBinarySensor(BinarySensorDevice):
|
class DemoBinarySensor(BinarySensorDevice):
|
||||||
""" A Demo binary sensor. """
|
"""A Demo binary sensor."""
|
||||||
|
def __init__(self, name, state, sensor_class):
|
||||||
def __init__(self, name, state):
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = state
|
self._state = state
|
||||||
|
self._sensor_type = sensor_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensor_class(self):
|
||||||
|
"""Return the class of this sensor."""
|
||||||
|
return self._sensor_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
""" No polling needed for a demo binary sensor. """
|
"""No polling needed for a demo binary sensor."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the binary sensor. """
|
"""Return the name of the binary sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if the binary sensor is on. """
|
"""Return true if the binary sensor is on."""
|
||||||
return self._state
|
return self._state
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor.mqtt
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Allows to configure a MQTT binary sensor.
|
Allows to configure a MQTT binary sensor.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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
|
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
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -25,7 +23,7 @@ DEPENDENCIES = ['mqtt']
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
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:
|
if config.get('state_topic') is None:
|
||||||
_LOGGER.error('Missing required variable: state_topic')
|
_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
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
class MqttBinarySensor(BinarySensorDevice):
|
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,
|
def __init__(self, hass, name, state_topic, qos, payload_on, payload_off,
|
||||||
value_template):
|
value_template):
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
@ -55,7 +53,7 @@ class MqttBinarySensor(BinarySensorDevice):
|
|||||||
self._qos = qos
|
self._qos = qos
|
||||||
|
|
||||||
def message_received(topic, payload, 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:
|
if value_template is not None:
|
||||||
payload = template.render_with_possible_json_value(
|
payload = template.render_with_possible_json_value(
|
||||||
hass, value_template, payload)
|
hass, value_template, payload)
|
||||||
@ -70,15 +68,15 @@ class MqttBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
""" No polling needed. """
|
"""No polling needed."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the binary sensor. """
|
"""The name of the binary sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if the binary sensor is on. """
|
"""True if the binary sensor is on."""
|
||||||
return self._state
|
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.
|
Support for Nest Thermostat Binary Sensors.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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 logging
|
||||||
import socket
|
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.binary_sensor import BinarySensorDevice
|
||||||
|
from homeassistant.components.sensor.nest import NestSensor
|
||||||
|
|
||||||
DEPENDENCIES = ['nest']
|
DEPENDENCIES = ['nest']
|
||||||
BINARY_TYPES = ['fan',
|
BINARY_TYPES = ['fan',
|
||||||
@ -27,7 +25,7 @@ BINARY_TYPES = ['fan',
|
|||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Setup Nest binary sensors. """
|
"""Setup Nest binary sensors."""
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
try:
|
try:
|
||||||
@ -48,9 +46,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class NestBinarySensor(NestSensor, BinarySensorDevice):
|
class NestBinarySensor(NestSensor, BinarySensorDevice):
|
||||||
""" Represents a Nest binary sensor. """
|
"""Represents a Nest binary sensor."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
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))
|
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.
|
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
|
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
|
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.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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -21,7 +19,7 @@ DEFAULT_METHOD = 'GET'
|
|||||||
|
|
||||||
# pylint: disable=unused-variable
|
# pylint: disable=unused-variable
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Setup REST binary sensors. """
|
"""Setup REST binary sensors."""
|
||||||
resource = config.get('resource', None)
|
resource = config.get('resource', None)
|
||||||
method = config.get('method', DEFAULT_METHOD)
|
method = config.get('method', DEFAULT_METHOD)
|
||||||
payload = config.get('payload', None)
|
payload = config.get('payload', None)
|
||||||
@ -41,10 +39,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
class RestBinarySensor(BinarySensorDevice):
|
class RestBinarySensor(BinarySensorDevice):
|
||||||
""" A REST binary sensor. """
|
"""A REST binary sensor."""
|
||||||
|
|
||||||
def __init__(self, hass, rest, name, value_template):
|
def __init__(self, hass, rest, name, value_template):
|
||||||
""" Initialize a REST binary sensor. """
|
"""Initialize a REST binary sensor."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self.rest = rest
|
self.rest = rest
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -54,12 +52,12 @@ class RestBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Name of the binary sensor. """
|
"""Name of the binary sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
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:
|
if self.rest.data is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -69,5 +67,5 @@ class RestBinarySensor(BinarySensorDevice):
|
|||||||
return bool(int(self.rest.data))
|
return bool(int(self.rest.data))
|
||||||
|
|
||||||
def update(self):
|
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()
|
self.rest.update()
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor.rpi_gpio
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Allows to configure a binary sensor using RPi GPIO.
|
Allows to configure a binary sensor using RPi GPIO.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/binary_sensor.rpi_gpio/
|
https://home-assistant.io/components/binary_sensor.rpi_gpio/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.components.rpi_gpio as rpi_gpio
|
import homeassistant.components.rpi_gpio as rpi_gpio
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
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_PULL_MODE = "UP"
|
||||||
DEFAULT_BOUNCETIME = 50
|
DEFAULT_BOUNCETIME = 50
|
||||||
@ -22,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
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)
|
pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE)
|
||||||
bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME)
|
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
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
class RPiGPIOBinarySensor(BinarySensorDevice):
|
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):
|
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
|
|
||||||
@ -52,22 +50,22 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
|
|||||||
self._state = rpi_gpio.read_input(self._port)
|
self._state = rpi_gpio.read_input(self._port)
|
||||||
|
|
||||||
def read_gpio(port):
|
def read_gpio(port):
|
||||||
""" Reads state from GPIO. """
|
"""Reads state from GPIO."""
|
||||||
self._state = rpi_gpio.read_input(self._port)
|
self._state = rpi_gpio.read_input(self._port)
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)
|
rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
""" No polling needed. """
|
"""No polling needed."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the sensor. """
|
"""The name of the sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" Returns the state of the entity. """
|
"""Returns the state of the entity."""
|
||||||
return self._state != self._invert_logic
|
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.
|
Contains functionality to use a ZigBee device as a binary sensor.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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 (
|
from homeassistant.components.zigbee import (
|
||||||
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
|
ZigBeeDigitalIn, ZigBeeDigitalInConfig)
|
||||||
|
|
||||||
|
|
||||||
DEPENDENCIES = ["zigbee"]
|
DEPENDENCIES = ["zigbee"]
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
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([
|
add_entities([
|
||||||
ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))
|
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
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.const import CONF_API_KEY
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
DOMAIN = "bloomsky"
|
DOMAIN = "bloomsky"
|
||||||
BLOOMSKY = None
|
BLOOMSKY = None
|
||||||
@ -22,6 +25,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
# no point in polling the API more frequently
|
# no point in polling the API more frequently
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=300)
|
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
|
# pylint: disable=unused-argument,too-few-public-methods
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
@ -40,6 +47,12 @@ def setup(hass, config):
|
|||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return False
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.browser
|
Provides functionality to launch a web browser on the host machine.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides functionality to launch a webbrowser on the host machine.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/browser/
|
https://home-assistant.io/components/browser/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DOMAIN = "browser"
|
DOMAIN = "browser"
|
||||||
|
|
||||||
SERVICE_BROWSE_URL = "browse_url"
|
SERVICE_BROWSE_URL = "browse_url"
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
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
|
import webbrowser
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
|
hass.services.register(DOMAIN, SERVICE_BROWSE_URL,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
"""
|
"""
|
||||||
homeassistant.components.camera
|
Component to interface with cameras.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Component to interface with various cameras.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/camera/
|
https://home-assistant.io/components/camera/
|
||||||
@ -15,8 +13,8 @@ import requests
|
|||||||
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.components import bloomsky
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_PICTURE,
|
|
||||||
HTTP_NOT_FOUND,
|
HTTP_NOT_FOUND,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
)
|
)
|
||||||
@ -24,33 +22,19 @@ from homeassistant.const import (
|
|||||||
|
|
||||||
DOMAIN = 'camera'
|
DOMAIN = 'camera'
|
||||||
DEPENDENCIES = ['http']
|
DEPENDENCIES = ['http']
|
||||||
GROUP_NAME_ALL_CAMERAS = 'all_cameras'
|
|
||||||
SCAN_INTERVAL = 30
|
SCAN_INTERVAL = 30
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
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
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {}
|
DISCOVERY_PLATFORMS = {
|
||||||
|
bloomsky.DISCOVER_CAMERAS: 'bloomsky',
|
||||||
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-'
|
|
||||||
|
|
||||||
STATE_RECORDING = 'recording'
|
STATE_RECORDING = 'recording'
|
||||||
STATE_STREAMING = 'streaming'
|
STATE_STREAMING = 'streaming'
|
||||||
STATE_IDLE = 'idle'
|
STATE_IDLE = 'idle'
|
||||||
|
|
||||||
CAMERA_PROXY_URL = '/api/camera_proxy_stream/{0}'
|
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}'
|
||||||
CAMERA_STILL_URL = '/api/camera_proxy/{0}'
|
|
||||||
ENTITY_IMAGE_URL = '/api/camera_proxy/{0}?time={1}'
|
|
||||||
|
|
||||||
MULTIPART_BOUNDARY = '--jpegboundary'
|
MULTIPART_BOUNDARY = '--jpegboundary'
|
||||||
MJPEG_START_HEADER = 'Content-type: {0}\r\n\r\n'
|
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
|
# pylint: disable=too-many-branches
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for cameras. """
|
"""Initialize camera component."""
|
||||||
|
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
||||||
DISCOVERY_PLATFORMS)
|
DISCOVERY_PLATFORMS)
|
||||||
@ -78,7 +61,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def _proxy_camera_image(handler, path_match, data):
|
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)
|
entity_id = path_match.group(ATTR_ENTITY_ID)
|
||||||
camera = component.entities.get(entity_id)
|
camera = component.entities.get(entity_id)
|
||||||
|
|
||||||
@ -104,7 +87,8 @@ def setup(hass, config):
|
|||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def _proxy_camera_mjpeg_stream(handler, path_match, data):
|
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
|
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
|
into an MJPEG stream. This means that HA can return a live video
|
||||||
stream even with only a still image URL available.
|
stream even with only a still image URL available.
|
||||||
@ -136,35 +120,46 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class Camera(Entity):
|
class Camera(Entity):
|
||||||
""" The base class for camera components. """
|
"""The base class for camera entities."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize a camera."""
|
||||||
self.is_streaming = False
|
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
|
@property
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def is_recording(self):
|
def is_recording(self):
|
||||||
""" Returns true if the device is recording. """
|
"""Return true if the device is recording."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def brand(self):
|
def brand(self):
|
||||||
""" Should return a string of the camera brand. """
|
"""Camera brand."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def model(self):
|
def model(self):
|
||||||
""" Returns string of camera model. """
|
"""Camera model."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
""" Return bytes of camera image. """
|
"""Return bytes of camera image."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def mjpeg_stream(self, handler):
|
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('HTTP/1.1 200 OK\r\n', 'utf-8'))
|
||||||
handler.request.sendall(bytes(
|
handler.request.sendall(bytes(
|
||||||
'Content-type: multipart/x-mixed-replace; \
|
'Content-type: multipart/x-mixed-replace; \
|
||||||
@ -193,7 +188,7 @@ class Camera(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the entity. """
|
"""Camera state."""
|
||||||
if self.is_recording:
|
if self.is_recording:
|
||||||
return STATE_RECORDING
|
return STATE_RECORDING
|
||||||
elif self.is_streaming:
|
elif self.is_streaming:
|
||||||
@ -203,11 +198,8 @@ class Camera(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Returns optional state attributes. """
|
"""Camera state attributes."""
|
||||||
attr = {
|
attr = {}
|
||||||
ATTR_ENTITY_PICTURE: ENTITY_IMAGE_URL.format(
|
|
||||||
self.entity_id, time.time()),
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.model:
|
if self.model:
|
||||||
attr['model_name'] = 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/
|
https://home-assistant.io/components/camera.bloomsky/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import homeassistant.components.bloomsky as bloomsky
|
|
||||||
from homeassistant.components.camera import Camera
|
from homeassistant.components.camera import Camera
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
DEPENDENCIES = ["bloomsky"]
|
DEPENDENCIES = ["bloomsky"]
|
||||||
|
|
||||||
@ -17,6 +19,7 @@ DEPENDENCIES = ["bloomsky"]
|
|||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" set up access to BloomSky cameras """
|
""" set up access to BloomSky cameras """
|
||||||
|
bloomsky = get_component('bloomsky')
|
||||||
for device in bloomsky.BLOOMSKY.devices.values():
|
for device in bloomsky.BLOOMSKY.devices.values():
|
||||||
add_devices_callback([BloomSkyCamera(bloomsky.BLOOMSKY, device)])
|
add_devices_callback([BloomSkyCamera(bloomsky.BLOOMSKY, device)])
|
||||||
|
|
||||||
|
@ -1,29 +1,31 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.demo
|
Demo camera platform that has a fake camera.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Demo 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
|
import os
|
||||||
from homeassistant.components.camera import Camera
|
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
from homeassistant.components.camera import Camera
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the Demo camera. """
|
"""Setup the Demo camera platform."""
|
||||||
add_devices([
|
add_devices([
|
||||||
DemoCamera('Demo camera')
|
DemoCamera('Demo camera')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class DemoCamera(Camera):
|
class DemoCamera(Camera):
|
||||||
""" A Demo camera. """
|
"""A Demo camera."""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
""" Return a faked still image response. """
|
"""Return a faked still image response."""
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
|
|
||||||
image_path = os.path.join(os.path.dirname(__file__),
|
image_path = os.path.join(os.path.dirname(__file__),
|
||||||
@ -33,5 +35,5 @@ class DemoCamera(Camera):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Return the name of this device. """
|
"""Return the name of this camera."""
|
||||||
return self._name
|
return self._name
|
||||||
|
@ -10,8 +10,8 @@ import logging
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
|
||||||
from homeassistant.components.camera import DOMAIN, Camera
|
from homeassistant.components.camera import DOMAIN, Camera
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
|
||||||
from homeassistant.components.camera import DOMAIN, Camera
|
from homeassistant.components.camera import DOMAIN, Camera
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -6,15 +6,15 @@ Support for IP Cameras.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/camera.mjpeg/
|
https://home-assistant.io/components/camera.mjpeg/
|
||||||
"""
|
"""
|
||||||
from contextlib import closing
|
|
||||||
import logging
|
import logging
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
|
||||||
from homeassistant.components.camera import DOMAIN, Camera
|
from homeassistant.components.camera import DOMAIN, Camera
|
||||||
from homeassistant.const import HTTP_OK
|
from homeassistant.const import HTTP_OK
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
|
||||||
CONTENT_TYPE_HEADER = 'Content-Type'
|
CONTENT_TYPE_HEADER = 'Content-Type'
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ import socket
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
|
||||||
from homeassistant.components.camera import DOMAIN, Camera
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -26,8 +26,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
addr = config.get('nvr')
|
addr = config.get('nvr')
|
||||||
port = int(config.get('port', 7080))
|
|
||||||
key = config.get('key')
|
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
|
from uvcclient import nvr
|
||||||
nvrconn = nvr.UVCRemote(addr, port, key)
|
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))
|
_LOGGER.error('Unable to connect to NVR: %s', str(ex))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for camera in cameras:
|
# Filter out airCam models, which are not supported in the latest
|
||||||
add_devices([UnifiVideoCamera(nvrconn,
|
# version of UnifiVideo and which are EOL by Ubiquiti
|
||||||
camera['uuid'],
|
cameras = [camera for camera in cameras
|
||||||
camera['name'])])
|
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):
|
class UnifiVideoCamera(Camera):
|
||||||
@ -58,6 +68,8 @@ class UnifiVideoCamera(Camera):
|
|||||||
self._uuid = uuid
|
self._uuid = uuid
|
||||||
self._name = name
|
self._name = name
|
||||||
self.is_streaming = False
|
self.is_streaming = False
|
||||||
|
self._connect_addr = None
|
||||||
|
self._camera = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -68,24 +80,74 @@ class UnifiVideoCamera(Camera):
|
|||||||
caminfo = self._nvr.get_camera(self._uuid)
|
caminfo = self._nvr.get_camera(self._uuid)
|
||||||
return caminfo['recordingSettings']['fullTimeRecordEnabled']
|
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 camera as uvc_camera
|
||||||
|
from uvcclient import store as uvc_store
|
||||||
|
|
||||||
caminfo = self._nvr.get_camera(self._uuid)
|
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
|
camera = None
|
||||||
for addr in [caminfo['host'], caminfo['internalHost']]:
|
for addr in addrs:
|
||||||
try:
|
try:
|
||||||
camera = uvc_camera.UVCCameraClient(addr,
|
camera = uvc_camera.UVCCameraClient(addr,
|
||||||
caminfo['username'],
|
caminfo['username'],
|
||||||
'ubnt')
|
password)
|
||||||
|
camera.login()
|
||||||
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
|
_LOGGER.debug('Logged into UVC camera %(name)s via %(addr)s',
|
||||||
dict(name=self._name, addr=addr))
|
dict(name=self._name, addr=addr))
|
||||||
|
self._connect_addr = addr
|
||||||
|
break
|
||||||
except socket.error:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
|
except uvc_camera.CameraConnectError:
|
||||||
if not camera:
|
pass
|
||||||
|
except uvc_camera.CameraAuthError:
|
||||||
|
pass
|
||||||
|
if not self._connect_addr:
|
||||||
_LOGGER.error('Unable to login to camera')
|
_LOGGER.error('Unable to login to camera')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
camera.login()
|
self._camera = camera
|
||||||
return camera.get_snapshot()
|
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
|
import logging
|
||||||
|
|
||||||
from homeassistant.helpers.entity import generate_entity_id
|
|
||||||
from homeassistant.const import EVENT_TIME_CHANGED
|
from homeassistant.const import EVENT_TIME_CHANGED
|
||||||
|
from homeassistant.helpers.entity import generate_entity_id
|
||||||
|
|
||||||
DOMAIN = "configurator"
|
DOMAIN = "configurator"
|
||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
@ -9,10 +9,9 @@ https://home-assistant.io/components/conversation/
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
from homeassistant import core
|
from homeassistant import core
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
|
||||||
|
|
||||||
DOMAIN = "conversation"
|
DOMAIN = "conversation"
|
||||||
|
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.demo
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Sets up a demo environment that mimics interaction with devices.
|
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 time
|
||||||
|
|
||||||
import homeassistant.core as ha
|
|
||||||
import homeassistant.bootstrap as bootstrap
|
import homeassistant.bootstrap as bootstrap
|
||||||
|
import homeassistant.core as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.const import (
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
|
||||||
CONF_PLATFORM, ATTR_ENTITY_ID)
|
|
||||||
|
|
||||||
DOMAIN = "demo"
|
DOMAIN = "demo"
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup a demo environment. """
|
"""Setup a demo environment."""
|
||||||
group = loader.get_component('group')
|
group = loader.get_component('group')
|
||||||
configurator = loader.get_component('configurator')
|
configurator = loader.get_component('configurator')
|
||||||
|
|
||||||
@ -64,14 +63,29 @@ def setup(hass, config):
|
|||||||
switches = sorted(hass.states.entity_ids('switch'))
|
switches = sorted(hass.states.entity_ids('switch'))
|
||||||
media_players = sorted(hass.states.entity_ids('media_player'))
|
media_players = sorted(hass.states.entity_ids('media_player'))
|
||||||
group.Group(hass, 'living room', [
|
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'])
|
'scene.romantic_lights'])
|
||||||
group.Group(hass, 'bedroom', [lights[0], switches[1],
|
group.Group(hass, 'bedroom', [lights[0], switches[1], media_players[0]])
|
||||||
media_players[0]])
|
group.Group(hass, 'kitchen', [
|
||||||
group.Group(hass, 'Rooms', [
|
lights[2], 'rollershutter.kitchen_window', 'lock.kitchen_door'])
|
||||||
'group.living_room', 'group.bedroom',
|
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',
|
'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)
|
], view=True)
|
||||||
|
|
||||||
# Setup scripts
|
# 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
|
# Setup configurator
|
||||||
configurator_ids = []
|
configurator_ids = []
|
||||||
|
|
||||||
def hue_configuration_callback(data):
|
def hue_configuration_callback(data):
|
||||||
""" Fake callback, mark config as done. """
|
"""Fake callback, mark config as done."""
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
# First time it is called, pretend it failed.
|
# First time it is called, pretend it failed.
|
||||||
|
@ -10,10 +10,11 @@ https://home-assistant.io/components/device_sun_light_trigger/
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.helpers.event import track_point_in_time, track_state_change
|
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
|
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"
|
DOMAIN = "device_sun_light_trigger"
|
||||||
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']
|
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.helpers.event import track_utc_time_change
|
||||||
from homeassistant.const import (
|
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)
|
DEVICE_DEFAULT_NAME, STATE_HOME, STATE_NOT_HOME)
|
||||||
|
|
||||||
DOMAIN = "device_tracker"
|
DOMAIN = "device_tracker"
|
||||||
@ -297,14 +297,16 @@ class Device(Entity):
|
|||||||
""" State of the device. """
|
""" State of the device. """
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entity_picture(self):
|
||||||
|
"""Picture of the device."""
|
||||||
|
return self.config_picture
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Device state attributes. """
|
""" Device state attributes. """
|
||||||
attr = {}
|
attr = {}
|
||||||
|
|
||||||
if self.config_picture:
|
|
||||||
attr[ATTR_ENTITY_PICTURE] = self.config_picture
|
|
||||||
|
|
||||||
if self.gps:
|
if self.gps:
|
||||||
attr[ATTR_LATITUDE] = self.gps[0]
|
attr[ATTR_LATITUDE] = self.gps[0]
|
||||||
attr[ATTR_LONGITUDE] = self.gps[1]
|
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/
|
https://home-assistant.io/components/device_tracker.actiontec/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
from collections import namedtuple
|
|
||||||
import re
|
import re
|
||||||
import threading
|
|
||||||
import telnetlib
|
import telnetlib
|
||||||
|
import threading
|
||||||
|
from collections import namedtuple
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
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/
|
https://home-assistant.io/components/device_tracker.aruba/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import re
|
import re
|
||||||
import threading
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
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/
|
https://home-assistant.io/components/device_tracker.asuswrt/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import re
|
import re
|
||||||
import threading
|
|
||||||
import telnetlib
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
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/
|
https://home-assistant.io/components/device_tracker.ddwrt/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
@ -10,10 +10,10 @@ https://home-assistant.io/components/device_tracker.fritz/
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
REQUIREMENTS = ['fritzconnection==0.4.6']
|
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/
|
https://home-assistant.io/components/device_tracker.icloud/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import re
|
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
|
from homeassistant.helpers.event import track_utc_time_change
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -9,9 +9,8 @@ https://home-assistant.io/components/device_tracker.locative/
|
|||||||
import logging
|
import logging
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from homeassistant.const import (
|
|
||||||
HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME)
|
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, STATE_NOT_HOME
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -7,17 +7,18 @@ presence.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.luci/
|
https://home-assistant.io/components/device_tracker.luci/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
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/
|
https://home-assistant.io/components/device_tracker.mqtt/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant import util
|
|
||||||
import homeassistant.components.mqtt as mqtt
|
import homeassistant.components.mqtt as mqtt
|
||||||
|
from homeassistant import util
|
||||||
|
|
||||||
DEPENDENCIES = ['mqtt']
|
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/
|
https://home-assistant.io/components/device_tracker.netgear/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import threading
|
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.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
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
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/
|
https://home-assistant.io/components/device_tracker.nmap_scanner/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
from collections import namedtuple
|
|
||||||
import subprocess
|
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
from collections import namedtuple
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
from homeassistant.const import CONF_HOSTS
|
from homeassistant.const import CONF_HOSTS
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
from homeassistant.util import Throttle, convert
|
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
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
@ -62,7 +62,7 @@ def setup_scanner(hass, config, see):
|
|||||||
see_beacons(dev_id, kwargs)
|
see_beacons(dev_id, kwargs)
|
||||||
|
|
||||||
def owntracks_event_update(topic, payload, qos):
|
def owntracks_event_update(topic, payload, qos):
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches, too-many-statements
|
||||||
""" MQTT event (geofences) received. """
|
""" MQTT event (geofences) received. """
|
||||||
|
|
||||||
# Docs on available data:
|
# Docs on available data:
|
||||||
@ -92,7 +92,10 @@ def setup_scanner(hass, config, see):
|
|||||||
if zone is None:
|
if zone is None:
|
||||||
if data['t'] == 'b':
|
if data['t'] == 'b':
|
||||||
# Not a HA zone, and a beacon so assume mobile
|
# 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:
|
else:
|
||||||
# Normal region
|
# Normal region
|
||||||
if not zone.attributes.get('passive'):
|
if not zone.attributes.get('passive'):
|
||||||
@ -108,28 +111,30 @@ def setup_scanner(hass, config, see):
|
|||||||
see_beacons(dev_id, kwargs)
|
see_beacons(dev_id, kwargs)
|
||||||
|
|
||||||
elif data['event'] == 'leave':
|
elif data['event'] == 'leave':
|
||||||
regions = REGIONS_ENTERED[dev_id]
|
with LOCK:
|
||||||
if location in regions:
|
regions = REGIONS_ENTERED[dev_id]
|
||||||
regions.remove(location)
|
if location in regions:
|
||||||
new_region = regions[-1] if regions else None
|
regions.remove(location)
|
||||||
|
new_region = regions[-1] if regions else None
|
||||||
|
|
||||||
if new_region:
|
if new_region:
|
||||||
# Exit to previous region
|
# Exit to previous region
|
||||||
zone = hass.states.get("zone.{}".format(new_region))
|
zone = hass.states.get("zone.{}".format(new_region))
|
||||||
if not zone.attributes.get('passive'):
|
if not zone.attributes.get('passive'):
|
||||||
kwargs['location_name'] = new_region
|
kwargs['location_name'] = new_region
|
||||||
_set_gps_from_zone(kwargs, zone)
|
_set_gps_from_zone(kwargs, zone)
|
||||||
_LOGGER.info("Exit from to %s", new_region)
|
_LOGGER.info("Exit to %s", new_region)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.info("Exit to GPS")
|
_LOGGER.info("Exit to GPS")
|
||||||
|
|
||||||
see(**kwargs)
|
see(**kwargs)
|
||||||
see_beacons(dev_id, kwargs)
|
see_beacons(dev_id, kwargs)
|
||||||
|
|
||||||
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
|
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
|
||||||
if location in beacons:
|
if location in beacons:
|
||||||
beacons.remove(location)
|
beacons.remove(location)
|
||||||
|
_LOGGER.info("Remove beacon %s", location)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
@ -141,6 +146,8 @@ def setup_scanner(hass, config, see):
|
|||||||
""" Set active beacons to the current location """
|
""" Set active beacons to the current location """
|
||||||
|
|
||||||
kwargs = kwargs_param.copy()
|
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]:
|
for beacon in MOBILE_BEACONS_ACTIVE[dev_id]:
|
||||||
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
|
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
|
||||||
kwargs['host_name'] = beacon
|
kwargs['host_name'] = beacon
|
||||||
|
@ -7,15 +7,15 @@ through SNMP.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.snmp/
|
https://home-assistant.io/components/device_tracker.snmp/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
|
||||||
import threading
|
|
||||||
import binascii
|
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.const import CONF_HOST
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
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/
|
https://home-assistant.io/components/device_tracker.thomson/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import re
|
import re
|
||||||
import threading
|
|
||||||
import telnetlib
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||||
|
@ -7,18 +7,18 @@ presence.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.tomato/
|
https://home-assistant.io/components/device_tracker.tomato/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
@ -9,15 +9,16 @@ https://home-assistant.io/components/device_tracker.tplink/
|
|||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
@ -7,17 +7,18 @@ presence.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.ubus/
|
https://home-assistant.io/components/device_tracker.ubus/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
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.helpers import validate_config
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
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.
|
Starts a service to scan in intervals for new devices.
|
||||||
|
|
||||||
Will emit EVENT_PLATFORM_DISCOVERED whenever a new service has been discovered.
|
Will emit EVENT_PLATFORM_DISCOVERED whenever a new service has been discovered.
|
||||||
@ -13,8 +11,8 @@ import threading
|
|||||||
|
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_PLATFORM_DISCOVERED,
|
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START,
|
||||||
ATTR_SERVICE, ATTR_DISCOVERED)
|
EVENT_PLATFORM_DISCOVERED)
|
||||||
|
|
||||||
DOMAIN = "discovery"
|
DOMAIN = "discovery"
|
||||||
REQUIREMENTS = ['netdisco==0.5.2']
|
REQUIREMENTS = ['netdisco==0.5.2']
|
||||||
@ -29,7 +27,7 @@ SERVICE_SONOS = 'sonos'
|
|||||||
SERVICE_PLEX = 'plex_mediaserver'
|
SERVICE_PLEX = 'plex_mediaserver'
|
||||||
|
|
||||||
SERVICE_HANDLERS = {
|
SERVICE_HANDLERS = {
|
||||||
SERVICE_WEMO: "switch",
|
SERVICE_WEMO: "wemo",
|
||||||
SERVICE_CAST: "media_player",
|
SERVICE_CAST: "media_player",
|
||||||
SERVICE_HUE: "light",
|
SERVICE_HUE: "light",
|
||||||
SERVICE_NETGEAR: 'device_tracker',
|
SERVICE_NETGEAR: 'device_tracker',
|
||||||
@ -39,24 +37,41 @@ SERVICE_HANDLERS = {
|
|||||||
|
|
||||||
|
|
||||||
def listen(hass, service, callback):
|
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.
|
Service can be a string or a list/tuple.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(service, str):
|
if isinstance(service, str):
|
||||||
service = (service,)
|
service = (service,)
|
||||||
else:
|
else:
|
||||||
service = tuple(service)
|
service = tuple(service)
|
||||||
|
|
||||||
def discovery_event_listener(event):
|
def discovery_event_listener(event):
|
||||||
""" Listens for discovery events. """
|
"""Listen for discovery events."""
|
||||||
if event.data[ATTR_SERVICE] in service:
|
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)
|
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):
|
def setup(hass, config):
|
||||||
""" Starts a discovery service. """
|
""" Starts a discovery service. """
|
||||||
logger = logging.getLogger(__name__)
|
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
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/downloader/
|
https://home-assistant.io/components/downloader/
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
@ -1,40 +1,20 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.ecobee
|
homeassistant.components.ecobee
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Ecobee component
|
||||||
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
|
|
||||||
|
|
||||||
|
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 logging
|
||||||
import os
|
import os
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from homeassistant.loader import get_component
|
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
from homeassistant.util import Throttle
|
|
||||||
from homeassistant.const import (
|
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"
|
DOMAIN = "ecobee"
|
||||||
DISCOVER_THERMOSTAT = "ecobee.thermostat"
|
DISCOVER_THERMOSTAT = "ecobee.thermostat"
|
||||||
@ -82,7 +62,7 @@ def request_configuration(network, hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def setup_ecobee(hass, network, 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 ecobee has a PIN then it needs to be configured.
|
||||||
if network.pin is not None:
|
if network.pin is not None:
|
||||||
request_configuration(network, hass, config)
|
request_configuration(network, hass, config)
|
||||||
|
@ -11,6 +11,7 @@ import logging
|
|||||||
from . import version, mdi_version
|
from . import version, mdi_version
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.const import URL_ROOT, HTTP_OK
|
from homeassistant.const import URL_ROOT, HTTP_OK
|
||||||
|
from homeassistant.components import api
|
||||||
|
|
||||||
DOMAIN = 'frontend'
|
DOMAIN = 'frontend'
|
||||||
DEPENDENCIES = ['api']
|
DEPENDENCIES = ['api']
|
||||||
@ -25,21 +26,23 @@ FRONTEND_URLS = [
|
|||||||
re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)'),
|
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)
|
_FINGERPRINT = re.compile(r'^(\w+)-[a-z0-9]{32}\.(\w+)$', re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup serving the frontend. """
|
""" 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:
|
for url in FRONTEND_URLS:
|
||||||
hass.http.register_path('GET', url, _handle_get_root, False)
|
hass.http.register_path('GET', url, _handle_get_root, False)
|
||||||
|
|
||||||
hass.http.register_path('GET', '/service_worker.js',
|
hass.http.register_path('GET', '/service_worker.js',
|
||||||
_handle_get_service_worker, False)
|
_handle_get_service_worker, False)
|
||||||
|
|
||||||
|
# Bootstrap API
|
||||||
|
hass.http.register_path(
|
||||||
|
'GET', URL_API_BOOTSTRAP, _handle_get_api_bootstrap)
|
||||||
|
|
||||||
# Static files
|
# Static files
|
||||||
hass.http.register_path(
|
hass.http.register_path(
|
||||||
'GET', re.compile(r'/static/(?P<file>[a-zA-Z\._\-0-9/]+)'),
|
'GET', re.compile(r'/static/(?P<file>[a-zA-Z\._\-0-9/]+)'),
|
||||||
@ -54,6 +57,18 @@ def setup(hass, config):
|
|||||||
return True
|
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):
|
def _handle_get_root(handler, path_match, data):
|
||||||
""" Renders the frontend. """
|
""" Renders the frontend. """
|
||||||
handler.send_response(HTTP_OK)
|
handler.send_response(HTTP_OK)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by update_mdi script """
|
""" 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 """
|
""" 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.
|
Component to interface with garage doors that can be controlled remotely.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation
|
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):
|
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
|
entity_id = entity_id or ENTITY_ID_ALL_GARAGE_DOORS
|
||||||
return hass.states.is_state(entity_id, STATE_CLOSED)
|
return hass.states.is_state(entity_id, STATE_CLOSED)
|
||||||
|
|
||||||
|
|
||||||
def close_door(hass, entity_id=None):
|
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
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||||
hass.services.call(DOMAIN, SERVICE_CLOSE, data)
|
hass.services.call(DOMAIN, SERVICE_CLOSE, data)
|
||||||
|
|
||||||
|
|
||||||
def open_door(hass, entity_id=None):
|
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
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
|
||||||
hass.services.call(DOMAIN, SERVICE_OPEN, data)
|
hass.services.call(DOMAIN, SERVICE_OPEN, data)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for garage door. """
|
"""Track states and offer events for garage door."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
|
||||||
GROUP_NAME_ALL_GARAGE_DOORS)
|
GROUP_NAME_ALL_GARAGE_DOORS)
|
||||||
component.setup(config)
|
component.setup(config)
|
||||||
|
|
||||||
def handle_garage_door_service(service):
|
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)
|
target_locks = component.extract_from_service(service)
|
||||||
|
|
||||||
for item in target_locks:
|
for item in target_locks:
|
||||||
@ -83,25 +81,24 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class GarageDoorDevice(Entity):
|
class GarageDoorDevice(Entity):
|
||||||
""" Represents a garage door. """
|
"""Represents a garage door."""
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def is_closed(self):
|
||||||
""" Is the garage door closed or opened. """
|
"""Return true if door is closed."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def close_door(self):
|
def close_door(self):
|
||||||
""" Closes the garage door. """
|
"""Close the garage door."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def open_door(self):
|
def open_door(self):
|
||||||
""" Opens the garage door. """
|
"""Open the garage door."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" State of the garage door. """
|
"""Returns the state of the garage door."""
|
||||||
closed = self.is_closed
|
closed = self.is_closed
|
||||||
if closed is None:
|
if closed is None:
|
||||||
return STATE_UNKNOWN
|
return STATE_UNKNOWN
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.garage_door.demo
|
Demo garage door platform that has two fake doors.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Demo platform that has two fake garage 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.components.garage_door import GarageDoorDevice
|
||||||
from homeassistant.const import STATE_CLOSED, STATE_OPEN
|
from homeassistant.const import STATE_CLOSED, STATE_OPEN
|
||||||
@ -9,7 +10,7 @@ from homeassistant.const import STATE_CLOSED, STATE_OPEN
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
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([
|
add_devices_callback([
|
||||||
DemoGarageDoor('Left Garage Door', STATE_CLOSED),
|
DemoGarageDoor('Left Garage Door', STATE_CLOSED),
|
||||||
DemoGarageDoor('Right Garage Door', STATE_OPEN)
|
DemoGarageDoor('Right Garage Door', STATE_OPEN)
|
||||||
@ -17,32 +18,32 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class DemoGarageDoor(GarageDoorDevice):
|
class DemoGarageDoor(GarageDoorDevice):
|
||||||
""" Provides a demo garage door. """
|
"""Provides a demo garage door."""
|
||||||
def __init__(self, name, state):
|
def __init__(self, name, state):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = state
|
self._state = state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
""" No polling needed for a demo garage door. """
|
"""No polling needed for a demo garage door."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device if any. """
|
"""Return the name of the device if any."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def is_closed(self):
|
||||||
""" True if device is closed. """
|
"""Return true if garage door is closed."""
|
||||||
return self._state == STATE_CLOSED
|
return self._state == STATE_CLOSED
|
||||||
|
|
||||||
def close_door(self, **kwargs):
|
def close_door(self, **kwargs):
|
||||||
""" Close the device. """
|
"""Close the garage door."""
|
||||||
self._state = STATE_CLOSED
|
self._state = STATE_CLOSED
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
|
||||||
def open_door(self, **kwargs):
|
def open_door(self, **kwargs):
|
||||||
""" Open the device. """
|
"""Open the garage door."""
|
||||||
self._state = STATE_OPEN
|
self._state = STATE_OPEN
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.garage_door.wink
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for Wink garage doors.
|
Support for Wink garage doors.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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.components.garage_door import GarageDoorDevice
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
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):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the Wink platform. """
|
"""Sets up the Wink garage door platform."""
|
||||||
import pywink
|
import pywink
|
||||||
|
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
@ -34,34 +32,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class WinkGarageDoorDevice(GarageDoorDevice):
|
class WinkGarageDoorDevice(GarageDoorDevice):
|
||||||
""" Represents a Wink garage door. """
|
"""Represents a Wink garage door."""
|
||||||
|
|
||||||
def __init__(self, wink):
|
def __init__(self, wink):
|
||||||
self.wink = wink
|
self.wink = wink
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
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())
|
return "{}.{}".format(self.__class__, self.wink.device_id())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
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()
|
return self.wink.name()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Update the state of the garage door. """
|
"""Update the state of the garage door."""
|
||||||
self.wink.update_state()
|
self.wink.update_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def is_closed(self):
|
||||||
""" True if device is closed. """
|
"""Returns true if door is closed."""
|
||||||
return self.wink.state() == 0
|
return self.wink.state() == 0
|
||||||
|
|
||||||
def close_door(self):
|
def close_door(self):
|
||||||
""" Close the device. """
|
"""Closes the door."""
|
||||||
self.wink.set_state(0)
|
self.wink.set_state(0)
|
||||||
|
|
||||||
def open_door(self):
|
def open_door(self):
|
||||||
""" Open the device. """
|
"""Open the door."""
|
||||||
self.wink.set_state(1)
|
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
|
Component that records all events and state changes and feeds the data to
|
||||||
a graphite installation.
|
a Graphite installation.
|
||||||
|
|
||||||
Example configuration:
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/graphite/
|
||||||
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'.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import queue
|
import queue
|
||||||
@ -22,8 +12,7 @@ import threading
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_STATE_CHANGED,
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED)
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
|
|
||||||
from homeassistant.helpers import state
|
from homeassistant.helpers import state
|
||||||
|
|
||||||
DOMAIN = "graphite"
|
DOMAIN = "graphite"
|
||||||
@ -31,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup graphite feeder. """
|
"""Setup the Graphite feeder."""
|
||||||
graphite_config = config.get('graphite', {})
|
graphite_config = config.get('graphite', {})
|
||||||
host = graphite_config.get('host', 'localhost')
|
host = graphite_config.get('host', 'localhost')
|
||||||
prefix = graphite_config.get('prefix', 'ha')
|
prefix = graphite_config.get('prefix', 'ha')
|
||||||
@ -46,42 +35,48 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class GraphiteFeeder(threading.Thread):
|
class GraphiteFeeder(threading.Thread):
|
||||||
""" Feeds data to graphite. """
|
"""Feeds data to Graphite."""
|
||||||
def __init__(self, hass, host, port, prefix):
|
def __init__(self, hass, host, port, prefix):
|
||||||
super(GraphiteFeeder, self).__init__(daemon=True)
|
super(GraphiteFeeder, self).__init__(daemon=True)
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
# rstrip any trailing dots in case they think they
|
# rstrip any trailing dots in case they think they need it
|
||||||
# need it
|
|
||||||
self._prefix = prefix.rstrip('.')
|
self._prefix = prefix.rstrip('.')
|
||||||
self._queue = queue.Queue()
|
self._queue = queue.Queue()
|
||||||
self._quit_object = object()
|
self._quit_object = object()
|
||||||
|
self._we_started = False
|
||||||
|
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START,
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START,
|
||||||
self.start_listen)
|
self.start_listen)
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||||
self.shutdown)
|
self.shutdown)
|
||||||
hass.bus.listen(EVENT_STATE_CHANGED, self.event_listener)
|
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):
|
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()
|
self.start()
|
||||||
|
|
||||||
def shutdown(self, event):
|
def shutdown(self, event):
|
||||||
""" Tell the thread that we are done.
|
"""Signal shutdown of processing event."""
|
||||||
|
_LOGGER.debug('Event processing signaled exit')
|
||||||
This does not block because there is nothing to
|
|
||||||
clean up (and no penalty for killing in-process
|
|
||||||
connections to graphite.
|
|
||||||
"""
|
|
||||||
self._queue.put(self._quit_object)
|
self._queue.put(self._quit_object)
|
||||||
|
|
||||||
def event_listener(self, event):
|
def event_listener(self, event):
|
||||||
""" Queue an event for processing. """
|
"""Queue an event for processing."""
|
||||||
self._queue.put(event)
|
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):
|
def _send_to_graphite(self, data):
|
||||||
|
"""Send data to Graphite."""
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(10)
|
sock.settimeout(10)
|
||||||
sock.connect((self._host, self._port))
|
sock.connect((self._host, self._port))
|
||||||
@ -90,6 +85,7 @@ class GraphiteFeeder(threading.Thread):
|
|||||||
sock.close()
|
sock.close()
|
||||||
|
|
||||||
def _report_attributes(self, entity_id, new_state):
|
def _report_attributes(self, entity_id, new_state):
|
||||||
|
"""Report the attributes."""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
things = dict(new_state.attributes)
|
things = dict(new_state.attributes)
|
||||||
try:
|
try:
|
||||||
@ -106,17 +102,33 @@ class GraphiteFeeder(threading.Thread):
|
|||||||
_LOGGER.debug('Sending to graphite: %s', lines)
|
_LOGGER.debug('Sending to graphite: %s', lines)
|
||||||
try:
|
try:
|
||||||
self._send_to_graphite('\n'.join(lines))
|
self._send_to_graphite('\n'.join(lines))
|
||||||
|
except socket.gaierror:
|
||||||
|
_LOGGER.error('Unable to connect to host %s', self._host)
|
||||||
except socket.error:
|
except socket.error:
|
||||||
_LOGGER.exception('Failed to send data to graphite')
|
_LOGGER.exception('Failed to send data to graphite')
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""Run the process to export the data."""
|
||||||
while True:
|
while True:
|
||||||
event = self._queue.get()
|
event = self._queue.get()
|
||||||
if event == self._quit_object:
|
if event == self._quit_object:
|
||||||
|
_LOGGER.debug('Event processing thread stopped')
|
||||||
self._queue.task_done()
|
self._queue.task_done()
|
||||||
return
|
return
|
||||||
elif (event.event_type == EVENT_STATE_CHANGED and
|
elif (event.event_type == EVENT_STATE_CHANGED and
|
||||||
'new_state' in event.data):
|
event.data.get('new_state')):
|
||||||
self._report_attributes(event.data['entity_id'],
|
_LOGGER.debug('Processing STATE_CHANGED event for %s',
|
||||||
event.data['new_state'])
|
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()
|
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/
|
https://home-assistant.io/components/group/
|
||||||
"""
|
"""
|
||||||
import homeassistant.core as ha
|
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 (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
|
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
|
||||||
STATE_HOME, STATE_NOT_HOME, STATE_OPEN, STATE_CLOSED,
|
STATE_NOT_HOME, STATE_OFF, STATE_ON, STATE_OPEN, STATE_UNKNOWN,
|
||||||
STATE_UNKNOWN, CONF_NAME, CONF_ICON)
|
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'
|
DOMAIN = 'group'
|
||||||
|
|
||||||
@ -145,6 +145,7 @@ class Group(Entity):
|
|||||||
self.tracking = []
|
self.tracking = []
|
||||||
self.group_on = None
|
self.group_on = None
|
||||||
self.group_off = None
|
self.group_off = None
|
||||||
|
self._assumed_state = False
|
||||||
|
|
||||||
if entity_ids is not None:
|
if entity_ids is not None:
|
||||||
self.update_tracked_entity_ids(entity_ids)
|
self.update_tracked_entity_ids(entity_ids)
|
||||||
@ -183,6 +184,11 @@ class Group(Entity):
|
|||||||
data[ATTR_VIEW] = True
|
data[ATTR_VIEW] = True
|
||||||
return data
|
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):
|
def update_tracked_entity_ids(self, entity_ids):
|
||||||
""" Update the tracked entity IDs. """
|
""" Update the tracked entity IDs. """
|
||||||
self.stop()
|
self.stop()
|
||||||
@ -208,47 +214,77 @@ class Group(Entity):
|
|||||||
def update(self):
|
def update(self):
|
||||||
""" Query all the tracked states and determine current group state. """
|
""" Query all the tracked states and determine current group state. """
|
||||||
self._state = STATE_UNKNOWN
|
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:
|
for entity_id in self.tracking:
|
||||||
state = self.hass.states.get(entity_id)
|
state = self.hass.states.get(entity_id)
|
||||||
|
|
||||||
if state is not None:
|
if state is not None:
|
||||||
self._process_tracked_state(state)
|
states.append(state)
|
||||||
|
|
||||||
def _state_changed_listener(self, entity_id, old_state, new_state):
|
return states
|
||||||
""" Listener to receive state changes of tracked entities. """
|
|
||||||
self._process_tracked_state(new_state)
|
|
||||||
self.update_ha_state()
|
|
||||||
|
|
||||||
def _process_tracked_state(self, tr_state):
|
def _update_group_state(self, tr_state=None):
|
||||||
""" Updates group state based on a new state of a tracked entity. """
|
"""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
|
# We have not determined type of group yet
|
||||||
if self.group_on is None:
|
if gr_on is None:
|
||||||
self.group_on, self.group_off = _get_group_on_off(tr_state.state)
|
if tr_state is None:
|
||||||
|
states = self._tracking_states
|
||||||
|
|
||||||
if self.group_on is not None:
|
for state in states:
|
||||||
# New state of the group is going to be based on the first
|
gr_on, gr_off = \
|
||||||
# state that we can recognize
|
_get_group_on_off(state.state)
|
||||||
self._state = tr_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
|
return
|
||||||
|
|
||||||
# There is already a group state
|
if tr_state is None or (gr_state == gr_on and
|
||||||
cur_gr_state = self._state
|
tr_state.state == gr_off):
|
||||||
group_on, group_off = self.group_on, self.group_off
|
if states is None:
|
||||||
|
states = self._tracking_states
|
||||||
|
|
||||||
# if cur_gr_state = OFF and tr_state = ON: set ON
|
if any(state.state == gr_on for state in states):
|
||||||
# if cur_gr_state = ON and tr_state = OFF: research
|
self._state = gr_on
|
||||||
# else: ignore
|
else:
|
||||||
|
self._state = gr_off
|
||||||
|
|
||||||
if cur_gr_state == group_off and tr_state.state == group_on:
|
elif tr_state.state in (gr_on, gr_off):
|
||||||
self._state = group_on
|
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
|
self._assumed_state = any(state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
if not any(self.hass.states.is_state(ent_id, group_on)
|
for state in states)
|
||||||
for ent_id in self.tracking
|
|
||||||
if tr_state.entity_id != ent_id):
|
elif tr_state.attributes.get(ATTR_ASSUMED_STATE):
|
||||||
self._state = group_off
|
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/
|
https://home-assistant.io/components/history/
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
import homeassistant.components.recorder as recorder
|
import homeassistant.components.recorder as recorder
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.const import HTTP_BAD_REQUEST
|
from homeassistant.const import HTTP_BAD_REQUEST
|
||||||
|
|
||||||
DOMAIN = 'history'
|
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
|
For more details about the RESTful API, please refer to the documentation at
|
||||||
https://home-assistant.io/developers/api/
|
https://home-assistant.io/developers/api/
|
||||||
"""
|
"""
|
||||||
from datetime import timedelta
|
|
||||||
import gzip
|
import gzip
|
||||||
from http import cookies
|
|
||||||
from http.server import SimpleHTTPRequestHandler, HTTPServer
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from socketserver import ThreadingMixIn
|
|
||||||
import ssl
|
import ssl
|
||||||
import threading
|
import threading
|
||||||
import time
|
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
|
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.remote as rem
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
import homeassistant.util.dt as date_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"
|
DOMAIN = "http"
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ For more details about this component, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/ifttt/
|
https://home-assistant.io/components/ifttt/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant.helpers import validate_config
|
from homeassistant.helpers import validate_config
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.influxdb
|
A component which allows you to send data to an Influx database.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
InfluxDB component which allows you to send data to an Influx database.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/influxdb/
|
https://home-assistant.io/components/influxdb/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.util as util
|
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.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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -35,8 +34,7 @@ CONF_VERIFY_SSL = 'verify_ssl'
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup the InfluxDB component. """
|
"""Setup the InfluxDB component."""
|
||||||
|
|
||||||
from influxdb import InfluxDBClient, exceptions
|
from influxdb import InfluxDBClient, exceptions
|
||||||
|
|
||||||
if not validate_config(config, {DOMAIN: ['host',
|
if not validate_config(config, {DOMAIN: ['host',
|
||||||
@ -62,13 +60,12 @@ def setup(hass, config):
|
|||||||
influx.query("select * from /.*/ LIMIT 1;")
|
influx.query("select * from /.*/ LIMIT 1;")
|
||||||
except exceptions.InfluxDBClientError as exc:
|
except exceptions.InfluxDBClientError as exc:
|
||||||
_LOGGER.error("Database host is not accessible due to '%s', please "
|
_LOGGER.error("Database host is not accessible due to '%s', please "
|
||||||
"check your entries in the configuration file and that"
|
"check your entries in the configuration file and that "
|
||||||
" the database exists and is READ/WRITE.", exc)
|
"the database exists and is READ/WRITE.", exc)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def influx_event_listener(event):
|
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')
|
state = event.data.get('new_state')
|
||||||
if state is None or state.state in (STATE_UNKNOWN, ''):
|
if state is None or state.state in (STATE_UNKNOWN, ''):
|
||||||
return
|
return
|
||||||
|
@ -9,9 +9,9 @@ at https://home-assistant.io/components/input_boolean/
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON)
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
DOMAIN = 'input_boolean'
|
DOMAIN = 'input_boolean'
|
||||||
|
@ -9,8 +9,8 @@ at https://home-assistant.io/components/input_select/
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
DOMAIN = 'input_select'
|
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/
|
https://home-assistant.io/components/insteon_hub/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.bootstrap as bootstrap
|
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 (
|
from homeassistant.const import (
|
||||||
CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY, ATTR_DISCOVERED,
|
ATTR_DISCOVERED, ATTR_SERVICE, CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME,
|
||||||
ATTR_SERVICE, EVENT_PLATFORM_DISCOVERED)
|
EVENT_PLATFORM_DISCOVERED)
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
DOMAIN = "insteon_hub"
|
DOMAIN = "insteon_hub"
|
||||||
REQUIREMENTS = ['insteon_hub==0.4.5']
|
REQUIREMENTS = ['insteon_hub==0.4.5']
|
||||||
|
@ -11,13 +11,12 @@ import logging
|
|||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from homeassistant import bootstrap
|
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 import validate_config
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
from homeassistant.const import (
|
from homeassistant.loader import get_component
|
||||||
CONF_HOST, CONF_USERNAME, CONF_PASSWORD, EVENT_PLATFORM_DISCOVERED,
|
|
||||||
EVENT_HOMEASSISTANT_STOP, ATTR_SERVICE, ATTR_DISCOVERED,
|
|
||||||
ATTR_FRIENDLY_NAME)
|
|
||||||
|
|
||||||
DOMAIN = "isy994"
|
DOMAIN = "isy994"
|
||||||
REQUIREMENTS = ['PyISY==1.0.5']
|
REQUIREMENTS = ['PyISY==1.0.5']
|
||||||
@ -147,7 +146,7 @@ class ISYDeviceABC(ToggleEntity):
|
|||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Returns the state attributes for the node. """
|
""" Returns the state attributes for the node. """
|
||||||
attr = {ATTR_FRIENDLY_NAME: self.name}
|
attr = {}
|
||||||
for name, prop in self._attrs.items():
|
for name, prop in self._attrs.items():
|
||||||
attr[name] = getattr(self, prop)
|
attr[name] = getattr(self, prop)
|
||||||
attr = self._attr_filter(attr)
|
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/
|
https://home-assistant.io/components/keyboard/
|
||||||
"""
|
"""
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
|
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PLAY_PAUSE,
|
||||||
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK,
|
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
|
||||||
SERVICE_MEDIA_PLAY_PAUSE)
|
SERVICE_VOLUME_UP)
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = "keyboard"
|
DOMAIN = "keyboard"
|
||||||
REQUIREMENTS = ['pyuserinput==0.1.9']
|
REQUIREMENTS = ['pyuserinput==0.1.9']
|
||||||
|
@ -11,7 +11,7 @@ import os
|
|||||||
import csv
|
import csv
|
||||||
|
|
||||||
from homeassistant.components import (
|
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.config import load_yaml_config_file
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
|
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
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
|
wemo.DISCOVER_LIGHTS: 'wemo',
|
||||||
wink.DISCOVER_LIGHTS: 'wink',
|
wink.DISCOVER_LIGHTS: 'wink',
|
||||||
insteon_hub.DISCOVER_LIGHTS: 'insteon_hub',
|
insteon_hub.DISCOVER_LIGHTS: 'insteon_hub',
|
||||||
isy994.DISCOVER_LIGHTS: 'isy994',
|
isy994.DISCOVER_LIGHTS: 'isy994',
|
||||||
|
@ -8,7 +8,7 @@ https://home-assistant.io/components/light.blinksticklight/
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.light import Light, ATTR_RGB_COLOR
|
from homeassistant.components.light import ATTR_RGB_COLOR, Light
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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