mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
commit
6797365d4f
12
.coveragerc
12
.coveragerc
@ -51,15 +51,15 @@ omit =
|
|||||||
homeassistant/components/zwave.py
|
homeassistant/components/zwave.py
|
||||||
homeassistant/components/*/zwave.py
|
homeassistant/components/*/zwave.py
|
||||||
|
|
||||||
homeassistant/components/rfxtrx.py
|
|
||||||
homeassistant/components/*/rfxtrx.py
|
|
||||||
|
|
||||||
homeassistant/components/mysensors.py
|
homeassistant/components/mysensors.py
|
||||||
homeassistant/components/*/mysensors.py
|
homeassistant/components/*/mysensors.py
|
||||||
|
|
||||||
homeassistant/components/nest.py
|
homeassistant/components/nest.py
|
||||||
homeassistant/components/*/nest.py
|
homeassistant/components/*/nest.py
|
||||||
|
|
||||||
|
homeassistant/components/rfxtrx.py
|
||||||
|
homeassistant/components/*/rfxtrx.py
|
||||||
|
|
||||||
homeassistant/components/rpi_gpio.py
|
homeassistant/components/rpi_gpio.py
|
||||||
homeassistant/components/*/rpi_gpio.py
|
homeassistant/components/*/rpi_gpio.py
|
||||||
|
|
||||||
@ -123,6 +123,7 @@ omit =
|
|||||||
homeassistant/components/notify/telegram.py
|
homeassistant/components/notify/telegram.py
|
||||||
homeassistant/components/notify/twitter.py
|
homeassistant/components/notify/twitter.py
|
||||||
homeassistant/components/notify/xmpp.py
|
homeassistant/components/notify/xmpp.py
|
||||||
|
homeassistant/components/scene/hunterdouglas_powerview.py
|
||||||
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
|
||||||
@ -139,8 +140,8 @@ omit =
|
|||||||
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/steam_online.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
|
||||||
homeassistant/components/sensor/temper.py
|
homeassistant/components/sensor/temper.py
|
||||||
@ -150,8 +151,8 @@ omit =
|
|||||||
homeassistant/components/sensor/twitch.py
|
homeassistant/components/sensor/twitch.py
|
||||||
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/dlink.py
|
homeassistant/components/switch/dlink.py
|
||||||
|
homeassistant/components/switch/edimax.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
|
||||||
@ -162,6 +163,7 @@ omit =
|
|||||||
homeassistant/components/thermostat/proliphix.py
|
homeassistant/components/thermostat/proliphix.py
|
||||||
homeassistant/components/thermostat/radiotherm.py
|
homeassistant/components/thermostat/radiotherm.py
|
||||||
|
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
# Regexes for lines to exclude from consideration
|
# Regexes for lines to exclude from consideration
|
||||||
exclude_lines =
|
exclude_lines =
|
||||||
|
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
**Related issue (if applicable):** #
|
**Related issue (if applicable):** #
|
||||||
|
|
||||||
**Example entry for `configuration.yaml` (if applicable):**
|
**Example entry for `configuration.yaml` (if applicable):**
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
@ -9,17 +10,17 @@
|
|||||||
|
|
||||||
**Checklist:**
|
**Checklist:**
|
||||||
|
|
||||||
- [ ] Local tests with `tox` ran successfully.
|
- [ ] Local tests with `tox` run successfully.
|
||||||
- [ ] No CI failures. **Your PR cannot be merged unless CI is green!**
|
- [ ] TravisCI does not fail. **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.
|
- [ ] [Fork is up to date][fork] and was rebased on the `dev` branch before creating the PR.
|
||||||
|
- [ ] Commits have been [squashed][squash].
|
||||||
- If code communicates with devices:
|
- If code communicates with devices:
|
||||||
- [ ] 3rd party library/libraries for communication is/are added as dependencies via the `REQUIREMENTS` variable ([example][ex-requir]).
|
- [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]).
|
||||||
- [ ] 3rd party dependencies are imported inside functions that use them ([example][ex-import]).
|
- [ ] New dependencies are only 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 dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.
|
||||||
- [ ] New files were added to `.coveragerc`.
|
- [ ] New files were added to `.coveragerc`.
|
||||||
- If the code does not depend on external Python module:
|
- If the code does not interact with devices:
|
||||||
- [ ] Tests to verify that the code works are included.
|
- [ ] Tests have been added to verify that the new code works.
|
||||||
- [ ] [Commits will be squashed][squash] when the PR is ready to be merged.
|
|
||||||
|
|
||||||
[fork]: http://stackoverflow.com/a/7244456
|
[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
|
[squash]: https://github.com/ginatrapani/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit
|
||||||
|
@ -71,7 +71,7 @@ When you are done with development and ready to commit your changes, run `build_
|
|||||||
|
|
||||||
To test your code before submission, used the `tox` tool.
|
To test your code before submission, used the `tox` tool.
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
> pip install -U tox
|
> pip install -U tox
|
||||||
> tox
|
> tox
|
||||||
```
|
```
|
||||||
|
12
Dockerfile
12
Dockerfile
@ -6,19 +6,17 @@ 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
|
RUN pip3 install --no-cache-dir colorlog cython
|
||||||
|
|
||||||
# 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 cython3 libudev-dev sudo && \
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
COPY script/build_python_openzwave script/build_python_openzwave
|
COPY script/build_python_openzwave script/build_python_openzwave
|
||||||
RUN apt-get update && \
|
RUN script/build_python_openzwave && \
|
||||||
apt-get install -y cython3 libudev-dev && \
|
mkdir -p /usr/local/share/python-openzwave && \
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
ln -sf /usr/src/app/build/python-openzwave/openzwave/config /usr/local/share/python-openzwave/config
|
||||||
pip3 install "cython<0.23" && \
|
|
||||||
script/build_python_openzwave
|
|
||||||
|
|
||||||
COPY requirements_all.txt requirements_all.txt
|
COPY requirements_all.txt requirements_all.txt
|
||||||
RUN pip3 install --no-cache-dir -r requirements_all.txt
|
RUN pip3 install --no-cache-dir -r requirements_all.txt
|
||||||
|
@ -101,9 +101,10 @@ def track_devices(hass, entity_id, old_state, new_state):
|
|||||||
|
|
||||||
@track_time_change(hour=7, minute=0, second=0)
|
@track_time_change(hour=7, minute=0, second=0)
|
||||||
def wake_up(hass, now):
|
def wake_up(hass, now):
|
||||||
"""
|
"""Turn light on in the morning.
|
||||||
Turn it on in the morning (7 AM) if there are people home and
|
|
||||||
it is not already on.
|
Turn the light on at 7 AM if there are people home and it is not already
|
||||||
|
on.
|
||||||
"""
|
"""
|
||||||
if not TARGET_ID:
|
if not TARGET_ID:
|
||||||
return
|
return
|
||||||
@ -126,8 +127,9 @@ def all_lights_off(hass, entity_id, old_state, new_state):
|
|||||||
|
|
||||||
@service(DOMAIN, SERVICE_FLASH)
|
@service(DOMAIN, SERVICE_FLASH)
|
||||||
def flash_service(hass, call):
|
def flash_service(hass, call):
|
||||||
"""
|
"""Service that will toggle the target.
|
||||||
Service that will turn the target off for 10 seconds if on and vice versa.
|
|
||||||
|
Set the light to off for 10 seconds if on and vice versa.
|
||||||
"""
|
"""
|
||||||
if not TARGET_ID:
|
if not TARGET_ID:
|
||||||
return
|
return
|
||||||
|
@ -20,7 +20,6 @@ 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!')
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
"""Init file for Home Assistant."""
|
@ -12,21 +12,26 @@ from multiprocessing import Process
|
|||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, RESTART_EXIT_CODE, __version__)
|
__version__,
|
||||||
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
REQUIRED_PYTHON_VER,
|
||||||
|
RESTART_EXIT_CODE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_python():
|
def validate_python():
|
||||||
"""Validate we're running the right Python version."""
|
"""Validate we're running the right Python version."""
|
||||||
major, minor = sys.version_info[:2]
|
major, minor = sys.version_info[:2]
|
||||||
|
req_major, req_minor = REQUIRED_PYTHON_VER
|
||||||
|
|
||||||
if major < 3 or (major == 3 and minor < 4):
|
if major < req_major or (major == req_major and minor < req_minor):
|
||||||
print("Home Assistant requires atleast Python 3.4")
|
print("Home Assistant requires at least Python {}.{}".format(
|
||||||
|
req_major, req_minor))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_path(config_dir):
|
def ensure_config_path(config_dir):
|
||||||
""" Validates configuration directory. """
|
"""Validate the configuration directory."""
|
||||||
|
|
||||||
lib_dir = os.path.join(config_dir, 'lib')
|
lib_dir = os.path.join(config_dir, 'lib')
|
||||||
|
|
||||||
# Test if configuration directory exists
|
# Test if configuration directory exists
|
||||||
@ -130,25 +135,25 @@ def get_arguments():
|
|||||||
|
|
||||||
|
|
||||||
def daemonize():
|
def daemonize():
|
||||||
""" Move current process to daemon process """
|
"""Move current process to daemon process."""
|
||||||
# create first fork
|
# Create first fork
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid > 0:
|
if pid > 0:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# decouple fork
|
# Decouple fork
|
||||||
os.setsid()
|
os.setsid()
|
||||||
os.umask(0)
|
os.umask(0)
|
||||||
|
|
||||||
# create second fork
|
# Create second fork
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid > 0:
|
if pid > 0:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def check_pid(pid_file):
|
def check_pid(pid_file):
|
||||||
""" Check that HA is not already running """
|
"""Check that HA is not already running."""
|
||||||
# check pid file
|
# Check pid file
|
||||||
try:
|
try:
|
||||||
pid = int(open(pid_file, 'r').readline())
|
pid = int(open(pid_file, 'r').readline())
|
||||||
except IOError:
|
except IOError:
|
||||||
@ -165,7 +170,7 @@ def check_pid(pid_file):
|
|||||||
|
|
||||||
|
|
||||||
def write_pid(pid_file):
|
def write_pid(pid_file):
|
||||||
""" Create PID File """
|
"""Create a PID File."""
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
try:
|
try:
|
||||||
open(pid_file, 'w').write(str(pid))
|
open(pid_file, 'w').write(str(pid))
|
||||||
@ -175,7 +180,7 @@ def write_pid(pid_file):
|
|||||||
|
|
||||||
|
|
||||||
def install_osx():
|
def install_osx():
|
||||||
""" Setup to run via launchd on OS X """
|
"""Setup to run via launchd on OS X."""
|
||||||
with os.popen('which hass') as inp:
|
with os.popen('which hass') as inp:
|
||||||
hass_path = inp.read().strip()
|
hass_path = inp.read().strip()
|
||||||
|
|
||||||
@ -207,7 +212,7 @@ def install_osx():
|
|||||||
|
|
||||||
|
|
||||||
def uninstall_osx():
|
def uninstall_osx():
|
||||||
""" Unload from launchd on OS X """
|
"""Unload from launchd on OS X."""
|
||||||
path = os.path.expanduser("~/Library/LaunchAgents/org.homeassistant.plist")
|
path = os.path.expanduser("~/Library/LaunchAgents/org.homeassistant.plist")
|
||||||
os.popen('launchctl unload ' + path)
|
os.popen('launchctl unload ' + path)
|
||||||
|
|
||||||
@ -215,9 +220,10 @@ def uninstall_osx():
|
|||||||
|
|
||||||
|
|
||||||
def setup_and_run_hass(config_dir, args, top_process=False):
|
def setup_and_run_hass(config_dir, args, top_process=False):
|
||||||
"""
|
"""Setup HASS and run.
|
||||||
Setup HASS and run. Block until stopped. Will assume it is running in a
|
|
||||||
subprocess unless top_process is set to true.
|
Block until stopped. Will assume it is running in a subprocess unless
|
||||||
|
top_process is set to true.
|
||||||
"""
|
"""
|
||||||
if args.demo_mode:
|
if args.demo_mode:
|
||||||
config = {
|
config = {
|
||||||
@ -253,12 +259,12 @@ def setup_and_run_hass(config_dir, args, top_process=False):
|
|||||||
|
|
||||||
|
|
||||||
def run_hass_process(hass_proc):
|
def run_hass_process(hass_proc):
|
||||||
""" Runs a child hass process. Returns True if it should be restarted. """
|
"""Run a child hass process. Returns True if it should be restarted."""
|
||||||
requested_stop = threading.Event()
|
requested_stop = threading.Event()
|
||||||
hass_proc.daemon = True
|
hass_proc.daemon = True
|
||||||
|
|
||||||
def request_stop(*args):
|
def request_stop(*args):
|
||||||
""" request hass stop, *args is for signal handler callback """
|
"""Request hass stop, *args is for signal handler callback."""
|
||||||
requested_stop.set()
|
requested_stop.set()
|
||||||
hass_proc.terminate()
|
hass_proc.terminate()
|
||||||
|
|
||||||
@ -283,7 +289,7 @@ def run_hass_process(hass_proc):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Starts Home Assistant. """
|
"""Start Home Assistant."""
|
||||||
validate_python()
|
validate_python()
|
||||||
|
|
||||||
args = get_arguments()
|
args = get_arguments()
|
||||||
@ -291,7 +297,7 @@ def main():
|
|||||||
config_dir = os.path.join(os.getcwd(), args.config)
|
config_dir = os.path.join(os.getcwd(), args.config)
|
||||||
ensure_config_path(config_dir)
|
ensure_config_path(config_dir)
|
||||||
|
|
||||||
# os x launchd functions
|
# OS X launchd functions
|
||||||
if args.install_osx:
|
if args.install_osx:
|
||||||
install_osx()
|
install_osx()
|
||||||
return 0
|
return 0
|
||||||
@ -305,7 +311,7 @@ def main():
|
|||||||
install_osx()
|
install_osx()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# daemon functions
|
# Daemon functions
|
||||||
if args.pid_file:
|
if args.pid_file:
|
||||||
check_pid(args.pid_file)
|
check_pid(args.pid_file)
|
||||||
if args.daemon:
|
if args.daemon:
|
||||||
|
@ -35,7 +35,6 @@ ERROR_LOG_FILENAME = 'home-assistant.log'
|
|||||||
|
|
||||||
def setup_component(hass, domain, config=None):
|
def setup_component(hass, domain, config=None):
|
||||||
"""Setup a component and all its dependencies."""
|
"""Setup a component and all its dependencies."""
|
||||||
|
|
||||||
if domain in hass.config.components:
|
if domain in hass.config.components:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ def setup_component(hass, domain, config=None):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_requirements(hass, component, name):
|
def _handle_requirements(hass, component, name):
|
||||||
""" Installs requirements for component. """
|
"""Install the requirements for a component."""
|
||||||
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
|
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -126,7 +125,7 @@ def _setup_component(hass, domain, config):
|
|||||||
|
|
||||||
|
|
||||||
def prepare_setup_platform(hass, config, domain, platform_name):
|
def prepare_setup_platform(hass, config, domain, platform_name):
|
||||||
""" Loads a platform and makes sure dependencies are setup. """
|
"""Load a platform and makes sure dependencies are setup."""
|
||||||
_ensure_loader_prepared(hass)
|
_ensure_loader_prepared(hass)
|
||||||
|
|
||||||
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
|
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
|
||||||
@ -158,7 +157,7 @@ def prepare_setup_platform(hass, config, domain, platform_name):
|
|||||||
|
|
||||||
|
|
||||||
def mount_local_lib_path(config_dir):
|
def mount_local_lib_path(config_dir):
|
||||||
""" Add local library to Python Path """
|
"""Add local library to Python Path."""
|
||||||
sys.path.insert(0, os.path.join(config_dir, 'lib'))
|
sys.path.insert(0, os.path.join(config_dir, 'lib'))
|
||||||
|
|
||||||
|
|
||||||
@ -166,8 +165,7 @@ def mount_local_lib_path(config_dir):
|
|||||||
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||||
verbose=False, daemon=False, skip_pip=False,
|
verbose=False, daemon=False, skip_pip=False,
|
||||||
log_rotate_days=None):
|
log_rotate_days=None):
|
||||||
"""
|
"""Try to configure Home Assistant from a config dict.
|
||||||
Tries to configure Home Assistant from a config dict.
|
|
||||||
|
|
||||||
Dynamically loads required components and its dependencies.
|
Dynamically loads required components and its dependencies.
|
||||||
"""
|
"""
|
||||||
@ -209,7 +207,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
|||||||
|
|
||||||
_LOGGER.info('Home Assistant core initialized')
|
_LOGGER.info('Home Assistant core initialized')
|
||||||
|
|
||||||
# give event decorators access to HASS
|
# Give event decorators access to HASS
|
||||||
event_decorators.HASS = hass
|
event_decorators.HASS = hass
|
||||||
service.HASS = hass
|
service.HASS = hass
|
||||||
|
|
||||||
@ -222,9 +220,9 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
|||||||
|
|
||||||
def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
||||||
skip_pip=True, log_rotate_days=None):
|
skip_pip=True, log_rotate_days=None):
|
||||||
"""
|
"""Read the configuration file and try to start all the functionality.
|
||||||
Reads the configuration file and tries to start all the required
|
|
||||||
functionality. Will add functionality to 'hass' parameter if given,
|
Will add functionality to 'hass' parameter if given,
|
||||||
instantiates a new Home Assistant object if 'hass' is not given.
|
instantiates a new Home Assistant object if 'hass' is not given.
|
||||||
"""
|
"""
|
||||||
if hass is None:
|
if hass is None:
|
||||||
@ -244,7 +242,7 @@ def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
|||||||
|
|
||||||
|
|
||||||
def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
||||||
""" Setup the logging for home assistant. """
|
"""Setup the logging."""
|
||||||
if not daemon:
|
if not daemon:
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) "
|
fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) "
|
||||||
@ -322,11 +320,11 @@ def process_ha_config_upgrade(hass):
|
|||||||
|
|
||||||
|
|
||||||
def process_ha_core_config(hass, config):
|
def process_ha_core_config(hass, config):
|
||||||
""" Processes the [homeassistant] section from the config. """
|
"""Process the [homeassistant] section from the config."""
|
||||||
hac = hass.config
|
hac = hass.config
|
||||||
|
|
||||||
def set_time_zone(time_zone_str):
|
def set_time_zone(time_zone_str):
|
||||||
""" Helper method to set time zone in HA. """
|
"""Helper method to set time zone."""
|
||||||
if time_zone_str is None:
|
if time_zone_str is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
This package contains components that can be plugged into Home Assistant.
|
This package contains components that can be plugged into Home Assistant.
|
||||||
|
|
||||||
Component design guidelines:
|
Component design guidelines:
|
||||||
|
- Each component defines a constant DOMAIN that is equal to its filename.
|
||||||
Each component defines a constant DOMAIN that is equal to its filename.
|
- Each component that tracks states should create state entity names in the
|
||||||
|
|
||||||
Each component that tracks states should create state entity names in the
|
|
||||||
format "<DOMAIN>.<OBJECT_ID>".
|
format "<DOMAIN>.<OBJECT_ID>".
|
||||||
|
- Each component should publish services only under its own domain.
|
||||||
Each component should publish services only under its own domain.
|
|
||||||
"""
|
"""
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import logging
|
import logging
|
||||||
@ -26,8 +21,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id=None):
|
def is_on(hass, entity_id=None):
|
||||||
""" Loads up the module to call the is_on method.
|
"""Load up the module to call the is_on method.
|
||||||
If there is no entity id given we will check all. """
|
|
||||||
|
If there is no entity id given we will check all.
|
||||||
|
"""
|
||||||
if entity_id:
|
if entity_id:
|
||||||
group = get_component('group')
|
group = get_component('group')
|
||||||
|
|
||||||
@ -53,7 +50,7 @@ def is_on(hass, entity_id=None):
|
|||||||
|
|
||||||
|
|
||||||
def turn_on(hass, entity_id=None, **service_data):
|
def turn_on(hass, entity_id=None, **service_data):
|
||||||
""" Turns specified entity on if possible. """
|
"""Turn specified entity on if possible."""
|
||||||
if entity_id is not None:
|
if entity_id is not None:
|
||||||
service_data[ATTR_ENTITY_ID] = entity_id
|
service_data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
@ -61,7 +58,7 @@ def turn_on(hass, entity_id=None, **service_data):
|
|||||||
|
|
||||||
|
|
||||||
def turn_off(hass, entity_id=None, **service_data):
|
def turn_off(hass, entity_id=None, **service_data):
|
||||||
""" Turns specified entity off. """
|
"""Turn specified entity off."""
|
||||||
if entity_id is not None:
|
if entity_id is not None:
|
||||||
service_data[ATTR_ENTITY_ID] = entity_id
|
service_data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
@ -69,7 +66,7 @@ def turn_off(hass, entity_id=None, **service_data):
|
|||||||
|
|
||||||
|
|
||||||
def toggle(hass, entity_id=None, **service_data):
|
def toggle(hass, entity_id=None, **service_data):
|
||||||
""" Toggles specified entity. """
|
"""Toggle specified entity."""
|
||||||
if entity_id is not None:
|
if entity_id is not None:
|
||||||
service_data[ATTR_ENTITY_ID] = entity_id
|
service_data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
@ -77,8 +74,7 @@ def toggle(hass, entity_id=None, **service_data):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup general services related to homeassistant. """
|
"""Setup general services related to Home Assistant."""
|
||||||
|
|
||||||
def handle_turn_service(service):
|
def handle_turn_service(service):
|
||||||
"""Method to handle calls to homeassistant.turn_on/off."""
|
"""Method to handle calls to homeassistant.turn_on/off."""
|
||||||
entity_ids = extract_entity_ids(hass, service)
|
entity_ids = extract_entity_ids(hass, service)
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel
|
Component to interface with an alarm control panel.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Component to interface with a alarm control panel.
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/alarm_control_panel/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from homeassistant.components import verisure
|
from homeassistant.components import verisure
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
|
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
|
||||||
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
@ -31,9 +32,6 @@ SERVICE_TO_METHOD = {
|
|||||||
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
|
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
|
||||||
}
|
}
|
||||||
|
|
||||||
ATTR_CODE = 'code'
|
|
||||||
ATTR_CODE_FORMAT = 'code_format'
|
|
||||||
|
|
||||||
ATTR_TO_PROPERTY = [
|
ATTR_TO_PROPERTY = [
|
||||||
ATTR_CODE,
|
ATTR_CODE,
|
||||||
ATTR_CODE_FORMAT
|
ATTR_CODE_FORMAT
|
||||||
@ -49,7 +47,7 @@ def setup(hass, config):
|
|||||||
component.setup(config)
|
component.setup(config)
|
||||||
|
|
||||||
def alarm_service_handler(service):
|
def alarm_service_handler(service):
|
||||||
""" Maps services to methods on Alarm. """
|
"""Map services to methods on Alarm."""
|
||||||
target_alarms = component.extract_from_service(service)
|
target_alarms = component.extract_from_service(service)
|
||||||
|
|
||||||
if ATTR_CODE not in service.data:
|
if ATTR_CODE not in service.data:
|
||||||
@ -120,11 +118,11 @@ def alarm_trigger(hass, code=None, entity_id=None):
|
|||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
class AlarmControlPanel(Entity):
|
class AlarmControlPanel(Entity):
|
||||||
""" ABC for alarm control devices. """
|
"""An abstract class for alarm control devices."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self):
|
def code_format(self):
|
||||||
""" regex for code format or None if no code is required. """
|
"""Regex for code format or None if no code is required."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
def alarm_disarm(self, code=None):
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.alarmdotcom
|
Interfaces with Alarm.com alarm control panels.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Interfaces with Verisure alarm control panel.
|
|
||||||
|
|
||||||
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.alarmdotcom/
|
https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
|
||||||
@ -24,7 +22,6 @@ DEFAULT_NAME = 'Alarm.com'
|
|||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup an Alarm.com control panel."""
|
"""Setup an Alarm.com control panel."""
|
||||||
|
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
@ -42,9 +39,10 @@ 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
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
class AlarmDotCom(alarm.AlarmControlPanel):
|
class AlarmDotCom(alarm.AlarmControlPanel):
|
||||||
""" Represents a Alarm.com status. """
|
"""Represent an Alarm.com status."""
|
||||||
|
|
||||||
def __init__(self, hass, name, code, username, password):
|
def __init__(self, hass, name, code, username, password):
|
||||||
|
"""Initialize the Alarm.com status."""
|
||||||
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
|
from pyalarmdotcom.pyalarmdotcom import Alarmdotcom
|
||||||
self._alarm = Alarmdotcom(username, password, timeout=10)
|
self._alarm = Alarmdotcom(username, password, timeout=10)
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
@ -60,7 +58,7 @@ class AlarmDotCom(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device. """
|
"""Return the name of the alarm."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -70,7 +68,7 @@ class AlarmDotCom(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
"""Return the state of the device."""
|
||||||
if self._alarm.state == 'Disarmed':
|
if self._alarm.state == 'Disarmed':
|
||||||
return STATE_ALARM_DISARMED
|
return STATE_ALARM_DISARMED
|
||||||
elif self._alarm.state == 'Armed Stay':
|
elif self._alarm.state == 'Armed Stay':
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.manual
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for manual alarms.
|
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
|
||||||
@ -24,8 +22,7 @@ DEFAULT_TRIGGER_TIME = 120
|
|||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the manual alarm platform. """
|
"""Setup the manual alarm platform."""
|
||||||
|
|
||||||
add_devices([ManualAlarm(
|
add_devices([ManualAlarm(
|
||||||
hass,
|
hass,
|
||||||
config.get('name', DEFAULT_ALARM_NAME),
|
config.get('name', DEFAULT_ALARM_NAME),
|
||||||
@ -47,6 +44,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, hass, name, code, pending_time, trigger_time):
|
def __init__(self, hass, name, code, pending_time, trigger_time):
|
||||||
|
"""Initalize the manual alarm panel."""
|
||||||
self._state = STATE_ALARM_DISARMED
|
self._state = STATE_ALARM_DISARMED
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -62,12 +60,12 @@ class ManualAlarm(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device. """
|
"""Return the name of the device."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
"""Return the state of the device."""
|
||||||
if self._state in (STATE_ALARM_ARMED_HOME,
|
if self._state in (STATE_ALARM_ARMED_HOME,
|
||||||
STATE_ALARM_ARMED_AWAY) and \
|
STATE_ALARM_ARMED_AWAY) and \
|
||||||
self._pending_time and self._state_ts + self._pending_time > \
|
self._pending_time and self._state_ts + self._pending_time > \
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.mqtt
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
This platform enables the possibility to control a MQTT alarm.
|
This platform enables the possibility to control a MQTT alarm.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
@ -26,8 +24,7 @@ DEPENDENCIES = ['mqtt']
|
|||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the MQTT platform. """
|
"""Setup the MQTT platform."""
|
||||||
|
|
||||||
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")
|
||||||
return False
|
return False
|
||||||
@ -51,10 +48,11 @@ 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
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
class MqttAlarm(alarm.AlarmControlPanel):
|
class MqttAlarm(alarm.AlarmControlPanel):
|
||||||
""" represents a MQTT alarm status within home assistant. """
|
"""Represent a MQTT alarm status."""
|
||||||
|
|
||||||
def __init__(self, hass, name, state_topic, command_topic, qos,
|
def __init__(self, hass, name, state_topic, command_topic, qos,
|
||||||
payload_disarm, payload_arm_home, payload_arm_away, code):
|
payload_disarm, payload_arm_home, payload_arm_away, code):
|
||||||
|
"""Initalize the MQTT alarm panel."""
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -80,22 +78,22 @@ class MqttAlarm(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@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):
|
||||||
""" Returns the name of the device. """
|
"""Return the name of the device."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
"""Return the state of the device."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self):
|
def code_format(self):
|
||||||
""" One or more characters if code is defined """
|
"""One or more characters if code is defined."""
|
||||||
return None if self._code is None else '.+'
|
return None if self._code is None else '.+'
|
||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
def alarm_disarm(self, code=None):
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.nx584
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for NX584 alarm control panels.
|
Support for NX584 alarm control panels.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
@ -16,12 +14,11 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN)
|
STATE_UNKNOWN)
|
||||||
|
|
||||||
REQUIREMENTS = ['pynx584==0.2']
|
REQUIREMENTS = ['pynx584==0.2']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Setup nx584. """
|
"""Setup nx584 platform."""
|
||||||
host = config.get('host', 'localhost:5007')
|
host = config.get('host', 'localhost:5007')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -32,8 +29,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class NX584Alarm(alarm.AlarmControlPanel):
|
class NX584Alarm(alarm.AlarmControlPanel):
|
||||||
""" NX584-based alarm panel. """
|
"""Represents the NX584-based alarm panel."""
|
||||||
|
|
||||||
def __init__(self, hass, host, name):
|
def __init__(self, hass, host, name):
|
||||||
|
"""Initalize the nx584 alarm panel."""
|
||||||
from nx584 import client
|
from nx584 import client
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._host = host
|
self._host = host
|
||||||
@ -51,17 +50,17 @@ class NX584Alarm(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device. """
|
"""Return the name of the device."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self):
|
def code_format(self):
|
||||||
""" Characters if code is defined. """
|
"""The characters if code is defined."""
|
||||||
return '[0-9]{4}([0-9]{2})?'
|
return '[0-9]{4}([0-9]{2})?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
"""Return the state of the device."""
|
||||||
try:
|
try:
|
||||||
part = self._alarm.list_partitions()[0]
|
part = self._alarm.list_partitions()[0]
|
||||||
zones = self._alarm.list_zones()
|
zones = self._alarm.list_zones()
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.verisure
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Interfaces with Verisure alarm control panel.
|
Interfaces with Verisure alarm control panel.
|
||||||
|
|
||||||
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/verisure/
|
https://home-assistant.io/components/alarm_control_panel.verisure/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -19,8 +17,7 @@ _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. """
|
"""Setup the Verisure platform."""
|
||||||
|
|
||||||
alarms = []
|
alarms = []
|
||||||
if int(hub.config.get('alarm', '1')):
|
if int(hub.config.get('alarm', '1')):
|
||||||
hub.update_alarms()
|
hub.update_alarms()
|
||||||
@ -33,30 +30,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
class VerisureAlarm(alarm.AlarmControlPanel):
|
class VerisureAlarm(alarm.AlarmControlPanel):
|
||||||
""" Represents a Verisure alarm status. """
|
"""Represent a Verisure alarm status."""
|
||||||
|
|
||||||
def __init__(self, device_id):
|
def __init__(self, device_id):
|
||||||
|
"""Initalize the Verisure alarm panel."""
|
||||||
self._id = device_id
|
self._id = device_id
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
self._digits = int(hub.config.get('code_digits', '4'))
|
self._digits = int(hub.config.get('code_digits', '4'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device. """
|
"""Return the name of the device."""
|
||||||
return 'Alarm {}'.format(self._id)
|
return 'Alarm {}'.format(self._id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
"""Return the state of the device."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self):
|
def code_format(self):
|
||||||
""" code format as regex """
|
"""The code format as regex."""
|
||||||
return '^\\d{%s}$' % self._digits
|
return '^\\d{%s}$' % self._digits
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Update alarm status """
|
"""Update alarm status."""
|
||||||
hub.update_alarms()
|
hub.update_alarms()
|
||||||
|
|
||||||
if hub.alarm_status[self._id].status == 'unarmed':
|
if hub.alarm_status[self._id].status == 'unarmed':
|
||||||
|
@ -97,21 +97,24 @@ def _handle_alexa(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
class SpeechType(enum.Enum):
|
class SpeechType(enum.Enum):
|
||||||
"""Alexa speech types."""
|
"""The Alexa speech types."""
|
||||||
|
|
||||||
plaintext = "PlainText"
|
plaintext = "PlainText"
|
||||||
ssml = "SSML"
|
ssml = "SSML"
|
||||||
|
|
||||||
|
|
||||||
class CardType(enum.Enum):
|
class CardType(enum.Enum):
|
||||||
"""Alexa card types."""
|
"""The 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."""
|
"""Help generating the response for Alexa."""
|
||||||
|
|
||||||
def __init__(self, hass, intent=None):
|
def __init__(self, hass, intent=None):
|
||||||
|
"""Initialize the response."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.speech = None
|
self.speech = None
|
||||||
self.card = None
|
self.card = None
|
||||||
@ -163,7 +166,7 @@ class AlexaResponse(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
"""Returns response in an Alexa valid dict."""
|
"""Return response in an Alexa valid dict."""
|
||||||
response = {
|
response = {
|
||||||
'shouldEndSession': self.should_end_session
|
'shouldEndSession': self.should_end_session
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.apcupsd
|
Support for status output of APCUPSd via its Network Information Server (NIS).
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Sets up and provides access to the status output of APCUPSd via its Network
|
|
||||||
Information Server (NIS).
|
|
||||||
|
|
||||||
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/apcupsd/
|
https://home-assistant.io/components/apcupsd/
|
||||||
@ -54,11 +51,14 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class APCUPSdData(object):
|
class APCUPSdData(object):
|
||||||
|
"""Stores the data retrieved from APCUPSd.
|
||||||
|
|
||||||
|
For each entity to use, acts as the single point responsible for fetching
|
||||||
|
updates from the server.
|
||||||
"""
|
"""
|
||||||
Stores the data retrieved from APCUPSd for each entity to use, acts as the
|
|
||||||
single point responsible for fetching updates from the server.
|
|
||||||
"""
|
|
||||||
def __init__(self, host, port):
|
def __init__(self, host, port):
|
||||||
|
"""Initialize the data oject."""
|
||||||
from apcaccess import status
|
from apcaccess import status
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
@ -78,7 +78,5 @@ class APCUPSdData(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
"""
|
"""Fetch the latest status from APCUPSd."""
|
||||||
Fetch the latest status from APCUPSd and store it in self._status.
|
|
||||||
"""
|
|
||||||
self._status = self._get_status()
|
self._status = self._get_status()
|
||||||
|
@ -34,7 +34,6 @@ _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)
|
||||||
|
|
||||||
@ -96,7 +95,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_api(handler, path_match, data):
|
def _handle_get_api(handler, path_match, data):
|
||||||
"""Renders the debug interface."""
|
"""Render the debug interface."""
|
||||||
handler.write_json_message("API running.")
|
handler.write_json_message("API running.")
|
||||||
|
|
||||||
|
|
||||||
@ -114,7 +113,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."""
|
"""Write a message to the output."""
|
||||||
with write_lock:
|
with write_lock:
|
||||||
msg = "data: {}\n\n".format(payload)
|
msg = "data: {}\n\n".format(payload)
|
||||||
|
|
||||||
@ -127,7 +126,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."""
|
"""Forward 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:
|
||||||
@ -171,17 +170,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 configuration."""
|
"""Return 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_states(handler, path_match, data):
|
def _handle_get_api_states(handler, path_match, data):
|
||||||
"""Returns a dict containing all entity ids and their state."""
|
"""Return 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."""
|
"""Return 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)
|
||||||
@ -193,7 +192,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.
|
"""Handle 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,15 +239,14 @@ def _handle_delete_state_entity(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_api_events(handler, path_match, data):
|
def _handle_get_api_events(handler, path_match, data):
|
||||||
"""Handles getting overview of event listeners."""
|
"""Handle 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.
|
"""Handle firing of an event.
|
||||||
|
|
||||||
This handles the following paths:
|
This handles the following paths: /api/events/<event_type>
|
||||||
/api/events/<event_type>
|
|
||||||
|
|
||||||
Events from /api are threated as remote events.
|
Events from /api are threated as remote events.
|
||||||
"""
|
"""
|
||||||
@ -276,16 +274,15 @@ 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."""
|
"""Handle 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.
|
"""Handle calling a service.
|
||||||
|
|
||||||
This handles the following paths:
|
This handles the following paths: /api/services/<domain>/<service>
|
||||||
/api/services/<domain>/<service>
|
|
||||||
"""
|
"""
|
||||||
domain = path_match.group('domain')
|
domain = path_match.group('domain')
|
||||||
service = path_match.group('service')
|
service = path_match.group('service')
|
||||||
@ -298,7 +295,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."""
|
"""Handle adding an event forwarding target."""
|
||||||
try:
|
try:
|
||||||
host = data['host']
|
host = data['host']
|
||||||
api_password = data['api_password']
|
api_password = data['api_password']
|
||||||
@ -331,7 +328,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."""
|
"""Handle deleting an event forwarding target."""
|
||||||
try:
|
try:
|
||||||
host = data['host']
|
host = data['host']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -354,12 +351,12 @@ 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."""
|
"""Return 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."""
|
"""Return 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)
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
components.arduino
|
Support for Arduino boards running with the Firmata firmware.
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
Arduino component that connects to a directly attached Arduino board which
|
|
||||||
runs with the Firmata firmware.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/arduino/
|
https://home-assistant.io/components/arduino/
|
||||||
@ -21,7 +18,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Setup the Arduino component."""
|
"""Setup the Arduino component."""
|
||||||
|
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: ['port']},
|
{DOMAIN: ['port']},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -53,15 +49,16 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class ArduinoBoard(object):
|
class ArduinoBoard(object):
|
||||||
""" Represents an Arduino board. """
|
"""Representation of an Arduino board."""
|
||||||
|
|
||||||
def __init__(self, port):
|
def __init__(self, port):
|
||||||
|
"""Initialize the board."""
|
||||||
from PyMata.pymata import PyMata
|
from PyMata.pymata import PyMata
|
||||||
self._port = port
|
self._port = port
|
||||||
self._board = PyMata(self._port, verbose=False)
|
self._board = PyMata(self._port, verbose=False)
|
||||||
|
|
||||||
def set_mode(self, pin, direction, mode):
|
def set_mode(self, pin, direction, mode):
|
||||||
""" Sets the mode and the direction of a given pin. """
|
"""Set the mode and the direction of a given pin."""
|
||||||
if mode == 'analog' and direction == 'in':
|
if mode == 'analog' and direction == 'in':
|
||||||
self._board.set_pin_mode(pin,
|
self._board.set_pin_mode(pin,
|
||||||
self._board.INPUT,
|
self._board.INPUT,
|
||||||
@ -89,19 +86,19 @@ class ArduinoBoard(object):
|
|||||||
return self._board.get_analog_response_table()
|
return self._board.get_analog_response_table()
|
||||||
|
|
||||||
def set_digital_out_high(self, pin):
|
def set_digital_out_high(self, pin):
|
||||||
""" Sets a given digital pin to high. """
|
"""Set a given digital pin to high."""
|
||||||
self._board.digital_write(pin, 1)
|
self._board.digital_write(pin, 1)
|
||||||
|
|
||||||
def set_digital_out_low(self, pin):
|
def set_digital_out_low(self, pin):
|
||||||
""" Sets a given digital pin to low. """
|
"""Set a given digital pin to low."""
|
||||||
self._board.digital_write(pin, 0)
|
self._board.digital_write(pin, 0)
|
||||||
|
|
||||||
def get_digital_in(self, pin):
|
def get_digital_in(self, pin):
|
||||||
""" Gets the value from a given digital pin. """
|
"""Get the value from a given digital pin."""
|
||||||
self._board.digital_read(pin)
|
self._board.digital_read(pin)
|
||||||
|
|
||||||
def get_analog_in(self, pin):
|
def get_analog_in(self, pin):
|
||||||
""" Gets the value from a given analog pin. """
|
"""Get the value from a given analog pin."""
|
||||||
self._board.analog_read(pin)
|
self._board.analog_read(pin)
|
||||||
|
|
||||||
def get_firmata(self):
|
def get_firmata(self):
|
||||||
@ -109,6 +106,6 @@ class ArduinoBoard(object):
|
|||||||
return self._board.get_firmata_version()
|
return self._board.get_firmata_version()
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
""" Disconnects the board and closes the serial connection. """
|
"""Disconnect the board and close the serial connection."""
|
||||||
self._board.reset()
|
self._board.reset()
|
||||||
self._board.close()
|
self._board.close()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation
|
Allow to setup simple automation rules via the config file.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Allows to setup simple automation rules via the config file.
|
|
||||||
|
|
||||||
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/automation/
|
https://home-assistant.io/components/automation/
|
||||||
@ -12,13 +10,14 @@ from homeassistant.bootstrap import prepare_setup_platform
|
|||||||
from homeassistant.const import CONF_PLATFORM
|
from homeassistant.const import CONF_PLATFORM
|
||||||
from homeassistant.components import logbook
|
from homeassistant.components import logbook
|
||||||
from homeassistant.helpers.service import call_from_config
|
from homeassistant.helpers.service import call_from_config
|
||||||
|
from homeassistant.helpers.service import validate_service_call
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = 'automation'
|
DOMAIN = 'automation'
|
||||||
|
|
||||||
DEPENDENCIES = ['group']
|
DEPENDENCIES = ['group']
|
||||||
|
|
||||||
CONF_ALIAS = 'alias'
|
CONF_ALIAS = 'alias'
|
||||||
CONF_SERVICE = 'service'
|
|
||||||
|
|
||||||
CONF_CONDITION = 'condition'
|
CONF_CONDITION = 'condition'
|
||||||
CONF_ACTION = 'action'
|
CONF_ACTION = 'action'
|
||||||
@ -35,25 +34,25 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up automation. """
|
"""Setup the automation."""
|
||||||
config_key = DOMAIN
|
config_key = DOMAIN
|
||||||
found = 1
|
found = 1
|
||||||
|
|
||||||
while config_key in config:
|
while config_key in config:
|
||||||
# check for one block syntax
|
# Check for one block syntax
|
||||||
if isinstance(config[config_key], dict):
|
if isinstance(config[config_key], dict):
|
||||||
config_block = _migrate_old_config(config[config_key])
|
config_block = _migrate_old_config(config[config_key])
|
||||||
name = config_block.get(CONF_ALIAS, config_key)
|
name = config_block.get(CONF_ALIAS, config_key)
|
||||||
_setup_automation(hass, config_block, name, config)
|
_setup_automation(hass, config_block, name, config)
|
||||||
|
|
||||||
# check for multiple block syntax
|
# Check for multiple block syntax
|
||||||
elif isinstance(config[config_key], list):
|
elif isinstance(config[config_key], list):
|
||||||
for list_no, config_block in enumerate(config[config_key]):
|
for list_no, config_block in enumerate(config[config_key]):
|
||||||
name = config_block.get(CONF_ALIAS,
|
name = config_block.get(CONF_ALIAS,
|
||||||
"{}, {}".format(config_key, list_no))
|
"{}, {}".format(config_key, list_no))
|
||||||
_setup_automation(hass, config_block, name, config)
|
_setup_automation(hass, config_block, name, config)
|
||||||
|
|
||||||
# any scalar value is incorrect
|
# Any scalar value is incorrect
|
||||||
else:
|
else:
|
||||||
_LOGGER.error('Error in config in section %s.', config_key)
|
_LOGGER.error('Error in config in section %s.', config_key)
|
||||||
|
|
||||||
@ -64,8 +63,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _setup_automation(hass, config_block, name, config):
|
def _setup_automation(hass, config_block, name, config):
|
||||||
""" Setup one instance of automation """
|
"""Setup one instance of automation."""
|
||||||
|
|
||||||
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name)
|
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name)
|
||||||
|
|
||||||
if action is None:
|
if action is None:
|
||||||
@ -83,10 +81,10 @@ def _setup_automation(hass, config_block, name, config):
|
|||||||
|
|
||||||
|
|
||||||
def _get_action(hass, config, name):
|
def _get_action(hass, config, name):
|
||||||
""" Return an action based on a config. """
|
"""Return an action based on a configuration."""
|
||||||
|
validation_error = validate_service_call(config)
|
||||||
if CONF_SERVICE not in config:
|
if validation_error:
|
||||||
_LOGGER.error('Error setting up %s, no action specified.', name)
|
_LOGGER.error(validation_error)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def action():
|
def action():
|
||||||
@ -100,7 +98,7 @@ def _get_action(hass, config, name):
|
|||||||
|
|
||||||
|
|
||||||
def _migrate_old_config(config):
|
def _migrate_old_config(config):
|
||||||
""" Migrate old config to new. """
|
"""Migrate old configuration to new."""
|
||||||
if CONF_PLATFORM not in config:
|
if CONF_PLATFORM not in config:
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@ -134,8 +132,7 @@ def _migrate_old_config(config):
|
|||||||
|
|
||||||
|
|
||||||
def _process_if(hass, config, p_config, action):
|
def _process_if(hass, config, p_config, action):
|
||||||
""" Processes if checks. """
|
"""Process if checks."""
|
||||||
|
|
||||||
cond_type = p_config.get(CONF_CONDITION_TYPE,
|
cond_type = p_config.get(CONF_CONDITION_TYPE,
|
||||||
DEFAULT_CONDITION_TYPE).lower()
|
DEFAULT_CONDITION_TYPE).lower()
|
||||||
|
|
||||||
@ -178,7 +175,7 @@ def _process_if(hass, config, p_config, action):
|
|||||||
|
|
||||||
|
|
||||||
def _process_trigger(hass, config, trigger_configs, name, action):
|
def _process_trigger(hass, config, trigger_configs, name, action):
|
||||||
""" Setup triggers. """
|
"""Setup the triggers."""
|
||||||
if isinstance(trigger_configs, dict):
|
if isinstance(trigger_configs, dict):
|
||||||
trigger_configs = [trigger_configs]
|
trigger_configs = [trigger_configs]
|
||||||
|
|
||||||
@ -195,7 +192,7 @@ def _process_trigger(hass, config, trigger_configs, name, action):
|
|||||||
|
|
||||||
|
|
||||||
def _resolve_platform(method, hass, config, platform):
|
def _resolve_platform(method, hass, config, platform):
|
||||||
""" Find automation platform. """
|
"""Find the automation platform."""
|
||||||
if platform is None:
|
if platform is None:
|
||||||
return None
|
return None
|
||||||
platform = prepare_setup_platform(hass, config, DOMAIN, platform)
|
platform = prepare_setup_platform(hass, config, DOMAIN, platform)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.event
|
Offer event listening automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers event 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/#event-trigger
|
at https://home-assistant.io/components/automation/#event-trigger
|
||||||
@ -15,7 +13,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for events based on config. """
|
"""Listen for events based on configuration."""
|
||||||
event_type = config.get(CONF_EVENT_TYPE)
|
event_type = config.get(CONF_EVENT_TYPE)
|
||||||
|
|
||||||
if event_type is None:
|
if event_type is None:
|
||||||
@ -25,7 +23,7 @@ def trigger(hass, config, action):
|
|||||||
event_data = config.get(CONF_EVENT_DATA)
|
event_data = config.get(CONF_EVENT_DATA)
|
||||||
|
|
||||||
def handle_event(event):
|
def handle_event(event):
|
||||||
""" Listens for events and calls the action when data matches. """
|
"""Listen for events and calls the action when data matches."""
|
||||||
if not event_data or all(val == event.data.get(key) for key, val
|
if not event_data or all(val == event.data.get(key) for key, val
|
||||||
in event_data.items()):
|
in event_data.items()):
|
||||||
action()
|
action()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.mqtt
|
Offer MQTT listening automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers MQTT 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/#mqtt-trigger
|
at https://home-assistant.io/components/automation/#mqtt-trigger
|
||||||
@ -17,7 +15,7 @@ CONF_PAYLOAD = 'payload'
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
topic = config.get(CONF_TOPIC)
|
topic = config.get(CONF_TOPIC)
|
||||||
payload = config.get(CONF_PAYLOAD)
|
payload = config.get(CONF_PAYLOAD)
|
||||||
|
|
||||||
@ -27,7 +25,7 @@ def trigger(hass, config, action):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def mqtt_automation_listener(msg_topic, msg_payload, qos):
|
def mqtt_automation_listener(msg_topic, msg_payload, qos):
|
||||||
""" Listens for MQTT messages. """
|
"""Listen for MQTT messages."""
|
||||||
if payload is None or payload == msg_payload:
|
if payload is None or payload == msg_payload:
|
||||||
action()
|
action()
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.numeric_state
|
Offer numeric state listening automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
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
|
||||||
@ -21,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def _renderer(hass, value_template, state):
|
def _renderer(hass, value_template, state):
|
||||||
"""Render state value."""
|
"""Render the state value."""
|
||||||
if value_template is None:
|
if value_template is None:
|
||||||
return state.state
|
return state.state
|
||||||
|
|
||||||
@ -29,7 +27,7 @@ def _renderer(hass, value_template, state):
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
|
|
||||||
if entity_id is None:
|
if entity_id is None:
|
||||||
@ -50,8 +48,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
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. """
|
"""Listen for state changes and calls action."""
|
||||||
|
|
||||||
# Fire action if we go from outside range into range
|
# Fire action if we go from outside range into range
|
||||||
if _in_range(above, below, renderer(to_s)) and \
|
if _in_range(above, below, renderer(to_s)) and \
|
||||||
(from_s is None or not _in_range(above, below, renderer(from_s))):
|
(from_s is None or not _in_range(above, below, renderer(from_s))):
|
||||||
@ -64,8 +61,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with state based condition. """
|
"""Wrap action method with state based condition."""
|
||||||
|
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
|
|
||||||
if entity_id is None:
|
if entity_id is None:
|
||||||
@ -93,7 +89,7 @@ def if_action(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _in_range(range_start, range_end, value):
|
def _in_range(range_start, range_end, value):
|
||||||
""" Checks if value is inside the range """
|
"""Check if value is inside the range."""
|
||||||
try:
|
try:
|
||||||
value = float(value)
|
value = float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.state
|
Offer state listening automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers 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/#state-trigger
|
at https://home-assistant.io/components/automation/#state-trigger
|
||||||
@ -25,7 +23,7 @@ CONF_FOR = "for"
|
|||||||
|
|
||||||
|
|
||||||
def get_time_config(config):
|
def get_time_config(config):
|
||||||
""" Helper function to extract the time specified in the config """
|
"""Helper function to extract the time specified in the configuration."""
|
||||||
if CONF_FOR not in config:
|
if CONF_FOR not in config:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -51,7 +49,7 @@ def get_time_config(config):
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
|
|
||||||
if entity_id is None:
|
if entity_id is None:
|
||||||
@ -72,17 +70,15 @@ def trigger(hass, config, action):
|
|||||||
return 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. """
|
"""Listen for state changes and calls action."""
|
||||||
|
|
||||||
def state_for_listener(now):
|
def state_for_listener(now):
|
||||||
""" Fires on state changes after a delay and calls action. """
|
"""Fire on state changes after a delay and calls action."""
|
||||||
hass.bus.remove_listener(
|
hass.bus.remove_listener(
|
||||||
EVENT_STATE_CHANGED, for_state_listener)
|
EVENT_STATE_CHANGED, for_state_listener)
|
||||||
action()
|
action()
|
||||||
|
|
||||||
def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
|
def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
|
||||||
""" Fires on state changes and cancels
|
"""Fire on changes and cancel for listener if changed."""
|
||||||
for listener if state changed. """
|
|
||||||
if inner_to_s == to_s:
|
if inner_to_s == to_s:
|
||||||
return
|
return
|
||||||
hass.bus.remove_listener(EVENT_TIME_CHANGED, for_time_listener)
|
hass.bus.remove_listener(EVENT_TIME_CHANGED, for_time_listener)
|
||||||
@ -106,7 +102,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with state based condition. """
|
"""Wrap action method with state based condition."""
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
state = config.get(CONF_STATE)
|
state = config.get(CONF_STATE)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.sun
|
Offer sun based automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers sun based 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/#sun-trigger
|
at https://home-assistant.io/components/automation/#sun-trigger
|
||||||
@ -29,7 +27,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for events based on config. """
|
"""Listen for events based on configuration."""
|
||||||
event = config.get(CONF_EVENT)
|
event = config.get(CONF_EVENT)
|
||||||
|
|
||||||
if event is None:
|
if event is None:
|
||||||
@ -55,7 +53,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with sun based condition. """
|
"""Wrap action method with sun based condition."""
|
||||||
before = config.get(CONF_BEFORE)
|
before = config.get(CONF_BEFORE)
|
||||||
after = config.get(CONF_AFTER)
|
after = config.get(CONF_AFTER)
|
||||||
|
|
||||||
@ -106,8 +104,7 @@ def if_action(hass, config):
|
|||||||
return sun.next_setting(hass) + after_offset
|
return sun.next_setting(hass) + after_offset
|
||||||
|
|
||||||
def time_if():
|
def time_if():
|
||||||
""" Validate time based if-condition """
|
"""Validate time based if-condition."""
|
||||||
|
|
||||||
now = dt_util.now()
|
now = dt_util.now()
|
||||||
before = before_func()
|
before = before_func()
|
||||||
after = after_func()
|
after = after_func()
|
||||||
@ -126,6 +123,7 @@ def if_action(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _parse_offset(raw_offset):
|
def _parse_offset(raw_offset):
|
||||||
|
"""Parse the offset."""
|
||||||
if raw_offset is None:
|
if raw_offset is None:
|
||||||
return timedelta(0)
|
return timedelta(0)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.template
|
Offer template automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers template 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/#template-trigger
|
at https://home-assistant.io/components/automation/#template-trigger
|
||||||
@ -16,7 +14,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||||
|
|
||||||
if value_template is None:
|
if value_template is None:
|
||||||
@ -27,7 +25,7 @@ def trigger(hass, config, action):
|
|||||||
already_triggered = False
|
already_triggered = False
|
||||||
|
|
||||||
def event_listener(event):
|
def event_listener(event):
|
||||||
""" Listens for state changes and calls action. """
|
"""Listen for state changes and calls action."""
|
||||||
nonlocal already_triggered
|
nonlocal already_triggered
|
||||||
template_result = _check_template(hass, value_template)
|
template_result = _check_template(hass, value_template)
|
||||||
|
|
||||||
@ -43,8 +41,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with state based condition. """
|
"""Wrap action method with state based condition."""
|
||||||
|
|
||||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||||
|
|
||||||
if value_template is None:
|
if value_template is None:
|
||||||
@ -55,7 +52,7 @@ def if_action(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _check_template(hass, value_template):
|
def _check_template(hass, value_template):
|
||||||
""" Checks if result of template is true """
|
"""Check if result of template is true."""
|
||||||
try:
|
try:
|
||||||
value = template.render(hass, value_template, {})
|
value = template.render(hass, value_template, {})
|
||||||
except TemplateError:
|
except TemplateError:
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.time
|
Offer time listening automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers time 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/#time-trigger
|
at https://home-assistant.io/components/automation/#time-trigger
|
||||||
@ -24,7 +22,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
if CONF_AFTER in config:
|
if CONF_AFTER in config:
|
||||||
after = dt_util.parse_time_str(config[CONF_AFTER])
|
after = dt_util.parse_time_str(config[CONF_AFTER])
|
||||||
if after is None:
|
if after is None:
|
||||||
@ -42,7 +40,7 @@ def trigger(hass, config, action):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def time_automation_listener(now):
|
def time_automation_listener(now):
|
||||||
""" Listens for time changes and calls action. """
|
"""Listen for time changes and calls action."""
|
||||||
action()
|
action()
|
||||||
|
|
||||||
track_time_change(hass, time_automation_listener,
|
track_time_change(hass, time_automation_listener,
|
||||||
@ -52,7 +50,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with time based condition. """
|
"""Wrap action method with time based condition."""
|
||||||
before = config.get(CONF_BEFORE)
|
before = config.get(CONF_BEFORE)
|
||||||
after = config.get(CONF_AFTER)
|
after = config.get(CONF_AFTER)
|
||||||
weekday = config.get(CONF_WEEKDAY)
|
weekday = config.get(CONF_WEEKDAY)
|
||||||
@ -76,7 +74,7 @@ def if_action(hass, config):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def time_if():
|
def time_if():
|
||||||
""" Validate time based if-condition """
|
"""Validate time based if-condition."""
|
||||||
now = dt_util.now()
|
now = dt_util.now()
|
||||||
if before is not None and now > now.replace(hour=before.hour,
|
if before is not None and now > now.replace(hour=before.hour,
|
||||||
minute=before.minute):
|
minute=before.minute):
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.zone
|
Offer zone automation rules.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Offers zone 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/#zone-trigger
|
at https://home-assistant.io/components/automation/#zone-trigger
|
||||||
@ -22,7 +20,7 @@ DEFAULT_EVENT = EVENT_ENTER
|
|||||||
|
|
||||||
|
|
||||||
def trigger(hass, config, action):
|
def trigger(hass, config, action):
|
||||||
""" Listen for state changes based on `config`. """
|
"""Listen for state changes based on configuration."""
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
zone_entity_id = config.get(CONF_ZONE)
|
zone_entity_id = config.get(CONF_ZONE)
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ def trigger(hass, config, action):
|
|||||||
event = config.get(CONF_EVENT, DEFAULT_EVENT)
|
event = config.get(CONF_EVENT, DEFAULT_EVENT)
|
||||||
|
|
||||||
def zone_automation_listener(entity, from_s, to_s):
|
def zone_automation_listener(entity, from_s, to_s):
|
||||||
""" Listens for state changes and calls action. """
|
"""Listen for state changes and calls action."""
|
||||||
if from_s and None in (from_s.attributes.get(ATTR_LATITUDE),
|
if from_s and None in (from_s.attributes.get(ATTR_LATITUDE),
|
||||||
from_s.attributes.get(ATTR_LONGITUDE)) or \
|
from_s.attributes.get(ATTR_LONGITUDE)) or \
|
||||||
None in (to_s.attributes.get(ATTR_LATITUDE),
|
None in (to_s.attributes.get(ATTR_LATITUDE),
|
||||||
@ -57,7 +55,7 @@ def trigger(hass, config, action):
|
|||||||
|
|
||||||
|
|
||||||
def if_action(hass, config):
|
def if_action(hass, config):
|
||||||
""" Wraps action method with zone based condition. """
|
"""Wrap action method with zone based condition."""
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_id = config.get(CONF_ENTITY_ID)
|
||||||
zone_entity_id = config.get(CONF_ZONE)
|
zone_entity_id = config.get(CONF_ZONE)
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Component to interface with binary sensors (sensors which only know two states)
|
Component to interface with binary sensors.
|
||||||
that can be monitored.
|
|
||||||
|
|
||||||
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/binary_sensor/
|
https://home-assistant.io/components/binary_sensor/
|
||||||
@ -10,7 +9,7 @@ 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)
|
from homeassistant.components import (bloomsky, mysensors, zwave, wemo, wink)
|
||||||
|
|
||||||
DOMAIN = 'binary_sensor'
|
DOMAIN = 'binary_sensor'
|
||||||
SCAN_INTERVAL = 30
|
SCAN_INTERVAL = 30
|
||||||
@ -38,6 +37,7 @@ DISCOVERY_PLATFORMS = {
|
|||||||
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
|
bloomsky.DISCOVER_BINARY_SENSORS: 'bloomsky',
|
||||||
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
|
mysensors.DISCOVER_BINARY_SENSORS: 'mysensors',
|
||||||
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
|
zwave.DISCOVER_BINARY_SENSORS: 'zwave',
|
||||||
|
wemo.DISCOVER_BINARY_SENSORS: 'wemo',
|
||||||
wink.DISCOVER_BINARY_SENSORS: 'wink'
|
wink.DISCOVER_BINARY_SENSORS: 'wink'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Provides a binary sensor to track online status of a UPS.
|
Support for tracking the 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/
|
||||||
@ -17,8 +17,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class OnlineStatus(BinarySensorDevice):
|
class OnlineStatus(BinarySensorDevice):
|
||||||
"""Binary sensor to represent UPS online status."""
|
"""Represent UPS online status."""
|
||||||
|
|
||||||
def __init__(self, config, data):
|
def __init__(self, config, data):
|
||||||
|
"""Initialize the APCUPSd device."""
|
||||||
self._config = config
|
self._config = config
|
||||||
self._data = data
|
self._data = data
|
||||||
self._state = None
|
self._state = None
|
||||||
@ -26,17 +28,14 @@ class OnlineStatus(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the UPS online status sensor. """
|
"""Return the name of the UPS online status sensor."""
|
||||||
return self._config.get("name", DEFAULT_NAME)
|
return self._config.get("name", DEFAULT_NAME)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""True if the UPS is online, else False."""
|
"""Return 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):
|
||||||
"""
|
"""Get the status report from APCUPSd and set this entity's state."""
|
||||||
Get the status report from APCUPSd (or cache) and set this entity's
|
|
||||||
state.
|
|
||||||
"""
|
|
||||||
self._state = self._data.status[apcupsd.KEY_STATUS]
|
self._state = self._data.status[apcupsd.KEY_STATUS]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
The arest sensor will consume an exposed aREST API of a device.
|
Support for exposed aREST RESTful 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/
|
||||||
@ -22,7 +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."""
|
"""Setup the aREST binary sensor."""
|
||||||
resource = config.get(CONF_RESOURCE)
|
resource = config.get(CONF_RESOURCE)
|
||||||
pin = config.get(CONF_PIN)
|
pin = config.get(CONF_PIN)
|
||||||
|
|
||||||
@ -53,9 +53,10 @@ 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."""
|
"""Implement an aREST binary sensor for a pin."""
|
||||||
|
|
||||||
def __init__(self, arest, resource, name, pin):
|
def __init__(self, arest, resource, name, pin):
|
||||||
|
"""Initialize the aREST device."""
|
||||||
self.arest = arest
|
self.arest = arest
|
||||||
self._resource = resource
|
self._resource = resource
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -70,30 +71,32 @@ class ArestBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""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 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."""
|
"""Get 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):
|
||||||
|
"""Initialize the aREST data object."""
|
||||||
self._resource = resource
|
self._resource = resource
|
||||||
self._pin = pin
|
self._pin = pin
|
||||||
self.data = {}
|
self.data = {}
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Gets the latest data from aREST device."""
|
"""Get 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)
|
||||||
|
@ -35,7 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class BloomSkySensor(BinarySensorDevice):
|
class BloomSkySensor(BinarySensorDevice):
|
||||||
""" Represents a single binary sensor in a BloomSky device. """
|
"""Represent a single binary sensor in a BloomSky device."""
|
||||||
|
|
||||||
def __init__(self, bs, device, sensor_name):
|
def __init__(self, bs, device, sensor_name):
|
||||||
"""Initialize a BloomSky binary sensor."""
|
"""Initialize a BloomSky binary sensor."""
|
||||||
@ -53,7 +53,7 @@ class BloomSkySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Unique ID for this sensor."""
|
"""Return the unique ID for this sensor."""
|
||||||
return self._unique_id
|
return self._unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -63,7 +63,7 @@ class BloomSkySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""If binary sensor is on."""
|
"""Return true if binary sensor is on."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Allows to configure custom shell commands to turn a value into a logical value
|
Support for custom shell commands to to retrieve values.
|
||||||
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/
|
||||||
@ -25,7 +24,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."""
|
"""Setup 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
|
||||||
@ -44,11 +43,11 @@ 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):
|
||||||
"""
|
"""Represent a command line binary sensor."""
|
||||||
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):
|
||||||
|
"""Initialize the Command line binary sensor."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self.data = data
|
self.data = data
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -60,16 +59,16 @@ class CommandBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""The name of the sensor."""
|
"""Return 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."""
|
"""Return 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."""
|
"""Get the latest data and updates the state."""
|
||||||
self.data.update()
|
self.data.update()
|
||||||
value = self.data.value
|
value = self.data.value
|
||||||
|
|
||||||
|
@ -17,7 +17,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
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, sensor_class):
|
||||||
|
"""Initialize the demo sensor."""
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = state
|
self._state = state
|
||||||
self._sensor_type = sensor_class
|
self._sensor_type = sensor_class
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Allows to configure a MQTT binary sensor.
|
Support for MQTT 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
|
||||||
https://home-assistant.io/components/binary_sensor.mqtt/
|
https://home-assistant.io/components/binary_sensor.mqtt/
|
||||||
@ -7,7 +7,8 @@ https://home-assistant.io/components/binary_sensor.mqtt/
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.components.mqtt as mqtt
|
import homeassistant.components.mqtt as mqtt
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import (BinarySensorDevice,
|
||||||
|
SENSOR_CLASSES)
|
||||||
from homeassistant.const import CONF_VALUE_TEMPLATE
|
from homeassistant.const import CONF_VALUE_TEMPLATE
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
|
|
||||||
@ -24,15 +25,20 @@ 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')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
sensor_class = config.get('sensor_class')
|
||||||
|
if sensor_class not in SENSOR_CLASSES:
|
||||||
|
_LOGGER.warning('Unknown sensor class: %s', sensor_class)
|
||||||
|
sensor_class = None
|
||||||
|
|
||||||
add_devices([MqttBinarySensor(
|
add_devices([MqttBinarySensor(
|
||||||
hass,
|
hass,
|
||||||
config.get('name', DEFAULT_NAME),
|
config.get('name', DEFAULT_NAME),
|
||||||
config.get('state_topic', None),
|
config.get('state_topic', None),
|
||||||
|
sensor_class,
|
||||||
config.get('qos', DEFAULT_QOS),
|
config.get('qos', DEFAULT_QOS),
|
||||||
config.get('payload_on', DEFAULT_PAYLOAD_ON),
|
config.get('payload_on', DEFAULT_PAYLOAD_ON),
|
||||||
config.get('payload_off', DEFAULT_PAYLOAD_OFF),
|
config.get('payload_off', DEFAULT_PAYLOAD_OFF),
|
||||||
@ -41,13 +47,16 @@ 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."""
|
"""Representation a binary sensor that is updated by MQTT."""
|
||||||
def __init__(self, hass, name, state_topic, qos, payload_on, payload_off,
|
|
||||||
value_template):
|
def __init__(self, hass, name, state_topic, sensor_class, qos, payload_on,
|
||||||
|
payload_off, value_template):
|
||||||
|
"""Initialize the MQTT binary sensor."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = False
|
self._state = False
|
||||||
self._state_topic = state_topic
|
self._state_topic = state_topic
|
||||||
|
self._sensor_class = sensor_class
|
||||||
self._payload_on = payload_on
|
self._payload_on = payload_on
|
||||||
self._payload_off = payload_off
|
self._payload_off = payload_off
|
||||||
self._qos = qos
|
self._qos = qos
|
||||||
@ -73,10 +82,15 @@ class MqttBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensor_class(self):
|
||||||
|
"""Return the class of this sensor."""
|
||||||
|
return self._sensor_class
|
||||||
|
@ -90,7 +90,7 @@ class MySensorsBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
"""MySensor gateway pushes its state to HA."""
|
"""Mysensor gateway pushes its state to HA."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -26,7 +26,6 @@ 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:
|
||||||
for structure in nest.NEST.structures:
|
for structure in nest.NEST.structures:
|
||||||
|
@ -66,6 +66,7 @@ class NX584ZoneSensor(BinarySensorDevice):
|
|||||||
"""Represents a NX584 zone as a sensor."""
|
"""Represents a NX584 zone as a sensor."""
|
||||||
|
|
||||||
def __init__(self, zone, zone_type):
|
def __init__(self, zone, zone_type):
|
||||||
|
"""Initialize the nx594 binary sensor."""
|
||||||
self._zone = zone
|
self._zone = zone
|
||||||
self._zone_type = zone_type
|
self._zone_type = zone_type
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ class NX584ZoneSensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Name of the binary sensor."""
|
"""Return the name of the binary sensor."""
|
||||||
return self._zone['name']
|
return self._zone['name']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -95,6 +96,7 @@ class NX584Watcher(threading.Thread):
|
|||||||
"""Event listener thread to process NX584 events."""
|
"""Event listener thread to process NX584 events."""
|
||||||
|
|
||||||
def __init__(self, client, zone_sensors):
|
def __init__(self, client, zone_sensors):
|
||||||
|
"""Initialize nx584 watcher thread."""
|
||||||
super(NX584Watcher, self).__init__()
|
super(NX584Watcher, self).__init__()
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self._client = client
|
self._client = client
|
||||||
@ -115,7 +117,7 @@ class NX584Watcher(threading.Thread):
|
|||||||
self._process_zone_event(event)
|
self._process_zone_event(event)
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
# Throw away any existing events so we don't replay history
|
"""Throw away any existing events so we don't replay history."""
|
||||||
self._client.get_events()
|
self._client.get_events()
|
||||||
while True:
|
while True:
|
||||||
events = self._client.get_events()
|
events = self._client.get_events()
|
||||||
@ -123,6 +125,7 @@ class NX584Watcher(threading.Thread):
|
|||||||
self._process_events(events)
|
self._process_events(events)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""Run the watcher."""
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self._run()
|
self._run()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
The rest binary sensor will consume responses sent by an exposed REST API.
|
Support for RESTful 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
|
||||||
https://home-assistant.io/components/binary_sensor.rest/
|
https://home-assistant.io/components/binary_sensor.rest/
|
||||||
@ -52,7 +52,7 @@ class RestBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Name of the binary sensor."""
|
"""Return the name of the binary sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Allows to configure a binary sensor using RPi GPIO.
|
Support for 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/
|
||||||
@ -20,8 +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."""
|
"""Setup 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)
|
||||||
invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC)
|
invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC)
|
||||||
@ -36,10 +35,11 @@ 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."""
|
"""Represent a binary sensor that uses Raspberry Pi GPIO."""
|
||||||
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
|
|
||||||
# pylint: disable=no-member
|
|
||||||
|
|
||||||
|
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
|
||||||
|
"""Initialize the RPi binary sensor."""
|
||||||
|
# pylint: disable=no-member
|
||||||
self._name = name or DEVICE_DEFAULT_NAME
|
self._name = name or DEVICE_DEFAULT_NAME
|
||||||
self._port = port
|
self._port = port
|
||||||
self._pull_mode = pull_mode
|
self._pull_mode = pull_mode
|
||||||
@ -50,9 +50,10 @@ 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."""
|
"""Read 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
|
||||||
@ -62,10 +63,10 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""The name of the sensor."""
|
"""Return 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."""
|
"""Return the state of the entity."""
|
||||||
return self._state != self._invert_logic
|
return self._state != self._invert_logic
|
||||||
|
@ -23,6 +23,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
|
|
||||||
class BinarySensor(BinarySensorDevice, Sensor):
|
class BinarySensor(BinarySensorDevice, Sensor):
|
||||||
"""A binary sensor which is on when its state == CONF_VALUE_ON."""
|
"""A binary sensor which is on when its state == CONF_VALUE_ON."""
|
||||||
|
|
||||||
required = (CONF_VALUE_ON,)
|
required = (CONF_VALUE_ON,)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.binary_sensor.template
|
Support for exposing a templated binary sensor.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for exposing a templated binary_sensor
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/binary_sensor.template/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -22,7 +23,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup template binary sensors."""
|
"""Setup template binary sensors."""
|
||||||
|
|
||||||
sensors = []
|
sensors = []
|
||||||
if config.get(CONF_SENSORS) is None:
|
if config.get(CONF_SENSORS) is None:
|
||||||
_LOGGER.error('Missing configuration data for binary_sensor platform')
|
_LOGGER.error('Missing configuration data for binary_sensor platform')
|
||||||
@ -70,11 +70,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class BinarySensorTemplate(BinarySensorDevice):
|
class BinarySensorTemplate(BinarySensorDevice):
|
||||||
"""A virtual binary_sensor that triggers from another sensor."""
|
"""A virtual binary sensor that triggers from another sensor."""
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def __init__(self, hass, device, friendly_name, sensor_class,
|
def __init__(self, hass, device, friendly_name, sensor_class,
|
||||||
value_template):
|
value_template):
|
||||||
|
"""Initialize the Template binary sensor."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._device = device
|
self._device = device
|
||||||
self._name = friendly_name
|
self._name = friendly_name
|
||||||
@ -90,25 +91,32 @@ class BinarySensorTemplate(BinarySensorDevice):
|
|||||||
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
|
hass.bus.listen(EVENT_STATE_CHANGED, self._event_listener)
|
||||||
|
|
||||||
def _event_listener(self, event):
|
def _event_listener(self, event):
|
||||||
|
if not hasattr(self, 'hass'):
|
||||||
|
return
|
||||||
self.update_ha_state(True)
|
self.update_ha_state(True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
|
"""No polling needed."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sensor_class(self):
|
def sensor_class(self):
|
||||||
|
"""Return the sensor class of the sensor."""
|
||||||
return self._sensor_class
|
return self._sensor_class
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""Return the name of the sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
|
"""Return true if sensor is on."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
"""Get the latest data and update the state."""
|
||||||
try:
|
try:
|
||||||
value = template.render(self._hass, self._template)
|
value = template.render(self._hass, self._template)
|
||||||
except TemplateError as ex:
|
except TemplateError as ex:
|
||||||
|
75
homeassistant/components/binary_sensor/wemo.py
Normal file
75
homeassistant/components/binary_sensor/wemo.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
Support for WeMo sensors.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/binary_sensor.wemo/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
|
DEPENDENCIES = ['wemo']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument, too-many-function-args
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
"""Register discovered WeMo binary sensors."""
|
||||||
|
import pywemo.discovery as discovery
|
||||||
|
|
||||||
|
if discovery_info is not None:
|
||||||
|
location = discovery_info[2]
|
||||||
|
mac = discovery_info[3]
|
||||||
|
device = discovery.device_from_description(location, mac)
|
||||||
|
|
||||||
|
if device:
|
||||||
|
add_devices_callback([WemoBinarySensor(device)])
|
||||||
|
|
||||||
|
|
||||||
|
class WemoBinarySensor(BinarySensorDevice):
|
||||||
|
"""Represents a WeMo binary sensor."""
|
||||||
|
|
||||||
|
def __init__(self, device):
|
||||||
|
"""Initialize the WeMo sensor."""
|
||||||
|
self.wemo = device
|
||||||
|
self._state = None
|
||||||
|
|
||||||
|
wemo = get_component('wemo')
|
||||||
|
wemo.SUBSCRIPTION_REGISTRY.register(self.wemo)
|
||||||
|
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback)
|
||||||
|
|
||||||
|
def _update_callback(self, _device, _params):
|
||||||
|
"""Called by the wemo device callback to update state."""
|
||||||
|
_LOGGER.info(
|
||||||
|
'Subscription update for %s',
|
||||||
|
_device)
|
||||||
|
self.update_ha_state(True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""No polling needed with subscriptions."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return the id of this WeMo device."""
|
||||||
|
return "{}.{}".format(self.__class__, self.wemo.serialnumber)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the sevice if any."""
|
||||||
|
return self.wemo.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""True if sensor is on."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update WeMo state."""
|
||||||
|
try:
|
||||||
|
self._state = self.wemo.get_state(True)
|
||||||
|
except AttributeError:
|
||||||
|
_LOGGER.warning('Could not update status for %s', self.name)
|
@ -22,7 +22,7 @@ SENSOR_TYPES = {
|
|||||||
|
|
||||||
|
|
||||||
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."""
|
"""Setup the Wink platform."""
|
||||||
import pywink
|
import pywink
|
||||||
|
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
@ -42,16 +42,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class WinkBinarySensorDevice(BinarySensorDevice, Entity):
|
class WinkBinarySensorDevice(BinarySensorDevice, Entity):
|
||||||
"""Represents a Wink sensor."""
|
"""Representation of a Wink sensor."""
|
||||||
|
|
||||||
def __init__(self, wink):
|
def __init__(self, wink):
|
||||||
|
"""Initialize the Wink binary sensor."""
|
||||||
self.wink = wink
|
self.wink = wink
|
||||||
self._unit_of_measurement = self.wink.UNIT
|
self._unit_of_measurement = self.wink.UNIT
|
||||||
self.capability = self.wink.capability()
|
self.capability = self.wink.capability()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return True if the binary sensor is on."""
|
"""Return true if the binary sensor is on."""
|
||||||
if self.capability == "loudness":
|
if self.capability == "loudness":
|
||||||
return self.wink.loudness_boolean()
|
return self.wink.loudness_boolean()
|
||||||
elif self.capability == "vibration":
|
elif self.capability == "vibration":
|
||||||
@ -68,12 +69,12 @@ class WinkBinarySensorDevice(BinarySensorDevice, Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
""" Returns the id of this wink sensor """
|
"""Return the ID of this wink sensor."""
|
||||||
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 sensor if any. """
|
"""Return the name of the sensor if any."""
|
||||||
return self.wink.name()
|
return self.wink.name()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
@ -19,8 +19,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class ZigBeeBinarySensor(ZigBeeDigitalIn, BinarySensorDevice):
|
class ZigBeeBinarySensor(ZigBeeDigitalIn, BinarySensorDevice):
|
||||||
"""
|
"""Use ZigBeeDigitalIn as binary sensor."""
|
||||||
Use multiple inheritance to turn a ZigBeeDigitalIn into a
|
|
||||||
BinarySensorDevice.
|
|
||||||
"""
|
|
||||||
pass
|
pass
|
||||||
|
@ -23,17 +23,19 @@ DEPENDENCIES = []
|
|||||||
PHILIO = 0x013c
|
PHILIO = 0x013c
|
||||||
PHILIO_SLIM_SENSOR = 0x0002
|
PHILIO_SLIM_SENSOR = 0x0002
|
||||||
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
|
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
|
||||||
|
WENZHOU = 0x0118
|
||||||
|
WENZHOU_SLIM_SENSOR_MOTION = (WENZHOU, PHILIO_SLIM_SENSOR, 0)
|
||||||
|
|
||||||
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
|
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
|
||||||
|
|
||||||
DEVICE_MAPPINGS = {
|
DEVICE_MAPPINGS = {
|
||||||
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
|
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
|
||||||
|
WENZHOU_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup the Z-Wave platform for sensors."""
|
"""Setup the Z-Wave platform for sensors."""
|
||||||
|
|
||||||
if discovery_info is None or NETWORK is None:
|
if discovery_info is None or NETWORK is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -63,9 +65,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
|
class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
|
||||||
"""Represents a binary sensor within Z-Wave."""
|
"""Representation of a binary sensor within Z-Wave."""
|
||||||
|
|
||||||
def __init__(self, value, sensor_class):
|
def __init__(self, value, sensor_class):
|
||||||
|
"""Initialize the sensor."""
|
||||||
self._sensor_type = sensor_class
|
self._sensor_type = sensor_class
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
from openzwave.network import ZWaveNetwork
|
from openzwave.network import ZWaveNetwork
|
||||||
@ -98,12 +101,10 @@ class ZWaveBinarySensor(BinarySensorDevice, ZWaveDeviceEntity):
|
|||||||
|
|
||||||
|
|
||||||
class ZWaveTriggerSensor(ZWaveBinarySensor):
|
class ZWaveTriggerSensor(ZWaveBinarySensor):
|
||||||
"""
|
"""Representation of a stateless sensor within Z-Wave."""
|
||||||
Represents a stateless sensor which triggers events just 'On'
|
|
||||||
within Z-Wave.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60):
|
def __init__(self, sensor_value, sensor_class, hass, re_arm_sec=60):
|
||||||
|
"""Initialize the sensor."""
|
||||||
super(ZWaveTriggerSensor, self).__init__(sensor_value, sensor_class)
|
super(ZWaveTriggerSensor, self).__init__(sensor_value, sensor_class)
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self.re_arm_sec = re_arm_sec
|
self.re_arm_sec = re_arm_sec
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.bloomsky
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for BloomSky weather station.
|
Support for BloomSky weather station.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
@ -60,10 +58,10 @@ class BloomSky(object):
|
|||||||
"""Handle all communication with the BloomSky API."""
|
"""Handle all communication with the BloomSky API."""
|
||||||
|
|
||||||
# API documentation at http://weatherlution.com/bloomsky-api/
|
# API documentation at http://weatherlution.com/bloomsky-api/
|
||||||
|
|
||||||
API_URL = "https://api.bloomsky.com/api/skydata"
|
API_URL = "https://api.bloomsky.com/api/skydata"
|
||||||
|
|
||||||
def __init__(self, api_key):
|
def __init__(self, api_key):
|
||||||
|
"""Initialize the BookSky."""
|
||||||
self._api_key = api_key
|
self._api_key = api_key
|
||||||
self.devices = {}
|
self.devices = {}
|
||||||
_LOGGER.debug("Initial bloomsky device load...")
|
_LOGGER.debug("Initial bloomsky device load...")
|
||||||
@ -71,10 +69,7 @@ class BloomSky(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def refresh_devices(self):
|
def refresh_devices(self):
|
||||||
"""
|
"""Use the API to retreive a list of devices."""
|
||||||
Uses the API to retreive a list of devices associated with an
|
|
||||||
account along with all the sensors on the device.
|
|
||||||
"""
|
|
||||||
_LOGGER.debug("Fetching bloomsky update")
|
_LOGGER.debug("Fetching bloomsky update")
|
||||||
response = requests.get(self.API_URL,
|
response = requests.get(self.API_URL,
|
||||||
headers={"Authorization": self._api_key},
|
headers={"Authorization": self._api_key},
|
||||||
@ -84,7 +79,7 @@ class BloomSky(object):
|
|||||||
elif response.status_code != 200:
|
elif response.status_code != 200:
|
||||||
_LOGGER.error("Invalid HTTP response: %s", response.status_code)
|
_LOGGER.error("Invalid HTTP response: %s", response.status_code)
|
||||||
return
|
return
|
||||||
# create dictionary keyed off of the device unique id
|
# Create dictionary keyed off of the device unique id
|
||||||
self.devices.update({
|
self.devices.update({
|
||||||
device["DeviceID"]: device for device in response.json()
|
device["DeviceID"]: device for device in response.json()
|
||||||
})
|
})
|
||||||
|
@ -10,9 +10,7 @@ SERVICE_BROWSE_URL = "browse_url"
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""
|
"""Listen for browse_url events."""
|
||||||
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,
|
||||||
|
@ -42,7 +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):
|
||||||
"""Initialize camera component."""
|
"""Setup the camera component."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
|
||||||
DISCOVERY_PLATFORMS)
|
DISCOVERY_PLATFORMS)
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.bloomsky
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for a camera of a BloomSky weather station.
|
Support for a camera of a BloomSky weather station.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
@ -18,17 +16,17 @@ 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 """
|
"""Setup access to BloomSky cameras."""
|
||||||
bloomsky = get_component('bloomsky')
|
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)])
|
||||||
|
|
||||||
|
|
||||||
class BloomSkyCamera(Camera):
|
class BloomSkyCamera(Camera):
|
||||||
""" Represents the images published from the BloomSky's camera. """
|
"""Representation of the images published from the BloomSky's camera."""
|
||||||
|
|
||||||
def __init__(self, bs, device):
|
def __init__(self, bs, device):
|
||||||
""" set up for access to the BloomSky camera images """
|
"""Setup for access to the BloomSky camera images."""
|
||||||
super(BloomSkyCamera, self).__init__()
|
super(BloomSkyCamera, self).__init__()
|
||||||
self._name = device["DeviceName"]
|
self._name = device["DeviceName"]
|
||||||
self._id = device["DeviceID"]
|
self._id = device["DeviceID"]
|
||||||
@ -37,7 +35,7 @@ class BloomSkyCamera(Camera):
|
|||||||
self._last_url = ""
|
self._last_url = ""
|
||||||
# _last_image will store images as they are downloaded so that the
|
# _last_image will store images as they are downloaded so that the
|
||||||
# frequent updates in home-assistant don't keep poking the server
|
# frequent updates in home-assistant don't keep poking the server
|
||||||
# to download the same image over and over
|
# to download the same image over and over.
|
||||||
self._last_image = ""
|
self._last_image = ""
|
||||||
self._logger = logging.getLogger(__name__)
|
self._logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ class BloomSkyCamera(Camera):
|
|||||||
try:
|
try:
|
||||||
self._url = self._bloomsky.devices[self._id]["Data"]["ImageURL"]
|
self._url = self._bloomsky.devices[self._id]["Data"]["ImageURL"]
|
||||||
self._bloomsky.refresh_devices()
|
self._bloomsky.refresh_devices()
|
||||||
# if the url hasn't changed then the image hasn't changed
|
# If the URL hasn't changed then the image hasn't changed.
|
||||||
if self._url != self._last_url:
|
if self._url != self._last_url:
|
||||||
response = requests.get(self._url, timeout=10)
|
response = requests.get(self._url, timeout=10)
|
||||||
self._last_url = self._url
|
self._last_url = self._url
|
||||||
@ -59,5 +57,5 @@ class BloomSkyCamera(Camera):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of this BloomSky device. """
|
"""Return the name of this BloomSky device."""
|
||||||
return self._name
|
return self._name
|
||||||
|
@ -21,6 +21,7 @@ class DemoCamera(Camera):
|
|||||||
"""A Demo camera."""
|
"""A Demo camera."""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
|
"""Initialize demo camera component."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.foscam
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
This component provides basic support for Foscam IP cameras.
|
This component provides basic support for Foscam 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
|
||||||
@ -18,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# 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):
|
||||||
""" Adds a Foscam IP Camera. """
|
"""Setup a Foscam IP Camera."""
|
||||||
if not validate_config({DOMAIN: config},
|
if not validate_config({DOMAIN: config},
|
||||||
{DOMAIN: ['username', 'password', 'ip']}, _LOGGER):
|
{DOMAIN: ['username', 'password', 'ip']}, _LOGGER):
|
||||||
return None
|
return None
|
||||||
@ -31,6 +29,7 @@ class FoscamCamera(Camera):
|
|||||||
"""An implementation of a Foscam IP camera."""
|
"""An implementation of a Foscam IP camera."""
|
||||||
|
|
||||||
def __init__(self, device_info):
|
def __init__(self, device_info):
|
||||||
|
"""Initialize a Foscam camera."""
|
||||||
super(FoscamCamera, self).__init__()
|
super(FoscamCamera, self).__init__()
|
||||||
|
|
||||||
ip_address = device_info.get('ip')
|
ip_address = device_info.get('ip')
|
||||||
@ -49,7 +48,6 @@ class FoscamCamera(Camera):
|
|||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
"""Return a still image reponse from the camera."""
|
"""Return a still image reponse from the camera."""
|
||||||
|
|
||||||
# Send the request to snap a picture and return raw jpg data
|
# Send the request to snap a picture and return raw jpg data
|
||||||
response = requests.get(self._snap_picture_url)
|
response = requests.get(self._snap_picture_url)
|
||||||
|
|
||||||
@ -57,5 +55,5 @@ class FoscamCamera(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
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.generic
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for IP Cameras.
|
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
|
||||||
@ -19,7 +17,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# 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):
|
||||||
""" Adds a generic IP Camera. """
|
"""Setup a generic IP Camera."""
|
||||||
if not validate_config({DOMAIN: config}, {DOMAIN: ['still_image_url']},
|
if not validate_config({DOMAIN: config}, {DOMAIN: ['still_image_url']},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
@ -29,11 +27,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class GenericCamera(Camera):
|
class GenericCamera(Camera):
|
||||||
"""
|
"""A generic implementation of an IP camera."""
|
||||||
A generic implementation of an IP camera that is reachable over a URL.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, device_info):
|
def __init__(self, device_info):
|
||||||
|
"""Initialize a generic camera."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._name = device_info.get('name', 'Generic Camera')
|
self._name = device_info.get('name', 'Generic Camera')
|
||||||
self._username = device_info.get('username')
|
self._username = device_info.get('username')
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.mjpeg
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for IP Cameras.
|
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
|
||||||
@ -23,7 +21,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# 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):
|
||||||
""" Adds a mjpeg IP Camera. """
|
"""Setup a MJPEG IP Camera."""
|
||||||
if not validate_config({DOMAIN: config}, {DOMAIN: ['mjpeg_url']},
|
if not validate_config({DOMAIN: config}, {DOMAIN: ['mjpeg_url']},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
@ -33,11 +31,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class MjpegCamera(Camera):
|
class MjpegCamera(Camera):
|
||||||
"""
|
"""An implementation of an IP camera that is reachable over a URL."""
|
||||||
A generic implementation of an IP camera that is reachable over a URL.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, device_info):
|
def __init__(self, device_info):
|
||||||
|
"""Initialize a MJPEG camera."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._name = device_info.get('name', 'Mjpeg Camera')
|
self._name = device_info.get('name', 'Mjpeg Camera')
|
||||||
self._username = device_info.get('username')
|
self._username = device_info.get('username')
|
||||||
@ -45,7 +42,7 @@ class MjpegCamera(Camera):
|
|||||||
self._mjpeg_url = device_info['mjpeg_url']
|
self._mjpeg_url = device_info['mjpeg_url']
|
||||||
|
|
||||||
def camera_stream(self):
|
def camera_stream(self):
|
||||||
""" Return a mjpeg stream image response directly from the camera. """
|
"""Return a MJPEG stream image response directly from the camera."""
|
||||||
if self._username and self._password:
|
if self._username and self._password:
|
||||||
return requests.get(self._mjpeg_url,
|
return requests.get(self._mjpeg_url,
|
||||||
auth=HTTPBasicAuth(self._username,
|
auth=HTTPBasicAuth(self._username,
|
||||||
@ -57,7 +54,6 @@ class MjpegCamera(Camera):
|
|||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
"""Return a still image response from the camera."""
|
"""Return a still image response from the camera."""
|
||||||
|
|
||||||
def process_response(response):
|
def process_response(response):
|
||||||
"""Take in a response object, return the jpg from it."""
|
"""Take in a response object, return the jpg from it."""
|
||||||
data = b''
|
data = b''
|
||||||
@ -88,5 +84,5 @@ class MjpegCamera(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
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.camera.uvc
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for Ubiquiti's UVC cameras.
|
Support for Ubiquiti's UVC 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
|
||||||
@ -63,6 +61,7 @@ class UnifiVideoCamera(Camera):
|
|||||||
"""A Ubiquiti Unifi Video Camera."""
|
"""A Ubiquiti Unifi Video Camera."""
|
||||||
|
|
||||||
def __init__(self, nvr, uuid, name):
|
def __init__(self, nvr, uuid, name):
|
||||||
|
"""Initialize an Unifi camera."""
|
||||||
super(UnifiVideoCamera, self).__init__()
|
super(UnifiVideoCamera, self).__init__()
|
||||||
self._nvr = nvr
|
self._nvr = nvr
|
||||||
self._uuid = uuid
|
self._uuid = uuid
|
||||||
@ -73,23 +72,28 @@ class UnifiVideoCamera(Camera):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""Return the name of this camera."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_recording(self):
|
def is_recording(self):
|
||||||
|
"""Return true if the camera is recording."""
|
||||||
caminfo = self._nvr.get_camera(self._uuid)
|
caminfo = self._nvr.get_camera(self._uuid)
|
||||||
return caminfo['recordingSettings']['fullTimeRecordEnabled']
|
return caminfo['recordingSettings']['fullTimeRecordEnabled']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brand(self):
|
def brand(self):
|
||||||
|
"""Return the brand of this camera."""
|
||||||
return 'Ubiquiti'
|
return 'Ubiquiti'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self):
|
def model(self):
|
||||||
|
"""Return the model of this camera."""
|
||||||
caminfo = self._nvr.get_camera(self._uuid)
|
caminfo = self._nvr.get_camera(self._uuid)
|
||||||
return caminfo['model']
|
return caminfo['model']
|
||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
|
"""Login to the camera."""
|
||||||
from uvcclient import camera as uvc_camera
|
from uvcclient import camera as uvc_camera
|
||||||
from uvcclient import store as uvc_store
|
from uvcclient import store as uvc_store
|
||||||
|
|
||||||
@ -131,6 +135,7 @@ class UnifiVideoCamera(Camera):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
|
"""Return the image of this camera."""
|
||||||
from uvcclient import camera as uvc_camera
|
from uvcclient import camera as uvc_camera
|
||||||
if not self._camera:
|
if not self._camera:
|
||||||
if not self._login():
|
if not self._login():
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.configurator
|
Support to allow pieces of code to request configuration from the user.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
A component to allow pieces of code to request configuration from the user.
|
|
||||||
|
|
||||||
Initiate a request by calling the `request_config` method with a callback.
|
Initiate a request by calling the `request_config` method with a callback.
|
||||||
This will return a request id that has to be used for future calls.
|
This will return a request id that has to be used for future calls.
|
||||||
@ -38,9 +35,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
def request_config(
|
def request_config(
|
||||||
hass, name, callback, description=None, description_image=None,
|
hass, name, callback, description=None, description_image=None,
|
||||||
submit_caption=None, fields=None):
|
submit_caption=None, fields=None):
|
||||||
""" Create a new request for config.
|
"""Create a new request for configuration.
|
||||||
Will return an ID to be used for sequent calls. """
|
|
||||||
|
|
||||||
|
Will return an ID to be used for sequent calls.
|
||||||
|
"""
|
||||||
instance = _get_instance(hass)
|
instance = _get_instance(hass)
|
||||||
|
|
||||||
request_id = instance.request_config(
|
request_id = instance.request_config(
|
||||||
@ -62,7 +60,7 @@ def notify_errors(request_id, error):
|
|||||||
|
|
||||||
|
|
||||||
def request_done(request_id):
|
def request_done(request_id):
|
||||||
""" Mark a config request as done. """
|
"""Mark a configuration request as done."""
|
||||||
try:
|
try:
|
||||||
_REQUESTS.pop(request_id).request_done(request_id)
|
_REQUESTS.pop(request_id).request_done(request_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -71,7 +69,7 @@ def request_done(request_id):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Set up Configurator. """
|
"""Setup the configurator component."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -89,11 +87,10 @@ def _get_instance(hass):
|
|||||||
|
|
||||||
|
|
||||||
class Configurator(object):
|
class Configurator(object):
|
||||||
"""
|
"""The class to keep track of current configuration requests."""
|
||||||
Class to keep track of current configuration requests.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, hass):
|
def __init__(self, hass):
|
||||||
|
"""Initialize the configurator."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._cur_id = 0
|
self._cur_id = 0
|
||||||
self._requests = {}
|
self._requests = {}
|
||||||
@ -105,7 +102,6 @@ class Configurator(object):
|
|||||||
self, name, callback,
|
self, name, callback,
|
||||||
description, description_image, submit_caption, fields):
|
description, description_image, submit_caption, fields):
|
||||||
"""Setup a request for configuration."""
|
"""Setup a request for configuration."""
|
||||||
|
|
||||||
entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=self.hass)
|
entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=self.hass)
|
||||||
|
|
||||||
if fields is None:
|
if fields is None:
|
||||||
@ -147,7 +143,7 @@ class Configurator(object):
|
|||||||
self.hass.states.set(entity_id, STATE_CONFIGURE, new_data)
|
self.hass.states.set(entity_id, STATE_CONFIGURE, new_data)
|
||||||
|
|
||||||
def request_done(self, request_id):
|
def request_done(self, request_id):
|
||||||
""" Remove the config request. """
|
"""Remove the configuration request."""
|
||||||
if not self._validate_request_id(request_id):
|
if not self._validate_request_id(request_id):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -180,7 +176,7 @@ class Configurator(object):
|
|||||||
callback(call.data.get(ATTR_FIELDS, {}))
|
callback(call.data.get(ATTR_FIELDS, {}))
|
||||||
|
|
||||||
def _generate_unique_id(self):
|
def _generate_unique_id(self):
|
||||||
""" Generates a unique configurator id. """
|
"""Generate a unique configurator ID."""
|
||||||
self._cur_id += 1
|
self._cur_id += 1
|
||||||
return "{}-{}".format(id(self), self._cur_id)
|
return "{}-{}".format(id(self), self._cur_id)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.conversation
|
Support for functionality to have conversations with Home Assistant.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides functionality to have conversations with Home Assistant.
|
|
||||||
|
|
||||||
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/conversation/
|
https://home-assistant.io/components/conversation/
|
||||||
@ -25,19 +23,18 @@ REQUIREMENTS = ['fuzzywuzzy==0.8.0']
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Registers the process service. """
|
"""Register the process service."""
|
||||||
from fuzzywuzzy import process as fuzzyExtract
|
from fuzzywuzzy import process as fuzzyExtract
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def process(service):
|
def process(service):
|
||||||
""" Parses text into commands for Home Assistant. """
|
"""Parse text into commands."""
|
||||||
if ATTR_TEXT not in service.data:
|
if ATTR_TEXT not in service.data:
|
||||||
logger.error("Received process service call without a text")
|
logger.error("Received process service call without a text")
|
||||||
return
|
return
|
||||||
|
|
||||||
text = service.data[ATTR_TEXT].lower()
|
text = service.data[ATTR_TEXT].lower()
|
||||||
|
|
||||||
match = REGEX_TURN_COMMAND.match(text)
|
match = REGEX_TURN_COMMAND.match(text)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
@ -45,11 +42,8 @@ def setup(hass, config):
|
|||||||
return
|
return
|
||||||
|
|
||||||
name, command = match.groups()
|
name, command = match.groups()
|
||||||
|
|
||||||
entities = {state.entity_id: state.name for state in hass.states.all()}
|
entities = {state.entity_id: state.name for state in hass.states.all()}
|
||||||
|
entity_ids = fuzzyExtract.extractOne(name, entities,
|
||||||
entity_ids = fuzzyExtract.extractOne(name,
|
|
||||||
entities,
|
|
||||||
score_cutoff=65)[2]
|
score_cutoff=65)[2]
|
||||||
|
|
||||||
if not entity_ids:
|
if not entity_ids:
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_sun_light_trigger
|
Provides functionality to turn on lights based on the states.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides functionality to turn on lights based on the state of the sun and
|
|
||||||
devices.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_sun_light_trigger/
|
https://home-assistant.io/components/device_sun_light_trigger/
|
||||||
@ -12,9 +9,9 @@ from datetime import timedelta
|
|||||||
|
|
||||||
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 homeassistant.helpers.event import track_point_in_time, track_state_change
|
from homeassistant.helpers.event import track_point_in_time
|
||||||
|
from homeassistant.helpers.event_decorators import track_state_change
|
||||||
from . import device_tracker, group, light, sun
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
DOMAIN = "device_sun_light_trigger"
|
DOMAIN = "device_sun_light_trigger"
|
||||||
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']
|
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']
|
||||||
@ -29,28 +26,26 @@ CONF_LIGHT_GROUP = 'light_group'
|
|||||||
CONF_DEVICE_GROUP = 'device_group'
|
CONF_DEVICE_GROUP = 'device_group'
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-locals
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Triggers to turn lights on or off based on device precense. """
|
"""The triggers to turn lights on or off based on device presence."""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
device_tracker = get_component('device_tracker')
|
||||||
|
group = get_component('group')
|
||||||
|
light = get_component('light')
|
||||||
|
sun = get_component('sun')
|
||||||
|
|
||||||
disable_turn_off = 'disable_turn_off' in config[DOMAIN]
|
disable_turn_off = 'disable_turn_off' in config[DOMAIN]
|
||||||
|
|
||||||
light_group = config[DOMAIN].get(CONF_LIGHT_GROUP,
|
light_group = config[DOMAIN].get(CONF_LIGHT_GROUP,
|
||||||
light.ENTITY_ID_ALL_LIGHTS)
|
light.ENTITY_ID_ALL_LIGHTS)
|
||||||
|
|
||||||
light_profile = config[DOMAIN].get(CONF_LIGHT_PROFILE, LIGHT_PROFILE)
|
light_profile = config[DOMAIN].get(CONF_LIGHT_PROFILE, LIGHT_PROFILE)
|
||||||
|
|
||||||
device_group = config[DOMAIN].get(CONF_DEVICE_GROUP,
|
device_group = config[DOMAIN].get(CONF_DEVICE_GROUP,
|
||||||
device_tracker.ENTITY_ID_ALL_DEVICES)
|
device_tracker.ENTITY_ID_ALL_DEVICES)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
device_entity_ids = group.get_entity_ids(hass, device_group,
|
device_entity_ids = group.get_entity_ids(hass, device_group,
|
||||||
device_tracker.DOMAIN)
|
device_tracker.DOMAIN)
|
||||||
|
|
||||||
if not device_entity_ids:
|
if not device_entity_ids:
|
||||||
logger.error("No devices found to track")
|
logger.error("No devices found to track")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Get the light IDs from the specified group
|
# Get the light IDs from the specified group
|
||||||
@ -58,77 +53,75 @@ def setup(hass, config):
|
|||||||
|
|
||||||
if not light_ids:
|
if not light_ids:
|
||||||
logger.error("No lights found to turn on ")
|
logger.error("No lights found to turn on ")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def calc_time_for_light_when_sunset():
|
def calc_time_for_light_when_sunset():
|
||||||
""" Calculates the time when to start fading lights in when sun sets.
|
"""Calculate the time when to start fading lights in when sun sets.
|
||||||
Returns None if no next_setting data available. """
|
|
||||||
|
Returns None if no next_setting data available.
|
||||||
|
"""
|
||||||
next_setting = sun.next_setting(hass)
|
next_setting = sun.next_setting(hass)
|
||||||
|
if not next_setting:
|
||||||
if next_setting:
|
|
||||||
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
|
||||||
def schedule_light_on_sun_rise(entity, old_state, new_state):
|
|
||||||
"""The moment sun sets we want to have all the lights on.
|
|
||||||
We will schedule to have each light start after one another
|
|
||||||
and slowly transition in."""
|
|
||||||
|
|
||||||
def turn_light_on_before_sunset(light_id):
|
def turn_light_on_before_sunset(light_id):
|
||||||
""" Helper function to turn on lights slowly if there
|
"""Helper function to turn on lights.
|
||||||
are devices home and the light is not on yet. """
|
|
||||||
if device_tracker.is_on(hass) and not light.is_on(hass, light_id):
|
|
||||||
|
|
||||||
|
Speed is slow if there are devices home and the light is not on yet.
|
||||||
|
"""
|
||||||
|
if not device_tracker.is_on(hass) or light.is_on(hass, light_id):
|
||||||
|
return
|
||||||
light.turn_on(hass, light_id,
|
light.turn_on(hass, light_id,
|
||||||
transition=LIGHT_TRANSITION_TIME.seconds,
|
transition=LIGHT_TRANSITION_TIME.seconds,
|
||||||
profile=light_profile)
|
profile=light_profile)
|
||||||
|
|
||||||
def turn_on(light_id):
|
|
||||||
""" Lambda can keep track of function parameters but not local
|
|
||||||
parameters. If we put the lambda directly in the below statement
|
|
||||||
only the last light will be turned on.. """
|
|
||||||
return lambda now: turn_light_on_before_sunset(light_id)
|
|
||||||
|
|
||||||
start_point = calc_time_for_light_when_sunset()
|
|
||||||
|
|
||||||
if start_point:
|
|
||||||
for index, light_id in enumerate(light_ids):
|
|
||||||
track_point_in_time(
|
|
||||||
hass, turn_on(light_id),
|
|
||||||
(start_point + index * LIGHT_TRANSITION_TIME))
|
|
||||||
|
|
||||||
# Track every time sun rises so we can schedule a time-based
|
# Track every time sun rises so we can schedule a time-based
|
||||||
# pre-sun set event
|
# pre-sun set event
|
||||||
track_state_change(hass, sun.ENTITY_ID, schedule_light_on_sun_rise,
|
@track_state_change(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON,
|
||||||
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
|
sun.STATE_ABOVE_HORIZON)
|
||||||
|
def schedule_lights_at_sun_set(hass, entity, old_state, new_state):
|
||||||
|
"""The moment sun sets we want to have all the lights on.
|
||||||
|
|
||||||
# If the sun is already above horizon
|
We will schedule to have each light start after one another
|
||||||
# schedule the time-based pre-sun set event
|
and slowly transition in.
|
||||||
|
"""
|
||||||
|
start_point = calc_time_for_light_when_sunset()
|
||||||
|
if not start_point:
|
||||||
|
return
|
||||||
|
|
||||||
|
def turn_on(light_id):
|
||||||
|
"""Lambda can keep track of function parameters.
|
||||||
|
|
||||||
|
No local parameters. If we put the lambda directly in the below
|
||||||
|
statement only the last light will be turned on.
|
||||||
|
"""
|
||||||
|
return lambda now: turn_light_on_before_sunset(light_id)
|
||||||
|
|
||||||
|
for index, light_id in enumerate(light_ids):
|
||||||
|
track_point_in_time(hass, turn_on(light_id),
|
||||||
|
start_point + index * LIGHT_TRANSITION_TIME)
|
||||||
|
|
||||||
|
# If the sun is already above horizon schedule the time-based pre-sun set
|
||||||
|
# event.
|
||||||
if sun.is_on(hass):
|
if sun.is_on(hass):
|
||||||
schedule_light_on_sun_rise(None, None, None)
|
schedule_lights_at_sun_set(hass, None, None, None)
|
||||||
|
|
||||||
def check_light_on_dev_state_change(entity, old_state, new_state):
|
@track_state_change(device_entity_ids, STATE_NOT_HOME, STATE_HOME)
|
||||||
""" Function to handle tracked device state changes. """
|
def check_light_on_dev_state_change(hass, entity, old_state, new_state):
|
||||||
|
"""Handle tracked device state changes."""
|
||||||
|
# pylint: disable=unused-variable
|
||||||
lights_are_on = group.is_on(hass, light_group)
|
lights_are_on = group.is_on(hass, light_group)
|
||||||
|
|
||||||
light_needed = not (lights_are_on or sun.is_on(hass))
|
light_needed = not (lights_are_on or sun.is_on(hass))
|
||||||
|
|
||||||
# Specific device came home ?
|
|
||||||
if entity != device_tracker.ENTITY_ID_ALL_DEVICES and \
|
|
||||||
new_state.state == STATE_HOME:
|
|
||||||
|
|
||||||
# These variables are needed for the elif check
|
# These variables are needed for the elif check
|
||||||
now = dt_util.now()
|
now = dt_util.now()
|
||||||
start_point = calc_time_for_light_when_sunset()
|
start_point = calc_time_for_light_when_sunset()
|
||||||
|
|
||||||
# Do we need lights?
|
# Do we need lights?
|
||||||
if light_needed:
|
if light_needed:
|
||||||
|
logger.info("Home coming event for %s. Turning lights on", entity)
|
||||||
logger.info(
|
|
||||||
"Home coming event for %s. Turning lights on", entity)
|
|
||||||
|
|
||||||
light.turn_on(hass, light_ids, profile=light_profile)
|
light.turn_on(hass, light_ids, profile=light_profile)
|
||||||
|
|
||||||
# Are we in the time span were we would turn on the lights
|
# Are we in the time span were we would turn on the lights
|
||||||
@ -141,7 +134,6 @@ def setup(hass, config):
|
|||||||
# Check for every light if it would be on if someone was home
|
# Check for every light if it would be on if someone was home
|
||||||
# when the fading in started and turn it on if so
|
# when the fading in started and turn it on if so
|
||||||
for index, light_id in enumerate(light_ids):
|
for index, light_id in enumerate(light_ids):
|
||||||
|
|
||||||
if now > start_point + index * LIGHT_TRANSITION_TIME:
|
if now > start_point + index * LIGHT_TRANSITION_TIME:
|
||||||
light.turn_on(hass, light_id)
|
light.turn_on(hass, light_id)
|
||||||
|
|
||||||
@ -150,24 +142,16 @@ def setup(hass, config):
|
|||||||
# will all the following then, break.
|
# will all the following then, break.
|
||||||
break
|
break
|
||||||
|
|
||||||
# Did all devices leave the house?
|
if not disable_turn_off:
|
||||||
elif (entity == device_group and
|
@track_state_change(device_group, STATE_HOME, STATE_NOT_HOME)
|
||||||
new_state.state == STATE_NOT_HOME and lights_are_on and
|
def turn_off_lights_when_all_leave(hass, entity, old_state, new_state):
|
||||||
not disable_turn_off):
|
"""Handle device group state change."""
|
||||||
|
# pylint: disable=unused-variable
|
||||||
|
if not group.is_on(hass, light_group):
|
||||||
|
return
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Everyone has left but there are lights on. Turning them off")
|
"Everyone has left but there are lights on. Turning them off")
|
||||||
|
|
||||||
light.turn_off(hass, light_ids)
|
light.turn_off(hass, light_ids)
|
||||||
|
|
||||||
# Track home coming of each device
|
|
||||||
track_state_change(
|
|
||||||
hass, device_entity_ids, check_light_on_dev_state_change,
|
|
||||||
STATE_NOT_HOME, STATE_HOME)
|
|
||||||
|
|
||||||
# Track when all devices are gone to shut down lights
|
|
||||||
track_state_change(
|
|
||||||
hass, device_group, check_light_on_dev_state_change,
|
|
||||||
STATE_HOME, STATE_NOT_HOME)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker
|
Provide functionality to keep track of devices.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides functionality to keep track of devices.
|
|
||||||
|
|
||||||
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/device_tracker/
|
https://home-assistant.io/components/device_tracker/
|
||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||||
# pylint: disable=too-many-locals
|
# pylint: disable=too-many-locals
|
||||||
import csv
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -36,7 +33,6 @@ ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format('all_devices')
|
|||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
CSV_DEVICES = "known_devices.csv"
|
|
||||||
YAML_DEVICES = 'known_devices.yaml'
|
YAML_DEVICES = 'known_devices.yaml'
|
||||||
|
|
||||||
CONF_TRACK_NEW = "track_new_devices"
|
CONF_TRACK_NEW = "track_new_devices"
|
||||||
@ -72,7 +68,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id=None):
|
def is_on(hass, entity_id=None):
|
||||||
""" Returns if any or specified device is home. """
|
"""Return the state if any or a specified device is home."""
|
||||||
entity = entity_id or ENTITY_ID_ALL_DEVICES
|
entity = entity_id or ENTITY_ID_ALL_DEVICES
|
||||||
|
|
||||||
return hass.states.is_state(entity, STATE_HOME)
|
return hass.states.is_state(entity, STATE_HOME)
|
||||||
@ -86,17 +82,15 @@ def see(hass, mac=None, dev_id=None, host_name=None, location_name=None,
|
|||||||
(ATTR_DEV_ID, dev_id),
|
(ATTR_DEV_ID, dev_id),
|
||||||
(ATTR_HOST_NAME, host_name),
|
(ATTR_HOST_NAME, host_name),
|
||||||
(ATTR_LOCATION_NAME, location_name),
|
(ATTR_LOCATION_NAME, location_name),
|
||||||
(ATTR_GPS, gps)) if value is not None}
|
(ATTR_GPS, gps),
|
||||||
|
(ATTR_GPS_ACCURACY, gps_accuracy),
|
||||||
|
(ATTR_BATTERY, battery)) if value is not None}
|
||||||
hass.services.call(DOMAIN, SERVICE_SEE, data)
|
hass.services.call(DOMAIN, SERVICE_SEE, data)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup device tracker """
|
"""Setup device tracker."""
|
||||||
yaml_path = hass.config.path(YAML_DEVICES)
|
yaml_path = hass.config.path(YAML_DEVICES)
|
||||||
csv_path = hass.config.path(CSV_DEVICES)
|
|
||||||
if os.path.isfile(csv_path) and not os.path.isfile(yaml_path) and \
|
|
||||||
convert_csv_config(csv_path, yaml_path):
|
|
||||||
os.remove(csv_path)
|
|
||||||
|
|
||||||
conf = config.get(DOMAIN, {})
|
conf = config.get(DOMAIN, {})
|
||||||
if isinstance(conf, list):
|
if isinstance(conf, list):
|
||||||
@ -169,8 +163,10 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceTracker(object):
|
class DeviceTracker(object):
|
||||||
""" Track devices """
|
"""Representation of a device tracker."""
|
||||||
|
|
||||||
def __init__(self, hass, consider_home, track_new, home_range, devices):
|
def __init__(self, hass, consider_home, track_new, home_range, devices):
|
||||||
|
"""Initialize a device tracker."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.devices = {dev.dev_id: dev for dev in devices}
|
self.devices = {dev.dev_id: dev for dev in devices}
|
||||||
self.mac_to_dev = {dev.mac: dev for dev in devices if dev.mac}
|
self.mac_to_dev = {dev.mac: dev for dev in devices if dev.mac}
|
||||||
@ -187,7 +183,7 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
def see(self, mac=None, dev_id=None, host_name=None, location_name=None,
|
def see(self, mac=None, dev_id=None, host_name=None, location_name=None,
|
||||||
gps=None, gps_accuracy=None, battery=None):
|
gps=None, gps_accuracy=None, battery=None):
|
||||||
""" Notify device tracker that you see a device. """
|
"""Notify the device tracker that you see a device."""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if mac is None and dev_id is None:
|
if mac is None and dev_id is None:
|
||||||
raise HomeAssistantError('Neither mac or device id passed in')
|
raise HomeAssistantError('Neither mac or device id passed in')
|
||||||
@ -226,7 +222,7 @@ class DeviceTracker(object):
|
|||||||
update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
|
update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
|
||||||
|
|
||||||
def setup_group(self):
|
def setup_group(self):
|
||||||
""" Initializes group for all tracked devices. """
|
"""Initialize group for all tracked devices."""
|
||||||
entity_ids = (dev.entity_id for dev in self.devices.values()
|
entity_ids = (dev.entity_id for dev in self.devices.values()
|
||||||
if dev.track)
|
if dev.track)
|
||||||
self.group = group.Group(
|
self.group = group.Group(
|
||||||
@ -242,7 +238,7 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
|
|
||||||
class Device(Entity):
|
class Device(Entity):
|
||||||
""" Tracked device. """
|
"""Represent a tracked device."""
|
||||||
|
|
||||||
host_name = None
|
host_name = None
|
||||||
location_name = None
|
location_name = None
|
||||||
@ -251,12 +247,13 @@ class Device(Entity):
|
|||||||
last_seen = None
|
last_seen = None
|
||||||
battery = None
|
battery = None
|
||||||
|
|
||||||
# Track if the last update of this device was HOME
|
# Track if the last update of this device was HOME.
|
||||||
last_update_home = False
|
last_update_home = False
|
||||||
_state = STATE_NOT_HOME
|
_state = STATE_NOT_HOME
|
||||||
|
|
||||||
def __init__(self, hass, consider_home, home_range, track, dev_id, mac,
|
def __init__(self, hass, consider_home, home_range, track, dev_id, mac,
|
||||||
name=None, picture=None, away_hide=False):
|
name=None, picture=None, away_hide=False):
|
||||||
|
"""Initialize a device."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.entity_id = ENTITY_ID_FORMAT.format(dev_id)
|
self.entity_id = ENTITY_ID_FORMAT.format(dev_id)
|
||||||
|
|
||||||
@ -289,22 +286,22 @@ class Device(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the entity. """
|
"""Return the name of the entity."""
|
||||||
return self.config_name or self.host_name or DEVICE_DEFAULT_NAME
|
return self.config_name or self.host_name or DEVICE_DEFAULT_NAME
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" State of the device. """
|
"""Return the state of the device."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entity_picture(self):
|
def entity_picture(self):
|
||||||
"""Picture of the device."""
|
"""Return the picture of the device."""
|
||||||
return self.config_picture
|
return self.config_picture
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Device state attributes. """
|
"""Return the device state attributes."""
|
||||||
attr = {}
|
attr = {}
|
||||||
|
|
||||||
if self.gps:
|
if self.gps:
|
||||||
@ -370,23 +367,8 @@ class Device(Entity):
|
|||||||
self.last_update_home = True
|
self.last_update_home = True
|
||||||
|
|
||||||
|
|
||||||
def convert_csv_config(csv_path, yaml_path):
|
|
||||||
""" Convert CSV config file format to YAML. """
|
|
||||||
used_ids = set()
|
|
||||||
with open(csv_path) as inp:
|
|
||||||
for row in csv.DictReader(inp):
|
|
||||||
dev_id = util.ensure_unique_string(
|
|
||||||
(util.slugify(row['name']) or DEVICE_DEFAULT_NAME).lower(),
|
|
||||||
used_ids)
|
|
||||||
used_ids.add(dev_id)
|
|
||||||
device = Device(None, None, None, row['track'] == '1', dev_id,
|
|
||||||
row['device'], row['name'], row['picture'])
|
|
||||||
update_config(yaml_path, dev_id, device)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def load_config(path, hass, consider_home, home_range):
|
def load_config(path, hass, consider_home, home_range):
|
||||||
""" Load devices from YAML config file. """
|
"""Load devices from YAML configuration file."""
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
return []
|
return []
|
||||||
return [
|
return [
|
||||||
@ -422,7 +404,7 @@ def setup_scanner_platform(hass, config, scanner, see_device):
|
|||||||
|
|
||||||
|
|
||||||
def update_config(path, dev_id, device):
|
def update_config(path, dev_id, device):
|
||||||
""" Add device to YAML config file. """
|
"""Add device to YAML configuration file."""
|
||||||
with open(path, 'a') as out:
|
with open(path, 'a') as out:
|
||||||
out.write('\n')
|
out.write('\n')
|
||||||
out.write('{}:\n'.format(device.dev_id))
|
out.write('{}:\n'.format(device.dev_id))
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.actiontec
|
Support for Actiontec MI424WR (Verizon FIOS) routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning an Actiontec MI424WR
|
|
||||||
(Verizon FIOS) router for device presence.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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/
|
||||||
@ -20,7 +17,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -34,7 +31,7 @@ _LEASES_REGEX = re.compile(
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns an Actiontec scanner. """
|
"""Validate the configuration and return an Actiontec scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -46,12 +43,10 @@ Device = namedtuple("Device", ["mac", "ip", "last_update"])
|
|||||||
|
|
||||||
|
|
||||||
class ActiontecDeviceScanner(object):
|
class ActiontecDeviceScanner(object):
|
||||||
"""
|
"""This class queries a an actiontec router for connected devices."""
|
||||||
This class queries a an actiontec router for connected devices.
|
|
||||||
Adapted from DD-WRT scanner.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.host = config[CONF_HOST]
|
self.host = config[CONF_HOST]
|
||||||
self.username = config[CONF_USERNAME]
|
self.username = config[CONF_USERNAME]
|
||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
@ -62,15 +57,12 @@ class ActiontecDeviceScanner(object):
|
|||||||
_LOGGER.info("actiontec scanner initialized")
|
_LOGGER.info("actiontec scanner initialized")
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return [client.mac for client in self.last_results]
|
return [client.mac for client in self.last_results]
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
if not self.last_results:
|
if not self.last_results:
|
||||||
return None
|
return None
|
||||||
for client in self.last_results:
|
for client in self.last_results:
|
||||||
@ -80,9 +72,9 @@ class ActiontecDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the router is up to date.
|
||||||
Ensures the information from the Actiontec MI424WR router is up
|
|
||||||
to date. Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
_LOGGER.info("Scanning")
|
_LOGGER.info("Scanning")
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.aruba
|
Support for Aruba Access Points.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a Aruba Access Point for device
|
|
||||||
presence.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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/
|
||||||
@ -31,7 +28,7 @@ _DEVICES_REGEX = re.compile(
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Aruba scanner. """
|
"""Validate the configuration and return a Aruba scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -43,9 +40,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class ArubaDeviceScanner(object):
|
class ArubaDeviceScanner(object):
|
||||||
""" This class queries a Aruba Acces Point for connected devices. """
|
"""This class queries a Aruba Access Point for connected devices."""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.host = config[CONF_HOST]
|
self.host = config[CONF_HOST]
|
||||||
self.username = config[CONF_USERNAME]
|
self.username = config[CONF_USERNAME]
|
||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
@ -54,20 +52,17 @@ class ArubaDeviceScanner(object):
|
|||||||
|
|
||||||
self.last_results = {}
|
self.last_results = {}
|
||||||
|
|
||||||
# Test the router is accessible
|
# Test the router is accessible.
|
||||||
data = self.get_aruba_data()
|
data = self.get_aruba_data()
|
||||||
self.success_init = data is not None
|
self.success_init = data is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device IDs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return [client['mac'] for client in self.last_results]
|
return [client['mac'] for client in self.last_results]
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
if not self.last_results:
|
if not self.last_results:
|
||||||
return None
|
return None
|
||||||
for client in self.last_results:
|
for client in self.last_results:
|
||||||
@ -77,9 +72,9 @@ class ArubaDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the Aruba Access Point is up to date.
|
||||||
Ensures the information from the Aruba Access Point is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
@ -94,7 +89,6 @@ class ArubaDeviceScanner(object):
|
|||||||
|
|
||||||
def get_aruba_data(self):
|
def get_aruba_data(self):
|
||||||
"""Retrieve data from Aruba Access Point and return parsed result."""
|
"""Retrieve data from Aruba Access Point and return parsed result."""
|
||||||
|
|
||||||
import pexpect
|
import pexpect
|
||||||
connect = "ssh {}@{}"
|
connect = "ssh {}@{}"
|
||||||
ssh = pexpect.spawn(connect.format(self.username, self.host))
|
ssh = pexpect.spawn(connect.format(self.username, self.host))
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.asuswrt
|
Support for ASUSWRT routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a ASUSWRT router for device
|
|
||||||
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.asuswrt/
|
https://home-assistant.io/components/device_tracker.asuswrt/
|
||||||
@ -18,7 +15,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -39,7 +36,7 @@ _IP_NEIGH_REGEX = re.compile(
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns an ASUS-WRT scanner. """
|
"""Validate the configuration and return an ASUS-WRT scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -51,12 +48,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class AsusWrtDeviceScanner(object):
|
class AsusWrtDeviceScanner(object):
|
||||||
"""
|
"""This class queries a router running ASUSWRT firmware."""
|
||||||
This class queries a router running ASUSWRT firmware
|
|
||||||
for connected devices. Adapted from DD-WRT scanner.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.host = config[CONF_HOST]
|
self.host = config[CONF_HOST]
|
||||||
self.username = str(config[CONF_USERNAME])
|
self.username = str(config[CONF_USERNAME])
|
||||||
self.password = str(config[CONF_PASSWORD])
|
self.password = str(config[CONF_PASSWORD])
|
||||||
@ -65,20 +60,17 @@ class AsusWrtDeviceScanner(object):
|
|||||||
|
|
||||||
self.last_results = {}
|
self.last_results = {}
|
||||||
|
|
||||||
# Test the router is accessible
|
# Test the router is accessible.
|
||||||
data = self.get_asuswrt_data()
|
data = self.get_asuswrt_data()
|
||||||
self.success_init = data is not None
|
self.success_init = data is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device IDs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return [client['mac'] for client in self.last_results]
|
return [client['mac'] for client in self.last_results]
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
if not self.last_results:
|
if not self.last_results:
|
||||||
return None
|
return None
|
||||||
for client in self.last_results:
|
for client in self.last_results:
|
||||||
@ -88,9 +80,9 @@ class AsusWrtDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the ASUSWRT router is up to date.
|
||||||
Ensures the information from the ASUSWRT router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
@ -138,9 +130,8 @@ class AsusWrtDeviceScanner(object):
|
|||||||
_LOGGER.warning("Could not parse lease row: %s", lease)
|
_LOGGER.warning("Could not parse lease row: %s", lease)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# For leases where the client doesn't set a hostname, ensure
|
# For leases where the client doesn't set a hostname, ensure it is
|
||||||
# it is blank and not '*', which breaks the entity_id down
|
# blank and not '*', which breaks the entity_id down the line.
|
||||||
# the line
|
|
||||||
host = match.group('host')
|
host = match.group('host')
|
||||||
if host == '*':
|
if host == '*':
|
||||||
host = ''
|
host = ''
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.ddwrt
|
Support for DD-WRT routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a DD-WRT router for device
|
|
||||||
presence.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
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/
|
||||||
@ -19,7 +16,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -30,7 +27,7 @@ _MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})')
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a DD-WRT scanner. """
|
"""Validate the configuration and return a DD-WRT scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -43,12 +40,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class DdWrtDeviceScanner(object):
|
class DdWrtDeviceScanner(object):
|
||||||
"""
|
"""This class queries a wireless router running DD-WRT firmware."""
|
||||||
This class queries a wireless router running DD-WRT firmware
|
|
||||||
for connected devices. Adapted from Tomato scanner.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.host = config[CONF_HOST]
|
self.host = config[CONF_HOST]
|
||||||
self.username = config[CONF_USERNAME]
|
self.username = config[CONF_USERNAME]
|
||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
@ -65,19 +60,15 @@ class DdWrtDeviceScanner(object):
|
|||||||
self.success_init = data is not None
|
self.success_init = data is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return self.last_results
|
return self.last_results
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
# if not initialised and not already scanned and not found
|
# If not initialised and not already scanned and not found.
|
||||||
if device not in self.mac2name:
|
if device not in self.mac2name:
|
||||||
url = 'http://{}/Status_Lan.live.asp'.format(self.host)
|
url = 'http://{}/Status_Lan.live.asp'.format(self.host)
|
||||||
data = self.get_ddwrt_data(url)
|
data = self.get_ddwrt_data(url)
|
||||||
@ -90,15 +81,15 @@ class DdWrtDeviceScanner(object):
|
|||||||
if not dhcp_leases:
|
if not dhcp_leases:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# remove leading and trailing single quotes
|
# Remove leading and trailing single quotes.
|
||||||
cleaned_str = dhcp_leases.strip().strip('"')
|
cleaned_str = dhcp_leases.strip().strip('"')
|
||||||
elements = cleaned_str.split('","')
|
elements = cleaned_str.split('","')
|
||||||
num_clients = int(len(elements)/5)
|
num_clients = int(len(elements)/5)
|
||||||
self.mac2name = {}
|
self.mac2name = {}
|
||||||
for idx in range(0, num_clients):
|
for idx in range(0, num_clients):
|
||||||
# this is stupid but the data is a single array
|
# This is stupid but the data is a single array
|
||||||
# every 5 elements represents one hosts, the MAC
|
# every 5 elements represents one hosts, the MAC
|
||||||
# is the third element and the name is the first
|
# is the third element and the name is the first.
|
||||||
mac_index = (idx * 5) + 2
|
mac_index = (idx * 5) + 2
|
||||||
if mac_index < len(elements):
|
if mac_index < len(elements):
|
||||||
mac = elements[mac_index]
|
mac = elements[mac_index]
|
||||||
@ -108,9 +99,9 @@ class DdWrtDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the DD-WRT router is up to date.
|
||||||
Ensures the information from the DD-WRT router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
@ -135,7 +126,7 @@ class DdWrtDeviceScanner(object):
|
|||||||
# regex's out values so I guess I have to do the same,
|
# regex's out values so I guess I have to do the same,
|
||||||
# LAME!!!
|
# LAME!!!
|
||||||
|
|
||||||
# remove leading and trailing single quotes
|
# Remove leading and trailing single quotes.
|
||||||
clean_str = active_clients.strip().strip("'")
|
clean_str = active_clients.strip().strip("'")
|
||||||
elements = clean_str.split("','")
|
elements = clean_str.split("','")
|
||||||
|
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
"""
|
"""Demo platform for the device tracker."""
|
||||||
homeassistant.components.device_tracker.demo
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Demo platform for the device tracker.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: demo
|
|
||||||
"""
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
def setup_scanner(hass, config, see):
|
def setup_scanner(hass, config, see):
|
||||||
""" Set up a demo tracker. """
|
"""Setup the demo tracker."""
|
||||||
|
|
||||||
def offset():
|
def offset():
|
||||||
"""Return random offset."""
|
"""Return random offset."""
|
||||||
return (random.randrange(500, 2000)) / 2e5 * random.choice((-1, 1))
|
return (random.randrange(500, 2000)) / 2e5 * random.choice((-1, 1))
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.fritz
|
Support for FRITZ!Box routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a FRITZ!Box router for device
|
|
||||||
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.fritz/
|
https://home-assistant.io/components/device_tracker.fritz/
|
||||||
@ -17,15 +14,14 @@ from homeassistant.util import Throttle
|
|||||||
|
|
||||||
REQUIREMENTS = ['fritzconnection==0.4.6']
|
REQUIREMENTS = ['fritzconnection==0.4.6']
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns FritzBoxScanner. """
|
"""Validate the configuration and return FritzBoxScanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: []},
|
{DOMAIN: []},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -37,22 +33,12 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class FritzBoxScanner(object):
|
class FritzBoxScanner(object):
|
||||||
"""
|
"""This class queries a FRITZ!Box router."""
|
||||||
This class queries a FRITZ!Box router. It is using the
|
|
||||||
fritzconnection library for communication with the router.
|
|
||||||
|
|
||||||
The API description can be found under:
|
|
||||||
https://pypi.python.org/pypi/fritzconnection/0.4.6
|
|
||||||
|
|
||||||
This scanner retrieves the list of known hosts and checks their
|
|
||||||
corresponding states (on, or off).
|
|
||||||
|
|
||||||
Due to a bug of the fritzbox api (router side) it is not possible
|
|
||||||
to track more than 16 hosts.
|
|
||||||
"""
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.last_results = []
|
self.last_results = []
|
||||||
self.host = '169.254.1.1' # This IP is valid for all fritzboxes
|
self.host = '169.254.1.1' # This IP is valid for all FRITZ!Box router.
|
||||||
self.username = 'admin'
|
self.username = 'admin'
|
||||||
self.password = ''
|
self.password = ''
|
||||||
self.success_init = True
|
self.success_init = True
|
||||||
@ -68,7 +54,7 @@ class FritzBoxScanner(object):
|
|||||||
if CONF_PASSWORD in config.keys():
|
if CONF_PASSWORD in config.keys():
|
||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
|
|
||||||
# Establish a connection to the FRITZ!Box
|
# Establish a connection to the FRITZ!Box.
|
||||||
try:
|
try:
|
||||||
self.fritz_box = fc.FritzHosts(address=self.host,
|
self.fritz_box = fc.FritzHosts(address=self.host,
|
||||||
user=self.username,
|
user=self.username,
|
||||||
@ -77,7 +63,7 @@ class FritzBoxScanner(object):
|
|||||||
self.fritz_box = None
|
self.fritz_box = None
|
||||||
|
|
||||||
# At this point it is difficult to tell if a connection is established.
|
# At this point it is difficult to tell if a connection is established.
|
||||||
# So just check for null objects ...
|
# So just check for null objects.
|
||||||
if self.fritz_box is None or not self.fritz_box.modelname:
|
if self.fritz_box is None or not self.fritz_box.modelname:
|
||||||
self.success_init = False
|
self.success_init = False
|
||||||
|
|
||||||
@ -99,7 +85,7 @@ class FritzBoxScanner(object):
|
|||||||
return active_hosts
|
return active_hosts
|
||||||
|
|
||||||
def get_device_name(self, mac):
|
def get_device_name(self, mac):
|
||||||
""" Returns the name of the given device or None if is not known. """
|
"""Return the name of the given device or None if is not known."""
|
||||||
ret = self.fritz_box.get_specific_host_entry(mac)["NewHostName"]
|
ret = self.fritz_box.get_specific_host_entry(mac)["NewHostName"]
|
||||||
if ret == {}:
|
if ret == {}:
|
||||||
return None
|
return None
|
||||||
@ -107,7 +93,7 @@ class FritzBoxScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
""" Retrieves latest information from the FRITZ!Box. """
|
"""Retrieve latest information from the FRITZ!Box."""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.icloud
|
Support for iCloud connected devices.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning iCloud devices.
|
|
||||||
|
|
||||||
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.icloud/
|
https://home-assistant.io/components/device_tracker.icloud/
|
||||||
@ -26,7 +24,7 @@ def setup_scanner(hass, config, see):
|
|||||||
from pyicloud.exceptions import PyiCloudFailedLoginException
|
from pyicloud.exceptions import PyiCloudFailedLoginException
|
||||||
from pyicloud.exceptions import PyiCloudNoDevicesException
|
from pyicloud.exceptions import PyiCloudNoDevicesException
|
||||||
|
|
||||||
# Get the username and password from the configuration
|
# Get the username and password from the configuration.
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
@ -45,7 +43,7 @@ def setup_scanner(hass, config, see):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def keep_alive(now):
|
def keep_alive(now):
|
||||||
""" Keeps authenticating iCloud connection. """
|
"""Keep authenticating iCloud connection."""
|
||||||
api.authenticate()
|
api.authenticate()
|
||||||
_LOGGER.info("Authenticate against iCloud")
|
_LOGGER.info("Authenticate against iCloud")
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.locative
|
Support for the Locative platform.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Locative platform for the device tracker.
|
|
||||||
|
|
||||||
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.locative/
|
https://home-assistant.io/components/device_tracker.locative/
|
||||||
@ -20,12 +18,10 @@ URL_API_LOCATIVE_ENDPOINT = "/api/locative"
|
|||||||
|
|
||||||
|
|
||||||
def setup_scanner(hass, config, see):
|
def setup_scanner(hass, config, see):
|
||||||
""" Set up an endpoint for the Locative app. """
|
"""Setup an endpoint for the Locative application."""
|
||||||
|
|
||||||
# POST would be semantically better, but that currently does not work
|
# POST would be semantically better, but that currently does not work
|
||||||
# since Locative sends the data as key1=value1&key2=value2
|
# since Locative sends the data as key1=value1&key2=value2
|
||||||
# in the request body, while Home Assistant expects json there.
|
# in the request body, while Home Assistant expects json there.
|
||||||
|
|
||||||
hass.http.register_path(
|
hass.http.register_path(
|
||||||
'GET', URL_API_LOCATIVE_ENDPOINT,
|
'GET', URL_API_LOCATIVE_ENDPOINT,
|
||||||
partial(_handle_get_api_locative, hass, see))
|
partial(_handle_get_api_locative, hass, see))
|
||||||
@ -35,7 +31,6 @@ def setup_scanner(hass, config, see):
|
|||||||
|
|
||||||
def _handle_get_api_locative(hass, see, handler, path_match, data):
|
def _handle_get_api_locative(hass, see, handler, path_match, data):
|
||||||
"""Locative message received."""
|
"""Locative message received."""
|
||||||
|
|
||||||
if not _check_data(handler, data):
|
if not _check_data(handler, data):
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -76,6 +71,7 @@ def _handle_get_api_locative(hass, see, handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _check_data(handler, data):
|
def _check_data(handler, data):
|
||||||
|
"""Check the data."""
|
||||||
if 'latitude' not in data or 'longitude' not in data:
|
if 'latitude' not in data or 'longitude' not in data:
|
||||||
handler.write_text("Latitude and longitude not specified.",
|
handler.write_text("Latitude and longitude not specified.",
|
||||||
HTTP_UNPROCESSABLE_ENTITY)
|
HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.luci
|
Support for OpenWRT (luci) routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a OpenWRT router for device
|
|
||||||
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/
|
||||||
@ -20,14 +17,14 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Luci scanner. """
|
"""Validate the configuration and return a Luci scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -40,20 +37,13 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class LuciDeviceScanner(object):
|
class LuciDeviceScanner(object):
|
||||||
"""
|
"""This class queries a wireless router running OpenWrt firmware.
|
||||||
This class queries a wireless router running OpenWrt firmware
|
|
||||||
for connected devices. Adapted from Tomato scanner.
|
|
||||||
|
|
||||||
# opkg install luci-mod-rpc
|
Adapted from Tomato scanner.
|
||||||
for this to work on the router.
|
|
||||||
|
|
||||||
The API is described here:
|
|
||||||
http://luci.subsignal.org/trac/wiki/Documentation/JsonRpcHowTo
|
|
||||||
|
|
||||||
(Currently, we do only wifi iwscan, and no DHCP lease access.)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
host = config[CONF_HOST]
|
host = config[CONF_HOST]
|
||||||
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
@ -70,17 +60,12 @@ class LuciDeviceScanner(object):
|
|||||||
self.success_init = self.token is not None
|
self.success_init = self.token is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return self.last_results
|
return self.last_results
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if self.mac2name is None:
|
if self.mac2name is None:
|
||||||
url = 'http://{}/cgi-bin/luci/rpc/uci'.format(self.host)
|
url = 'http://{}/cgi-bin/luci/rpc/uci'.format(self.host)
|
||||||
@ -100,8 +85,8 @@ class LuciDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the Luci router is up to date.
|
||||||
Ensures the information from the Luci router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Returns boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.mqtt
|
Support for tracking MQTT enabled devices.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
MQTT platform for the device tracker.
|
|
||||||
|
|
||||||
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.mqtt/
|
https://home-assistant.io/components/device_tracker.mqtt/
|
||||||
@ -22,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup_scanner(hass, config, see):
|
def setup_scanner(hass, config, see):
|
||||||
""" Set up a MQTT tracker. """
|
"""Setup the MQTT tracker."""
|
||||||
devices = config.get(CONF_DEVICES)
|
devices = config.get(CONF_DEVICES)
|
||||||
qos = util.convert(config.get(CONF_QOS), int, DEFAULT_QOS)
|
qos = util.convert(config.get(CONF_QOS), int, DEFAULT_QOS)
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.netgear
|
Support for Netgear routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a Netgear router for device
|
|
||||||
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.netgear/
|
https://home-assistant.io/components/device_tracker.netgear/
|
||||||
@ -15,7 +12,7 @@ from homeassistant.components.device_tracker import DOMAIN
|
|||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.util import Throttle
|
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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -23,7 +20,7 @@ REQUIREMENTS = ['pynetgear==0.3.2']
|
|||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Netgear scanner. """
|
"""Validate the configuration and returns a Netgear scanner."""
|
||||||
info = config[DOMAIN]
|
info = config[DOMAIN]
|
||||||
host = info.get(CONF_HOST)
|
host = info.get(CONF_HOST)
|
||||||
username = info.get(CONF_USERNAME)
|
username = info.get(CONF_USERNAME)
|
||||||
@ -39,9 +36,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class NetgearDeviceScanner(object):
|
class NetgearDeviceScanner(object):
|
||||||
""" This class queries a Netgear wireless router using the SOAP-API. """
|
"""Queries a Netgear wireless router using the SOAP-API."""
|
||||||
|
|
||||||
def __init__(self, host, username, password):
|
def __init__(self, host, username, password):
|
||||||
|
"""Initialize the scanner."""
|
||||||
import pynetgear
|
import pynetgear
|
||||||
|
|
||||||
self.last_results = []
|
self.last_results = []
|
||||||
@ -66,15 +64,13 @@ class NetgearDeviceScanner(object):
|
|||||||
_LOGGER.error("Failed to Login")
|
_LOGGER.error("Failed to Login")
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return (device.mac for device in self.last_results)
|
return (device.mac for device in self.last_results)
|
||||||
|
|
||||||
def get_device_name(self, mac):
|
def get_device_name(self, mac):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
try:
|
try:
|
||||||
return next(device.name for device in self.last_results
|
return next(device.name for device in self.last_results
|
||||||
if device.mac == mac)
|
if device.mac == mac)
|
||||||
@ -83,8 +79,8 @@ class NetgearDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Retrieve latest information from the Netgear router.
|
||||||
Retrieves latest information from the Netgear router.
|
|
||||||
Returns boolean if scanning successful.
|
Returns boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.nmap
|
Support for scanning a network with nmap.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a network with nmap.
|
|
||||||
|
|
||||||
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.nmap_scanner/
|
https://home-assistant.io/components/device_tracker.nmap_scanner/
|
||||||
@ -30,7 +28,7 @@ REQUIREMENTS = ['python-nmap==0.4.3']
|
|||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Nmap scanner. """
|
"""Validate the configuration and return a Nmap scanner."""
|
||||||
if not validate_config(config, {DOMAIN: [CONF_HOSTS]},
|
if not validate_config(config, {DOMAIN: [CONF_HOSTS]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
@ -58,6 +56,7 @@ class NmapDeviceScanner(object):
|
|||||||
"""This class scans for devices using nmap."""
|
"""This class scans for devices using nmap."""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.last_results = []
|
self.last_results = []
|
||||||
|
|
||||||
self.hosts = config[CONF_HOSTS]
|
self.hosts = config[CONF_HOSTS]
|
||||||
@ -68,17 +67,13 @@ class NmapDeviceScanner(object):
|
|||||||
_LOGGER.info("nmap scanner initialized")
|
_LOGGER.info("nmap scanner initialized")
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return [device.mac for device in self.last_results]
|
return [device.mac for device in self.last_results]
|
||||||
|
|
||||||
def get_device_name(self, mac):
|
def get_device_name(self, mac):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
|
|
||||||
filter_named = [device.name for device in self.last_results
|
filter_named = [device.name for device in self.last_results
|
||||||
if device.mac == mac]
|
if device.mac == mac]
|
||||||
|
|
||||||
@ -89,8 +84,8 @@ class NmapDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Scan the network for devices.
|
||||||
Scans the network for devices.
|
|
||||||
Returns boolean if scanning successful.
|
Returns boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
_LOGGER.info("Scanning")
|
_LOGGER.info("Scanning")
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.owntracks
|
Support the OwnTracks platform.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
OwnTracks platform for the device tracker.
|
|
||||||
|
|
||||||
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.owntracks/
|
https://home-assistant.io/components/device_tracker.owntracks/
|
||||||
@ -28,13 +26,15 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
|
|
||||||
|
CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy'
|
||||||
|
|
||||||
|
|
||||||
def setup_scanner(hass, config, see):
|
def setup_scanner(hass, config, see):
|
||||||
"""Setup an OwnTracks tracker."""
|
"""Setup an OwnTracks tracker."""
|
||||||
|
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY)
|
||||||
|
|
||||||
def owntracks_location_update(topic, payload, qos):
|
def owntracks_location_update(topic, payload, qos):
|
||||||
"""MQTT message received."""
|
"""MQTT message received."""
|
||||||
|
|
||||||
# Docs on available data:
|
# Docs on available data:
|
||||||
# http://owntracks.org/booklet/tech/json/#_typelocation
|
# http://owntracks.org/booklet/tech/json/#_typelocation
|
||||||
try:
|
try:
|
||||||
@ -45,7 +45,9 @@ def setup_scanner(hass, config, see):
|
|||||||
'Unable to parse payload as JSON: %s', payload)
|
'Unable to parse payload as JSON: %s', payload)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not isinstance(data, dict) or data.get('_type') != 'location':
|
if (not isinstance(data, dict) or data.get('_type') != 'location') or (
|
||||||
|
'acc' in data and max_gps_accuracy is not None and data[
|
||||||
|
'acc'] > max_gps_accuracy):
|
||||||
return
|
return
|
||||||
|
|
||||||
dev_id, kwargs = _parse_see_args(topic, data)
|
dev_id, kwargs = _parse_see_args(topic, data)
|
||||||
@ -64,7 +66,6 @@ def setup_scanner(hass, config, see):
|
|||||||
def owntracks_event_update(topic, payload, qos):
|
def owntracks_event_update(topic, payload, qos):
|
||||||
# pylint: disable=too-many-branches, too-many-statements
|
# pylint: disable=too-many-branches, too-many-statements
|
||||||
"""MQTT event (geofences) received."""
|
"""MQTT event (geofences) received."""
|
||||||
|
|
||||||
# Docs on available data:
|
# Docs on available data:
|
||||||
# http://owntracks.org/booklet/tech/json/#_typetransition
|
# http://owntracks.org/booklet/tech/json/#_typetransition
|
||||||
try:
|
try:
|
||||||
@ -124,12 +125,20 @@ def setup_scanner(hass, config, see):
|
|||||||
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 to %s", new_region)
|
_LOGGER.info("Exit to %s", new_region)
|
||||||
|
see(**kwargs)
|
||||||
|
see_beacons(dev_id, kwargs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.info("Exit to GPS")
|
_LOGGER.info("Exit to GPS")
|
||||||
|
# Check for GPS accuracy
|
||||||
|
if not ('acc' in data and
|
||||||
|
max_gps_accuracy is not None and
|
||||||
|
data['acc'] > max_gps_accuracy):
|
||||||
|
|
||||||
see(**kwargs)
|
see(**kwargs)
|
||||||
see_beacons(dev_id, kwargs)
|
see_beacons(dev_id, kwargs)
|
||||||
|
else:
|
||||||
|
_LOGGER.info("Inaccurate GPS reported")
|
||||||
|
|
||||||
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
|
beacons = MOBILE_BEACONS_ACTIVE[dev_id]
|
||||||
if location in beacons:
|
if location in beacons:
|
||||||
@ -143,8 +152,7 @@ def setup_scanner(hass, config, see):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def see_beacons(dev_id, kwargs_param):
|
def see_beacons(dev_id, kwargs_param):
|
||||||
""" 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
|
# the battery state applies to the tracking device, not the beacon
|
||||||
kwargs.pop('battery', None)
|
kwargs.pop('battery', None)
|
||||||
@ -154,16 +162,13 @@ def setup_scanner(hass, config, see):
|
|||||||
see(**kwargs)
|
see(**kwargs)
|
||||||
|
|
||||||
mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1)
|
mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1)
|
||||||
|
|
||||||
mqtt.subscribe(hass, EVENT_TOPIC, owntracks_event_update, 1)
|
mqtt.subscribe(hass, EVENT_TOPIC, owntracks_event_update, 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _parse_see_args(topic, data):
|
def _parse_see_args(topic, data):
|
||||||
""" Parse the OwnTracks location parameters,
|
"""Parse the OwnTracks location parameters, into the format see expects."""
|
||||||
into the format see expects. """
|
|
||||||
|
|
||||||
parts = topic.split('/')
|
parts = topic.split('/')
|
||||||
dev_id = '{}_{}'.format(parts[1], parts[2])
|
dev_id = '{}_{}'.format(parts[1], parts[2])
|
||||||
host_name = parts[1]
|
host_name = parts[1]
|
||||||
@ -180,8 +185,7 @@ def _parse_see_args(topic, data):
|
|||||||
|
|
||||||
|
|
||||||
def _set_gps_from_zone(kwargs, zone):
|
def _set_gps_from_zone(kwargs, zone):
|
||||||
""" Set the see parameters from the zone parameters """
|
"""Set the see parameters from the zone parameters."""
|
||||||
|
|
||||||
if zone is not None:
|
if zone is not None:
|
||||||
kwargs['gps'] = (
|
kwargs['gps'] = (
|
||||||
zone.attributes['latitude'],
|
zone.attributes['latitude'],
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.snmp
|
Support for fetching WiFi associations through SNMP.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports fetching WiFi associations
|
|
||||||
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/
|
||||||
@ -17,7 +14,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -29,7 +26,7 @@ CONF_BASEOID = "baseoid"
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns an snmp scanner """
|
"""Validate the configuration and return an snmp scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]},
|
{DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -41,10 +38,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class SnmpScanner(object):
|
class SnmpScanner(object):
|
||||||
"""
|
"""Queries any SNMP capable Access Point for connected devices."""
|
||||||
This class queries any SNMP capable Acces Point for connected devices.
|
|
||||||
"""
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
from pysnmp.entity.rfc3413.oneliner import cmdgen
|
from pysnmp.entity.rfc3413.oneliner import cmdgen
|
||||||
self.snmp = cmdgen.CommandGenerator()
|
self.snmp = cmdgen.CommandGenerator()
|
||||||
|
|
||||||
@ -61,25 +58,23 @@ class SnmpScanner(object):
|
|||||||
self.success_init = data is not None
|
self.success_init = data is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device IDs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return [client['mac'] for client in self.last_results]
|
return [client['mac'] for client in self.last_results
|
||||||
|
if client.get('mac')]
|
||||||
|
|
||||||
# Supressing no-self-use warning
|
# Supressing no-self-use warning
|
||||||
# pylint: disable=R0201
|
# pylint: disable=R0201
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
# We have no names
|
# We have no names
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the WAP is up to date.
|
||||||
Ensures the information from the WAP is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
@ -93,8 +88,7 @@ class SnmpScanner(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_snmp_data(self):
|
def get_snmp_data(self):
|
||||||
""" Fetch mac addresses from WAP via SNMP. """
|
"""Fetch MAC addresses from WAP via SNMP."""
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
|
|
||||||
errindication, errstatus, errindex, restable = self.snmp.nextCmd(
|
errindication, errstatus, errindex, restable = self.snmp.nextCmd(
|
||||||
@ -111,6 +105,7 @@ class SnmpScanner(object):
|
|||||||
for resrow in restable:
|
for resrow in restable:
|
||||||
for _, val in resrow:
|
for _, val in resrow:
|
||||||
mac = binascii.hexlify(val.asOctets()).decode('utf-8')
|
mac = binascii.hexlify(val.asOctets()).decode('utf-8')
|
||||||
|
_LOGGER.debug('Found mac %s', mac)
|
||||||
mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)])
|
mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)])
|
||||||
devices.append({'mac': mac})
|
devices.append({'mac': mac})
|
||||||
return devices
|
return devices
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.thomson
|
Support for THOMSON routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a THOMSON router for device
|
|
||||||
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.thomson/
|
https://home-assistant.io/components/device_tracker.thomson/
|
||||||
@ -18,7 +15,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -35,7 +32,7 @@ _DEVICES_REGEX = re.compile(
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a THOMSON scanner. """
|
"""Validate the configuration and return a THOMSON scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -47,12 +44,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class ThomsonDeviceScanner(object):
|
class ThomsonDeviceScanner(object):
|
||||||
"""
|
"""This class queries a router running THOMSON firmware."""
|
||||||
This class queries a router running THOMSON firmware
|
|
||||||
for connected devices. Adapted from ASUSWRT scanner.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.host = config[CONF_HOST]
|
self.host = config[CONF_HOST]
|
||||||
self.username = config[CONF_USERNAME]
|
self.username = config[CONF_USERNAME]
|
||||||
self.password = config[CONF_PASSWORD]
|
self.password = config[CONF_PASSWORD]
|
||||||
@ -61,20 +56,17 @@ class ThomsonDeviceScanner(object):
|
|||||||
|
|
||||||
self.last_results = {}
|
self.last_results = {}
|
||||||
|
|
||||||
# Test the router is accessible
|
# Test the router is accessible.
|
||||||
data = self.get_thomson_data()
|
data = self.get_thomson_data()
|
||||||
self.success_init = data is not None
|
self.success_init = data is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
""" Scans for new devices and return a
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
list containing found device ids. """
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return [client['mac'] for client in self.last_results]
|
return [client['mac'] for client in self.last_results]
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device
|
"""Return the name of the given device or None if we don't know."""
|
||||||
or None if we don't know. """
|
|
||||||
if not self.last_results:
|
if not self.last_results:
|
||||||
return None
|
return None
|
||||||
for client in self.last_results:
|
for client in self.last_results:
|
||||||
@ -84,9 +76,9 @@ class ThomsonDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the THOMSON router is up to date.
|
||||||
Ensures the information from the THOMSON router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Return boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
return False
|
return False
|
||||||
@ -97,7 +89,7 @@ class ThomsonDeviceScanner(object):
|
|||||||
if not data:
|
if not data:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# flag C stands for CONNECTED
|
# Flag C stands for CONNECTED
|
||||||
active_clients = [client for client in data.values() if
|
active_clients = [client for client in data.values() if
|
||||||
client['status'].find('C') != -1]
|
client['status'].find('C') != -1]
|
||||||
self.last_results = active_clients
|
self.last_results = active_clients
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.tomato
|
Support for Tomato routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a Tomato router for device
|
|
||||||
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/
|
||||||
@ -20,7 +17,7 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
CONF_HTTP_ID = "http_id"
|
CONF_HTTP_ID = "http_id"
|
||||||
@ -29,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Tomato scanner. """
|
"""Validate the configuration and returns a Tomato scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME,
|
{DOMAIN: [CONF_HOST, CONF_USERNAME,
|
||||||
CONF_PASSWORD, CONF_HTTP_ID]},
|
CONF_PASSWORD, CONF_HTTP_ID]},
|
||||||
@ -40,14 +37,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class TomatoDeviceScanner(object):
|
class TomatoDeviceScanner(object):
|
||||||
""" This class queries a wireless router running Tomato firmware
|
"""This class queries a wireless router running Tomato firmware."""
|
||||||
for connected devices.
|
|
||||||
|
|
||||||
A description of the Tomato API can be found on
|
|
||||||
http://paulusschoutsen.nl/blog/2013/10/tomato-api-documentation/
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
host, http_id = config[CONF_HOST], config[CONF_HTTP_ID]
|
host, http_id = config[CONF_HOST], config[CONF_HTTP_ID]
|
||||||
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
@ -68,16 +61,13 @@ class TomatoDeviceScanner(object):
|
|||||||
self.success_init = self._update_tomato_info()
|
self.success_init = self._update_tomato_info()
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
""" Scans for new devices and return a
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
list containing found device ids. """
|
|
||||||
|
|
||||||
self._update_tomato_info()
|
self._update_tomato_info()
|
||||||
|
|
||||||
return [item[1] for item in self.last_results['wldev']]
|
return [item[1] for item in self.last_results['wldev']]
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
|
|
||||||
filter_named = [item[0] for item in self.last_results['dhcpd_lease']
|
filter_named = [item[0] for item in self.last_results['dhcpd_lease']
|
||||||
if item[2] == device]
|
if item[2] == device]
|
||||||
|
|
||||||
@ -88,19 +78,17 @@ class TomatoDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_tomato_info(self):
|
def _update_tomato_info(self):
|
||||||
""" Ensures the information from the Tomato router is up to date.
|
"""Ensure the information from the Tomato router is up to date.
|
||||||
Returns boolean if scanning successful. """
|
|
||||||
|
|
||||||
|
Return boolean if scanning successful.
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.logger.info("Scanning")
|
self.logger.info("Scanning")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.Session().send(self.req, timeout=3)
|
response = requests.Session().send(self.req, timeout=3)
|
||||||
|
|
||||||
# Calling and parsing the Tomato api here. We only need the
|
# Calling and parsing the Tomato api here. We only need the
|
||||||
# wldev and dhcpd_lease values. For API description see:
|
# wldev and dhcpd_lease values.
|
||||||
# http://paulusschoutsen.nl/
|
|
||||||
# blog/2013/10/tomato-api-documentation/
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
|
|
||||||
for param, value in \
|
for param, value in \
|
||||||
@ -109,7 +97,6 @@ class TomatoDeviceScanner(object):
|
|||||||
if param == 'wldev' or param == 'dhcpd_lease':
|
if param == 'wldev' or param == 'dhcpd_lease':
|
||||||
self.last_results[param] = \
|
self.last_results[param] = \
|
||||||
json.loads(value.replace("'", '"'))
|
json.loads(value.replace("'", '"'))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif response.status_code == 401:
|
elif response.status_code == 401:
|
||||||
@ -117,29 +104,25 @@ class TomatoDeviceScanner(object):
|
|||||||
self.logger.exception((
|
self.logger.exception((
|
||||||
"Failed to authenticate, "
|
"Failed to authenticate, "
|
||||||
"please check your username and password"))
|
"please check your username and password"))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
# We get this if we could not connect to the router or
|
# We get this if we could not connect to the router or
|
||||||
# an invalid http_id was supplied
|
# an invalid http_id was supplied.
|
||||||
self.logger.exception((
|
self.logger.exception((
|
||||||
"Failed to connect to the router"
|
"Failed to connect to the router"
|
||||||
" or invalid http_id supplied"))
|
" or invalid http_id supplied"))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
# We get this if we could not connect to the router or
|
# We get this if we could not connect to the router or
|
||||||
# an invalid http_id was supplied
|
# an invalid http_id was supplied.
|
||||||
self.logger.exception(
|
self.logger.exception(
|
||||||
"Connection to the router timed out")
|
"Connection to the router timed out")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# If json decoder could not parse the response
|
# If JSON decoder could not parse the response.
|
||||||
self.logger.exception(
|
self.logger.exception(
|
||||||
"Failed to parse response from router")
|
"Failed to parse response from router")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.tplink
|
Support for TP-Link routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a TP-Link router for device
|
|
||||||
presence.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.tplink/
|
https://home-assistant.io/components/device_tracker.tplink/
|
||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
@ -27,12 +25,15 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a TP-Link scanner. """
|
"""Validate the configuration and return a TP-Link scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
scanner = Tplink4DeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
|
if not scanner.success_init:
|
||||||
scanner = Tplink3DeviceScanner(config[DOMAIN])
|
scanner = Tplink3DeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
if not scanner.success_init:
|
if not scanner.success_init:
|
||||||
@ -45,12 +46,10 @@ def get_scanner(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class TplinkDeviceScanner(object):
|
class TplinkDeviceScanner(object):
|
||||||
"""
|
"""This class queries a wireless router running TP-Link firmware."""
|
||||||
This class queries a wireless router running TP-Link firmware
|
|
||||||
for connected devices.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
host = config[CONF_HOST]
|
host = config[CONF_HOST]
|
||||||
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
@ -66,29 +65,21 @@ class TplinkDeviceScanner(object):
|
|||||||
self.success_init = self._update_info()
|
self.success_init = self._update_info()
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return self.last_results
|
return self.last_results
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
"""
|
"""The firmware doesn't save the name of the wireless device."""
|
||||||
The TP-Link firmware doesn't save the name of the wireless device.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the TP-Link router is up to date.
|
||||||
Ensures the information from the TP-Link router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
Return boolean if scanning successful.
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
_LOGGER.info("Loading wireless clients...")
|
_LOGGER.info("Loading wireless clients...")
|
||||||
|
|
||||||
@ -107,34 +98,24 @@ class TplinkDeviceScanner(object):
|
|||||||
|
|
||||||
|
|
||||||
class Tplink2DeviceScanner(TplinkDeviceScanner):
|
class Tplink2DeviceScanner(TplinkDeviceScanner):
|
||||||
"""
|
"""This class queries a router with newer version of TP-Link firmware."""
|
||||||
This class queries a wireless router running newer version of TP-Link
|
|
||||||
firmware for connected devices.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return self.last_results.keys()
|
return self.last_results.keys()
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
"""
|
"""The firmware doesn't save the name of the wireless device."""
|
||||||
The TP-Link firmware doesn't save the name of the wireless device.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return self.last_results.get(device)
|
return self.last_results.get(device)
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the TP-Link router is up to date.
|
||||||
Ensures the information from the TP-Link router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
Return boolean if scanning successful.
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
_LOGGER.info("Loading wireless clients...")
|
_LOGGER.info("Loading wireless clients...")
|
||||||
|
|
||||||
@ -172,46 +153,36 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
|
|||||||
|
|
||||||
|
|
||||||
class Tplink3DeviceScanner(TplinkDeviceScanner):
|
class Tplink3DeviceScanner(TplinkDeviceScanner):
|
||||||
"""
|
"""This class queries the Archer C9 router with version 150811 or high."""
|
||||||
This class queries the Archer C9 router running version 150811 or higher
|
|
||||||
of TP-Link firmware for connected devices.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self.stok = ''
|
self.stok = ''
|
||||||
self.sysauth = ''
|
self.sysauth = ''
|
||||||
super(Tplink3DeviceScanner, self).__init__(config)
|
super(Tplink3DeviceScanner, self).__init__(config)
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
return self.last_results.keys()
|
return self.last_results.keys()
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
"""
|
"""The firmware doesn't save the name of the wireless device.
|
||||||
The TP-Link firmware doesn't save the name of the wireless device.
|
|
||||||
We are forced to use the MAC address as name here.
|
We are forced to use the MAC address as name here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.last_results.get(device)
|
return self.last_results.get(device)
|
||||||
|
|
||||||
def _get_auth_tokens(self):
|
def _get_auth_tokens(self):
|
||||||
"""
|
"""Retrieve auth tokens from the router."""
|
||||||
Retrieves auth tokens from the router.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LOGGER.info("Retrieving auth tokens...")
|
_LOGGER.info("Retrieving auth tokens...")
|
||||||
|
|
||||||
url = 'http://{}/cgi-bin/luci/;stok=/login?form=login' \
|
url = 'http://{}/cgi-bin/luci/;stok=/login?form=login' \
|
||||||
.format(self.host)
|
.format(self.host)
|
||||||
referer = 'http://{}/webpages/login.html'.format(self.host)
|
referer = 'http://{}/webpages/login.html'.format(self.host)
|
||||||
|
|
||||||
# if possible implement rsa encryption of password here
|
# If possible implement rsa encryption of password here.
|
||||||
|
|
||||||
response = requests.post(url,
|
response = requests.post(url,
|
||||||
params={'operation': 'login',
|
params={'operation': 'login',
|
||||||
'username': self.username,
|
'username': self.username,
|
||||||
@ -232,11 +203,10 @@ class Tplink3DeviceScanner(TplinkDeviceScanner):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the TP-Link router is up to date.
|
||||||
Ensures the information from the TP-Link router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
Return boolean if scanning successful.
|
||||||
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if (self.stok == '') or (self.sysauth == ''):
|
if (self.stok == '') or (self.sysauth == ''):
|
||||||
self._get_auth_tokens()
|
self._get_auth_tokens()
|
||||||
@ -281,3 +251,81 @@ class Tplink3DeviceScanner(TplinkDeviceScanner):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Tplink4DeviceScanner(TplinkDeviceScanner):
|
||||||
|
"""This class queries an Archer C7 router with TP-Link firmware 150427."""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
|
self.credentials = ''
|
||||||
|
self.token = ''
|
||||||
|
super(Tplink4DeviceScanner, self).__init__(config)
|
||||||
|
|
||||||
|
def scan_devices(self):
|
||||||
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
|
self._update_info()
|
||||||
|
return self.last_results
|
||||||
|
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
def get_device_name(self, device):
|
||||||
|
"""The firmware doesn't save the name of the wireless device."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_auth_tokens(self):
|
||||||
|
"""Retrieve auth tokens from the router."""
|
||||||
|
_LOGGER.info("Retrieving auth tokens...")
|
||||||
|
url = 'http://{}/userRpm/LoginRpm.htm?Save=Save'.format(self.host)
|
||||||
|
|
||||||
|
# Generate md5 hash of password
|
||||||
|
password = hashlib.md5(self.password.encode('utf')).hexdigest()
|
||||||
|
credentials = '{}:{}'.format(self.username, password).encode('utf')
|
||||||
|
|
||||||
|
# Encode the credentials to be sent as a cookie.
|
||||||
|
self.credentials = base64.b64encode(credentials).decode('utf')
|
||||||
|
|
||||||
|
# Create the authorization cookie.
|
||||||
|
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
||||||
|
|
||||||
|
response = requests.get(url, headers={'cookie': cookie})
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = re.search(r'window.parent.location.href = '
|
||||||
|
r'"https?:\/\/.*\/(.*)\/userRpm\/Index.htm";',
|
||||||
|
response.text)
|
||||||
|
if not result:
|
||||||
|
return False
|
||||||
|
self.token = result.group(1)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.error("Couldn't fetch auth tokens!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
|
def _update_info(self):
|
||||||
|
"""Ensure the information from the TP-Link router is up to date.
|
||||||
|
|
||||||
|
Return boolean if scanning successful.
|
||||||
|
"""
|
||||||
|
with self.lock:
|
||||||
|
if (self.credentials == '') or (self.token == ''):
|
||||||
|
self._get_auth_tokens()
|
||||||
|
|
||||||
|
_LOGGER.info("Loading wireless clients...")
|
||||||
|
|
||||||
|
url = 'http://{}/{}/userRpm/WlanStationRpm.htm' \
|
||||||
|
.format(self.host, self.token)
|
||||||
|
referer = 'http://{}'.format(self.host)
|
||||||
|
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
||||||
|
|
||||||
|
page = requests.get(url, headers={
|
||||||
|
'cookie': cookie,
|
||||||
|
'referer': referer
|
||||||
|
})
|
||||||
|
result = self.parse_macs.findall(page.text)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.last_results = [mac.replace("-", ":") for mac in result]
|
||||||
|
return True
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.ubus
|
Support for OpenWRT (ubus) routers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a OpenWRT router for device
|
|
||||||
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/
|
||||||
@ -20,14 +17,14 @@ 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
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Luci scanner. """
|
"""Validate the configuration and return an ubus scanner."""
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
@ -41,23 +38,13 @@ def get_scanner(hass, config):
|
|||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class UbusDeviceScanner(object):
|
class UbusDeviceScanner(object):
|
||||||
"""
|
"""
|
||||||
This class queries a wireless router running OpenWrt firmware
|
This class queries a wireless router running OpenWrt firmware.
|
||||||
for connected devices. Adapted from Tomato scanner.
|
|
||||||
|
|
||||||
Configure your routers' ubus ACL based on following instructions:
|
|
||||||
|
|
||||||
http://wiki.openwrt.org/doc/techref/ubus
|
|
||||||
|
|
||||||
Read only access will be fine.
|
|
||||||
|
|
||||||
To use this class you have to install rpcd-mod-file package
|
|
||||||
in your OpenWrt router:
|
|
||||||
|
|
||||||
opkg install rpcd-mod-file
|
|
||||||
|
|
||||||
|
Adapted from Tomato scanner.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""Initialize the scanner."""
|
||||||
host = config[CONF_HOST]
|
host = config[CONF_HOST]
|
||||||
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
@ -73,17 +60,12 @@ class UbusDeviceScanner(object):
|
|||||||
self.success_init = self.session_id is not None
|
self.success_init = self.session_id is not None
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
"""
|
"""Scan for new devices and return a list with found device IDs."""
|
||||||
Scans for new devices and return a list containing found device ids.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._update_info()
|
self._update_info()
|
||||||
|
|
||||||
return self.last_results
|
return self.last_results
|
||||||
|
|
||||||
def get_device_name(self, device):
|
def get_device_name(self, device):
|
||||||
""" Returns the name of the given device or None if we don't know. """
|
"""Return the name of the given device or None if we don't know."""
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if self.leasefile is None:
|
if self.leasefile is None:
|
||||||
result = _req_json_rpc(self.url, self.session_id,
|
result = _req_json_rpc(self.url, self.session_id,
|
||||||
@ -112,8 +94,8 @@ class UbusDeviceScanner(object):
|
|||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
"""
|
"""Ensure the information from the Luci router is up to date.
|
||||||
Ensures the information from the Luci router is up to date.
|
|
||||||
Returns boolean if scanning successful.
|
Returns boolean if scanning successful.
|
||||||
"""
|
"""
|
||||||
if not self.success_init:
|
if not self.success_init:
|
||||||
@ -142,7 +124,6 @@ class UbusDeviceScanner(object):
|
|||||||
|
|
||||||
def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params):
|
def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params):
|
||||||
"""Perform one JSON RPC operation."""
|
"""Perform one JSON RPC operation."""
|
||||||
|
|
||||||
data = json.dumps({"jsonrpc": "2.0",
|
data = json.dumps({"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": rpcmethod,
|
"method": rpcmethod,
|
||||||
@ -167,7 +148,7 @@ def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params):
|
|||||||
|
|
||||||
|
|
||||||
def _get_session_id(url, username, password):
|
def _get_session_id(url, username, password):
|
||||||
""" Get authentication token for the given host+username+password. """
|
"""Get the authentication token for the given host+username+password."""
|
||||||
res = _req_json_rpc(url, "00000000000000000000000000000000", 'call',
|
res = _req_json_rpc(url, "00000000000000000000000000000000", 'call',
|
||||||
'session', 'login', username=username,
|
'session', 'login', username=username,
|
||||||
password=password)
|
password=password)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.unifi
|
Support for Unifi WAP controllers.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Device tracker platform that supports scanning a Unifi WAP controller
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.unifi/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
import urllib
|
||||||
@ -17,7 +18,7 @@ CONF_PORT = 'port'
|
|||||||
|
|
||||||
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Sets up unifi device_tracker """
|
"""Setup Unifi device_tracker."""
|
||||||
from unifi.controller import Controller
|
from unifi.controller import Controller
|
||||||
|
|
||||||
if not validate_config(config, {DOMAIN: [CONF_USERNAME,
|
if not validate_config(config, {DOMAIN: [CONF_USERNAME,
|
||||||
@ -50,10 +51,12 @@ class UnifiScanner(object):
|
|||||||
"""Provide device_tracker support from Unifi WAP client data."""
|
"""Provide device_tracker support from Unifi WAP client data."""
|
||||||
|
|
||||||
def __init__(self, controller):
|
def __init__(self, controller):
|
||||||
|
"""Initialize the scanner."""
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
|
"""Get the clients from the device."""
|
||||||
try:
|
try:
|
||||||
clients = self._controller.get_clients()
|
clients = self._controller.get_clients()
|
||||||
except urllib.error.HTTPError as ex:
|
except urllib.error.HTTPError as ex:
|
||||||
@ -63,12 +66,12 @@ class UnifiScanner(object):
|
|||||||
self._clients = {client['mac']: client for client in clients}
|
self._clients = {client['mac']: client for client in clients}
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
""" Scans for devices. """
|
"""Scan for devices."""
|
||||||
self._update()
|
self._update()
|
||||||
return self._clients.keys()
|
return self._clients.keys()
|
||||||
|
|
||||||
def get_device_name(self, mac):
|
def get_device_name(self, mac):
|
||||||
""" Returns the name (if known) of the device.
|
"""Return the name (if known) of the device.
|
||||||
|
|
||||||
If a name has been set in Unifi, then return that, else
|
If a name has been set in Unifi, then return that, else
|
||||||
return the hostname if it has been detected.
|
return the hostname if it has been detected.
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.const import (
|
|||||||
EVENT_PLATFORM_DISCOVERED)
|
EVENT_PLATFORM_DISCOVERED)
|
||||||
|
|
||||||
DOMAIN = "discovery"
|
DOMAIN = "discovery"
|
||||||
REQUIREMENTS = ['netdisco==0.5.2']
|
REQUIREMENTS = ['netdisco==0.5.4']
|
||||||
|
|
||||||
SCAN_INTERVAL = 300 # seconds
|
SCAN_INTERVAL = 300 # seconds
|
||||||
|
|
||||||
@ -55,10 +55,7 @@ def listen(hass, service, callback):
|
|||||||
|
|
||||||
|
|
||||||
def discover(hass, service, discovered=None, component=None, hass_config=None):
|
def discover(hass, service, discovered=None, component=None, hass_config=None):
|
||||||
"""Fire discovery event.
|
"""Fire discovery event. Can ensure a component is loaded."""
|
||||||
|
|
||||||
Can ensure a component is loaded.
|
|
||||||
"""
|
|
||||||
if component is not None:
|
if component is not None:
|
||||||
bootstrap.setup_component(hass, component, hass_config)
|
bootstrap.setup_component(hass, component, hass_config)
|
||||||
|
|
||||||
@ -73,7 +70,7 @@ def discover(hass, service, discovered=None, component=None, hass_config=None):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Starts a discovery service. """
|
"""Start a discovery service."""
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from netdisco.service import DiscoveryService
|
from netdisco.service import DiscoveryService
|
||||||
@ -90,7 +87,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
component = SERVICE_HANDLERS.get(service)
|
component = SERVICE_HANDLERS.get(service)
|
||||||
|
|
||||||
# We do not know how to handle this service
|
# We do not know how to handle this service.
|
||||||
if not component:
|
if not component:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.downloader
|
Support for functionality to download files.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
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/
|
||||||
@ -28,8 +26,7 @@ CONF_DOWNLOAD_DIR = 'download_dir'
|
|||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Listens for download events to download files. """
|
"""Listen for download events to download files."""
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if not validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger):
|
if not validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger):
|
||||||
@ -50,14 +47,13 @@ def setup(hass, config):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def download_file(service):
|
def download_file(service):
|
||||||
""" Starts thread to download file specified in the url. """
|
"""Start thread to download file specified in the URL."""
|
||||||
|
|
||||||
if ATTR_URL not in service.data:
|
if ATTR_URL not in service.data:
|
||||||
logger.error("Service called but 'url' parameter not specified.")
|
logger.error("Service called but 'url' parameter not specified.")
|
||||||
return
|
return
|
||||||
|
|
||||||
def do_download():
|
def do_download():
|
||||||
""" Downloads the file. """
|
"""Download the file."""
|
||||||
try:
|
try:
|
||||||
url = service.data[ATTR_URL]
|
url = service.data[ATTR_URL]
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.ecobee
|
Support for Ecobee.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Ecobee component
|
|
||||||
|
|
||||||
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/ecobee/
|
https://home-assistant.io/components/ecobee/
|
||||||
@ -31,7 +29,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
ECOBEE_CONFIG_FILE = 'ecobee.conf'
|
ECOBEE_CONFIG_FILE = 'ecobee.conf'
|
||||||
_CONFIGURING = {}
|
_CONFIGURING = {}
|
||||||
|
|
||||||
# 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_UPDATES = timedelta(seconds=180)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=180)
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ def request_configuration(network, hass, config):
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def ecobee_configuration_callback(callback_data):
|
def ecobee_configuration_callback(callback_data):
|
||||||
""" Actions to do when our configuration callback is called. """
|
"""The actions to do when our configuration callback is called."""
|
||||||
network.request_tokens()
|
network.request_tokens()
|
||||||
network.update()
|
network.update()
|
||||||
setup_ecobee(hass, network, config)
|
setup_ecobee(hass, network, config)
|
||||||
@ -93,9 +91,10 @@ def setup_ecobee(hass, network, config):
|
|||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
class EcobeeData(object):
|
class EcobeeData(object):
|
||||||
""" Gets the latest data and update the states. """
|
"""Get the latest data and update the states."""
|
||||||
|
|
||||||
def __init__(self, config_file):
|
def __init__(self, config_file):
|
||||||
|
"""Initialize the Ecobee data object."""
|
||||||
from pyecobee import Ecobee
|
from pyecobee import Ecobee
|
||||||
self.ecobee = Ecobee(config_file)
|
self.ecobee = Ecobee(config_file)
|
||||||
|
|
||||||
@ -107,8 +106,8 @@ class EcobeeData(object):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""
|
"""Setup Ecobee.
|
||||||
Setup Ecobee.
|
|
||||||
Will automatically load thermostat and sensor components to support
|
Will automatically load thermostat and sensor components to support
|
||||||
devices discovered on the network.
|
devices discovered on the network.
|
||||||
"""
|
"""
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
"""
|
"""Handle the frontend for Home Assistant."""
|
||||||
homeassistant.components.frontend
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Provides a frontend for Home Assistant.
|
|
||||||
"""
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@ -58,7 +53,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_api_bootstrap(handler, path_match, data):
|
def _handle_get_api_bootstrap(handler, path_match, data):
|
||||||
""" Returns all data needed to bootstrap Home Assistant. """
|
"""Return all data needed to bootstrap Home Assistant."""
|
||||||
hass = handler.server.hass
|
hass = handler.server.hass
|
||||||
|
|
||||||
handler.write_json({
|
handler.write_json({
|
||||||
@ -70,7 +65,7 @@ def _handle_get_api_bootstrap(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_root(handler, path_match, data):
|
def _handle_get_root(handler, path_match, data):
|
||||||
""" Renders the frontend. """
|
"""Render the frontend."""
|
||||||
handler.send_response(HTTP_OK)
|
handler.send_response(HTTP_OK)
|
||||||
handler.send_header('Content-type', 'text/html; charset=utf-8')
|
handler.send_header('Content-type', 'text/html; charset=utf-8')
|
||||||
handler.end_headers()
|
handler.end_headers()
|
||||||
@ -95,7 +90,7 @@ def _handle_get_root(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_service_worker(handler, path_match, data):
|
def _handle_get_service_worker(handler, path_match, data):
|
||||||
""" Returns service worker for the frontend. """
|
"""Return service worker for the frontend."""
|
||||||
if handler.server.development:
|
if handler.server.development:
|
||||||
sw_path = "home-assistant-polymer/build/service_worker.js"
|
sw_path = "home-assistant-polymer/build/service_worker.js"
|
||||||
else:
|
else:
|
||||||
@ -106,7 +101,7 @@ def _handle_get_service_worker(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_static(handler, path_match, data):
|
def _handle_get_static(handler, path_match, data):
|
||||||
""" Returns a static file for the frontend. """
|
"""Return a static file for the frontend."""
|
||||||
req_file = util.sanitize_path(path_match.group('file'))
|
req_file = util.sanitize_path(path_match.group('file'))
|
||||||
|
|
||||||
# Strip md5 hash out
|
# Strip md5 hash out
|
||||||
@ -120,9 +115,7 @@ def _handle_get_static(handler, path_match, data):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_get_local(handler, path_match, data):
|
def _handle_get_local(handler, path_match, data):
|
||||||
"""
|
"""Return a static file from the hass.config.path/www for the frontend."""
|
||||||
Returns a static file from the hass.config.path/www for the frontend.
|
|
||||||
"""
|
|
||||||
req_file = util.sanitize_path(path_match.group('file'))
|
req_file = util.sanitize_path(path_match.group('file'))
|
||||||
|
|
||||||
path = handler.server.hass.config.path('www', req_file)
|
path = handler.server.hass.config.path('www', req_file)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by update_mdi script """
|
"""DO NOT MODIFY. Auto-generated by update_mdi script."""
|
||||||
VERSION = "2f4adc5d3ad6d2f73bf69ed29b7594fd"
|
VERSION = "e85dc66e1a0730e44f79ed11501cd79a"
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
"""DO NOT MODIFY. Auto-generated by build_frontend script."""
|
||||||
VERSION = "a4d021cb50ed079fcfda7369ed2f0d4a"
|
VERSION = "30bcc0eacc13a2317000824741dc9ac0"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
Subproject commit 81ae753eb06a32bcac62cbee0981b1d24580e878
|
Subproject commit 0fed700045d6faba8eda8ec713ee9e6bc763507c
|
File diff suppressed because one or more lines are too long
@ -33,13 +33,13 @@ _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."""
|
"""Return 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."""
|
"""Close all or a 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)
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ def setup(hass, config):
|
|||||||
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."""
|
"""Handle 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:
|
||||||
@ -81,7 +81,8 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class GarageDoorDevice(Entity):
|
class GarageDoorDevice(Entity):
|
||||||
"""Represents a garage door."""
|
"""Representation of a garage door."""
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def is_closed(self):
|
||||||
@ -98,7 +99,7 @@ class GarageDoorDevice(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Returns the state of the garage door."""
|
"""Return 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
|
||||||
|
@ -19,7 +19,9 @@ 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):
|
||||||
|
"""Initialize the garage door."""
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = state
|
self._state = state
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ 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 garage door platform."""
|
"""Setup the Wink garage door platform."""
|
||||||
import pywink
|
import pywink
|
||||||
|
|
||||||
if discovery_info is None:
|
if discovery_info is None:
|
||||||
@ -32,19 +32,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
class WinkGarageDoorDevice(GarageDoorDevice):
|
class WinkGarageDoorDevice(GarageDoorDevice):
|
||||||
"""Represents a Wink garage door."""
|
"""Representation of a Wink garage door."""
|
||||||
|
|
||||||
def __init__(self, wink):
|
def __init__(self, wink):
|
||||||
|
"""Initialize the garage door."""
|
||||||
self.wink = wink
|
self.wink = wink
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Returns the id of this wink garage door."""
|
"""Return 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."""
|
"""Return the name of the garage door if any."""
|
||||||
return self.wink.name()
|
return self.wink.name()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
@ -53,11 +54,11 @@ class WinkGarageDoorDevice(GarageDoorDevice):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def is_closed(self):
|
||||||
"""Returns true if door is closed."""
|
"""Return true if door is closed."""
|
||||||
return self.wink.state() == 0
|
return self.wink.state() == 0
|
||||||
|
|
||||||
def close_door(self):
|
def close_door(self):
|
||||||
"""Closes the door."""
|
"""Close the door."""
|
||||||
self.wink.set_state(0)
|
self.wink.set_state(0)
|
||||||
|
|
||||||
def open_door(self):
|
def open_door(self):
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Component that records all events and state changes and feeds the data to
|
Component that sends data to aGraphite installation.
|
||||||
a Graphite installation.
|
|
||||||
|
|
||||||
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/graphite/
|
https://home-assistant.io/components/graphite/
|
||||||
@ -35,8 +34,10 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class GraphiteFeeder(threading.Thread):
|
class GraphiteFeeder(threading.Thread):
|
||||||
"""Feeds data to Graphite."""
|
"""Feed data to Graphite."""
|
||||||
|
|
||||||
def __init__(self, hass, host, port, prefix):
|
def __init__(self, hass, host, port, prefix):
|
||||||
|
"""Initialize the feeder."""
|
||||||
super(GraphiteFeeder, self).__init__(daemon=True)
|
super(GraphiteFeeder, self).__init__(daemon=True)
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._host = host
|
self._host = host
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.group
|
Provides functionality to group entities.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provides functionality to group devices that can be turned on or off.
|
|
||||||
|
|
||||||
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/group/
|
https://home-assistant.io/components/group/
|
||||||
"""
|
"""
|
||||||
|
import threading
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
|
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
|
||||||
@ -41,7 +41,7 @@ def _get_group_on_off(state):
|
|||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id):
|
def is_on(hass, entity_id):
|
||||||
""" Returns if the group state is in its ON-state. """
|
"""Test if the group state is in its ON-state."""
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
@ -54,8 +54,7 @@ def is_on(hass, entity_id):
|
|||||||
|
|
||||||
|
|
||||||
def expand_entity_ids(hass, entity_ids):
|
def expand_entity_ids(hass, entity_ids):
|
||||||
""" Returns the given list of entity ids and expands group ids into
|
"""Return entity_ids with group entity ids replaced by their members."""
|
||||||
the entity ids it represents if found. """
|
|
||||||
found_ids = []
|
found_ids = []
|
||||||
|
|
||||||
for entity_id in entity_ids:
|
for entity_id in entity_ids:
|
||||||
@ -86,7 +85,7 @@ def expand_entity_ids(hass, entity_ids):
|
|||||||
|
|
||||||
|
|
||||||
def get_entity_ids(hass, entity_id, domain_filter=None):
|
def get_entity_ids(hass, entity_id, domain_filter=None):
|
||||||
""" Get the entity ids that make up this group. """
|
"""Get members of this group."""
|
||||||
entity_id = entity_id.lower()
|
entity_id = entity_id.lower()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -107,7 +106,7 @@ def get_entity_ids(hass, entity_id, domain_filter=None):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up all groups found definded in the configuration. """
|
"""Setup all groups found definded in the configuration."""
|
||||||
for object_id, conf in config.get(DOMAIN, {}).items():
|
for object_id, conf in config.get(DOMAIN, {}).items():
|
||||||
if not isinstance(conf, dict):
|
if not isinstance(conf, dict):
|
||||||
conf = {CONF_ENTITIES: conf}
|
conf = {CONF_ENTITIES: conf}
|
||||||
@ -127,12 +126,12 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class Group(Entity):
|
class Group(Entity):
|
||||||
""" Tracks a group of entity ids. """
|
"""Track a group of entity ids."""
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||||
|
|
||||||
def __init__(self, hass, name, entity_ids=None, user_defined=True,
|
def __init__(self, hass, name, entity_ids=None, user_defined=True,
|
||||||
icon=None, view=False, object_id=None):
|
icon=None, view=False, object_id=None):
|
||||||
|
"""Initialize a group."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._name = name
|
self._name = name
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
@ -146,6 +145,7 @@ class Group(Entity):
|
|||||||
self.group_on = None
|
self.group_on = None
|
||||||
self.group_off = None
|
self.group_off = None
|
||||||
self._assumed_state = False
|
self._assumed_state = False
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
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)
|
||||||
@ -154,26 +154,32 @@ class Group(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
|
"""No need to poll because groups will update themselves."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
"""Return the name of the group."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
|
"""Return the state of the group."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
|
"""Return the icon of the group."""
|
||||||
return self._icon
|
return self._icon
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hidden(self):
|
def hidden(self):
|
||||||
|
"""If group should be hidden or not."""
|
||||||
return not self._user_defined or self._view
|
return not self._user_defined or self._view
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
|
"""Return the state attributes for the group."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: self.tracking,
|
ATTR_ENTITY_ID: self.tracking,
|
||||||
ATTR_ORDER: self._order,
|
ATTR_ORDER: self._order,
|
||||||
@ -186,11 +192,11 @@ class Group(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def assumed_state(self):
|
def assumed_state(self):
|
||||||
"""Return True if unable to access real state of entity."""
|
"""Test if any member has an assumed state."""
|
||||||
return self._assumed_state
|
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 member entity IDs."""
|
||||||
self.stop()
|
self.stop()
|
||||||
self.tracking = tuple(ent_id.lower() for ent_id in entity_ids)
|
self.tracking = tuple(ent_id.lower() for ent_id in entity_ids)
|
||||||
self.group_on, self.group_off = None, None
|
self.group_on, self.group_off = None, None
|
||||||
@ -200,30 +206,30 @@ class Group(Entity):
|
|||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
""" Starts the tracking. """
|
"""Start tracking members."""
|
||||||
track_state_change(
|
track_state_change(
|
||||||
self.hass, self.tracking, self._state_changed_listener)
|
self.hass, self.tracking, self._state_changed_listener)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
""" Unregisters the group from Home Assistant. """
|
"""Unregister the group from Home Assistant."""
|
||||||
self.hass.states.remove(self.entity_id)
|
self.hass.states.remove(self.entity_id)
|
||||||
|
|
||||||
self.hass.bus.remove_listener(
|
self.hass.bus.remove_listener(
|
||||||
ha.EVENT_STATE_CHANGED, self._state_changed_listener)
|
ha.EVENT_STATE_CHANGED, self._state_changed_listener)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Query all the tracked states and determine current group state. """
|
"""Query all members and determine current group state."""
|
||||||
self._state = STATE_UNKNOWN
|
self._state = STATE_UNKNOWN
|
||||||
self._update_group_state()
|
self._update_group_state()
|
||||||
|
|
||||||
def _state_changed_listener(self, entity_id, old_state, new_state):
|
def _state_changed_listener(self, entity_id, old_state, new_state):
|
||||||
""" Listener to receive state changes of tracked entities. """
|
"""Respond to a member state changing."""
|
||||||
self._update_group_state(new_state)
|
self._update_group_state(new_state)
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _tracking_states(self):
|
def _tracking_states(self):
|
||||||
"""States that the group is tracking."""
|
"""The states that the group is tracking."""
|
||||||
states = []
|
states = []
|
||||||
|
|
||||||
for entity_id in self.tracking:
|
for entity_id in self.tracking:
|
||||||
@ -242,8 +248,11 @@ class Group(Entity):
|
|||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
# To store current states of group entities. Might not be needed.
|
# To store current states of group entities. Might not be needed.
|
||||||
|
with self._lock:
|
||||||
states = None
|
states = None
|
||||||
gr_state, gr_on, gr_off = self._state, self.group_on, self.group_off
|
gr_state = self._state
|
||||||
|
gr_on = self.group_on
|
||||||
|
gr_off = self.group_off
|
||||||
|
|
||||||
# We have not determined type of group yet
|
# We have not determined type of group yet
|
||||||
if gr_on is None:
|
if gr_on is None:
|
||||||
@ -283,8 +292,9 @@ class Group(Entity):
|
|||||||
if states is None:
|
if states is None:
|
||||||
states = self._tracking_states
|
states = self._tracking_states
|
||||||
|
|
||||||
self._assumed_state = any(state.attributes.get(ATTR_ASSUMED_STATE)
|
self._assumed_state = any(
|
||||||
for state in states)
|
state.attributes.get(ATTR_ASSUMED_STATE) for state
|
||||||
|
in states)
|
||||||
|
|
||||||
elif tr_state.attributes.get(ATTR_ASSUMED_STATE):
|
elif tr_state.attributes.get(ATTR_ASSUMED_STATE):
|
||||||
self._assumed_state = True
|
self._assumed_state = True
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.history
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Provide pre-made queries on top of the recorder component.
|
Provide pre-made queries on top of the recorder component.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
@ -11,7 +9,7 @@ from collections import defaultdict
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
import homeassistant.components.recorder as recorder
|
from homeassistant.components import recorder, script
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.const import HTTP_BAD_REQUEST
|
from homeassistant.const import HTTP_BAD_REQUEST
|
||||||
|
|
||||||
@ -19,6 +17,7 @@ DOMAIN = 'history'
|
|||||||
DEPENDENCIES = ['recorder', 'http']
|
DEPENDENCIES = ['recorder', 'http']
|
||||||
|
|
||||||
SIGNIFICANT_DOMAINS = ('thermostat',)
|
SIGNIFICANT_DOMAINS = ('thermostat',)
|
||||||
|
IGNORE_DOMAINS = ('zone', 'scene',)
|
||||||
|
|
||||||
URL_HISTORY_PERIOD = re.compile(
|
URL_HISTORY_PERIOD = re.compile(
|
||||||
r'/api/history/period(?:/(?P<date>\d{4}-\d{1,2}-\d{1,2})|)')
|
r'/api/history/period(?:/(?P<date>\d{4}-\d{1,2}-\d{1,2})|)')
|
||||||
@ -38,17 +37,18 @@ def last_5_states(entity_id):
|
|||||||
|
|
||||||
|
|
||||||
def get_significant_states(start_time, end_time=None, entity_id=None):
|
def get_significant_states(start_time, end_time=None, entity_id=None):
|
||||||
"""Return states changes during UTC period start_time - end_time.
|
"""
|
||||||
|
Return states changes during UTC period start_time - end_time.
|
||||||
|
|
||||||
Significant states are all states where there is a state change,
|
Significant states are all states where there is a state change,
|
||||||
as well as all states from certain domains (for instance
|
as well as all states from certain domains (for instance
|
||||||
thermostat so that we get current temperature in our graphs).
|
thermostat so that we get current temperature in our graphs).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
where = """
|
where = """
|
||||||
(domain in ({}) or last_changed=last_updated)
|
(domain IN ({}) OR last_changed=last_updated)
|
||||||
AND last_updated > ?
|
AND domain NOT IN ({}) AND last_updated > ?
|
||||||
""".format(",".join(["'%s'" % x for x in SIGNIFICANT_DOMAINS]))
|
""".format(",".join("'%s'" % x for x in SIGNIFICANT_DOMAINS),
|
||||||
|
",".join("'%s'" % x for x in IGNORE_DOMAINS))
|
||||||
|
|
||||||
data = [start_time]
|
data = [start_time]
|
||||||
|
|
||||||
@ -63,15 +63,14 @@ def get_significant_states(start_time, end_time=None, entity_id=None):
|
|||||||
query = ("SELECT * FROM states WHERE {} "
|
query = ("SELECT * FROM states WHERE {} "
|
||||||
"ORDER BY entity_id, last_updated ASC").format(where)
|
"ORDER BY entity_id, last_updated ASC").format(where)
|
||||||
|
|
||||||
states = recorder.query_states(query, data)
|
states = (state for state in recorder.query_states(query, data)
|
||||||
|
if _is_significant(state))
|
||||||
|
|
||||||
return states_to_json(states, start_time, entity_id)
|
return states_to_json(states, start_time, entity_id)
|
||||||
|
|
||||||
|
|
||||||
def state_changes_during_period(start_time, end_time=None, entity_id=None):
|
def state_changes_during_period(start_time, end_time=None, entity_id=None):
|
||||||
"""
|
"""Return states changes during UTC period start_time - end_time."""
|
||||||
Return states changes during UTC period start_time - end_time.
|
|
||||||
"""
|
|
||||||
where = "last_changed=last_updated AND last_changed > ? "
|
where = "last_changed=last_updated AND last_changed > ? "
|
||||||
data = [start_time]
|
data = [start_time]
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ def state_changes_during_period(start_time, end_time=None, entity_id=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_states(utc_point_in_time, entity_ids=None, run=None):
|
def get_states(utc_point_in_time, entity_ids=None, run=None):
|
||||||
""" Returns the states at a specific point in time. """
|
"""Return the states at a specific point in time."""
|
||||||
if run is None:
|
if run is None:
|
||||||
run = recorder.run_information(utc_point_in_time)
|
run = recorder.run_information(utc_point_in_time)
|
||||||
|
|
||||||
@ -121,7 +120,7 @@ def get_states(utc_point_in_time, entity_ids=None, run=None):
|
|||||||
|
|
||||||
|
|
||||||
def states_to_json(states, start_time, entity_id):
|
def states_to_json(states, start_time, entity_id):
|
||||||
"""Converts SQL results into JSON friendly data structure.
|
"""Convert SQL results into JSON friendly data structure.
|
||||||
|
|
||||||
This takes our state list and turns it into a JSON friendly data
|
This takes our state list and turns it into a JSON friendly data
|
||||||
structure {'entity_id': [list of states], 'entity_id2': [list of states]}
|
structure {'entity_id': [list of states], 'entity_id2': [list of states]}
|
||||||
@ -130,7 +129,6 @@ def states_to_json(states, start_time, entity_id):
|
|||||||
each list of states, otherwise our graphs won't start on the Y
|
each list of states, otherwise our graphs won't start on the Y
|
||||||
axis correctly.
|
axis correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = defaultdict(list)
|
result = defaultdict(list)
|
||||||
|
|
||||||
entity_ids = [entity_id] if entity_id is not None else None
|
entity_ids = [entity_id] if entity_id is not None else None
|
||||||
@ -156,7 +154,7 @@ def get_state(utc_point_in_time, entity_id, run=None):
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup history hooks. """
|
"""Setup the history hooks."""
|
||||||
hass.http.register_path(
|
hass.http.register_path(
|
||||||
'GET',
|
'GET',
|
||||||
re.compile(
|
re.compile(
|
||||||
@ -200,3 +198,13 @@ def _api_history_period(handler, path_match, data):
|
|||||||
|
|
||||||
handler.write_json(
|
handler.write_json(
|
||||||
get_significant_states(start_time, end_time, entity_id).values())
|
get_significant_states(start_time, end_time, entity_id).values())
|
||||||
|
|
||||||
|
|
||||||
|
def _is_significant(state):
|
||||||
|
"""Test if state is significant for history charts.
|
||||||
|
|
||||||
|
Will only test for things that are not filtered out in SQL.
|
||||||
|
"""
|
||||||
|
# scripts that are not cancellable will never change state
|
||||||
|
return (state.domain != 'script' or
|
||||||
|
state.attributes.get(script.ATTR_CAN_CANCEL))
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.http
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
This module provides an API and a HTTP interface for debug purposes.
|
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
|
||||||
@ -52,7 +50,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up the HTTP API and debug interface. """
|
"""Set up the HTTP API and debug interface."""
|
||||||
conf = config.get(DOMAIN, {})
|
conf = config.get(DOMAIN, {})
|
||||||
|
|
||||||
api_password = util.convert(conf.get(CONF_API_PASSWORD), str)
|
api_password = util.convert(conf.get(CONF_API_PASSWORD), str)
|
||||||
@ -88,14 +86,15 @@ def setup(hass, config):
|
|||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
"""Handle HTTP requests in a threaded fashion."""
|
"""Handle HTTP requests in a threaded fashion."""
|
||||||
# pylint: disable=too-few-public-methods
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
allow_reuse_address = True
|
allow_reuse_address = True
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def __init__(self, server_address, request_handler_class,
|
def __init__(self, server_address, request_handler_class,
|
||||||
hass, api_password, development, ssl_certificate, ssl_key):
|
hass, api_password, development, ssl_certificate, ssl_key):
|
||||||
|
"""Initialize the server."""
|
||||||
super().__init__(server_address, request_handler_class)
|
super().__init__(server_address, request_handler_class)
|
||||||
|
|
||||||
self.server_address = server_address
|
self.server_address = server_address
|
||||||
@ -119,9 +118,9 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
self.socket = context.wrap_socket(self.socket, server_side=True)
|
self.socket = context.wrap_socket(self.socket, server_side=True)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
""" Starts the HTTP server. """
|
"""Start the HTTP server."""
|
||||||
def stop_http(event):
|
def stop_http(event):
|
||||||
""" Stops the HTTP server. """
|
"""Stop the HTTP server."""
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http)
|
self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http)
|
||||||
@ -140,19 +139,18 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
self.serve_forever()
|
self.serve_forever()
|
||||||
|
|
||||||
def register_path(self, method, url, callback, require_auth=True):
|
def register_path(self, method, url, callback, require_auth=True):
|
||||||
""" Registers a path with the server. """
|
"""Register a path with the server."""
|
||||||
self.paths.append((method, url, callback, require_auth))
|
self.paths.append((method, url, callback, require_auth))
|
||||||
|
|
||||||
def log_message(self, fmt, *args):
|
def log_message(self, fmt, *args):
|
||||||
""" Redirect built-in log to HA logging """
|
"""Redirect built-in log to HA logging."""
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
_LOGGER.info(fmt, *args)
|
_LOGGER.info(fmt, *args)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods,too-many-locals
|
# pylint: disable=too-many-public-methods,too-many-locals
|
||||||
class RequestHandler(SimpleHTTPRequestHandler):
|
class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
"""
|
"""Handle incoming HTTP requests.
|
||||||
Handles incoming HTTP requests
|
|
||||||
|
|
||||||
We extend from SimpleHTTPRequestHandler instead of Base so we
|
We extend from SimpleHTTPRequestHandler instead of Base so we
|
||||||
can use the guess content type methods.
|
can use the guess content type methods.
|
||||||
@ -161,13 +159,13 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
server_version = "HomeAssistant/1.0"
|
server_version = "HomeAssistant/1.0"
|
||||||
|
|
||||||
def __init__(self, req, client_addr, server):
|
def __init__(self, req, client_addr, server):
|
||||||
""" Contructor, call the base constructor and set up session """
|
"""Constructor, call the base constructor and set up session."""
|
||||||
# Track if this was an authenticated request
|
# Track if this was an authenticated request
|
||||||
self.authenticated = False
|
self.authenticated = False
|
||||||
SimpleHTTPRequestHandler.__init__(self, req, client_addr, server)
|
SimpleHTTPRequestHandler.__init__(self, req, client_addr, server)
|
||||||
|
|
||||||
def log_message(self, fmt, *arguments):
|
def log_message(self, fmt, *arguments):
|
||||||
""" Redirect built-in log to HA logging """
|
"""Redirect built-in log to HA logging."""
|
||||||
if self.server.api_password is None:
|
if self.server.api_password is None:
|
||||||
_LOGGER.info(fmt, *arguments)
|
_LOGGER.info(fmt, *arguments)
|
||||||
else:
|
else:
|
||||||
@ -176,7 +174,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
if isinstance(arg, str) else arg for arg in arguments))
|
if isinstance(arg, str) else arg for arg in arguments))
|
||||||
|
|
||||||
def _handle_request(self, method): # pylint: disable=too-many-branches
|
def _handle_request(self, method): # pylint: disable=too-many-branches
|
||||||
""" Does some common checks and calls appropriate method. """
|
"""Perform some common checks and call appropriate method."""
|
||||||
url = urlparse(self.path)
|
url = urlparse(self.path)
|
||||||
|
|
||||||
# Read query input. parse_qs gives a list for each value, we want last
|
# Read query input. parse_qs gives a list for each value, we want last
|
||||||
@ -306,7 +304,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
self.wfile.write(message.encode("UTF-8"))
|
self.wfile.write(message.encode("UTF-8"))
|
||||||
|
|
||||||
def write_file(self, path, cache_headers=True):
|
def write_file(self, path, cache_headers=True):
|
||||||
""" Returns a file to the user. """
|
"""Return a file to the user."""
|
||||||
try:
|
try:
|
||||||
with open(path, 'rb') as inp:
|
with open(path, 'rb') as inp:
|
||||||
self.write_file_pointer(self.guess_type(path), inp,
|
self.write_file_pointer(self.guess_type(path), inp,
|
||||||
@ -318,10 +316,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
_LOGGER.exception("Unable to serve %s", path)
|
_LOGGER.exception("Unable to serve %s", path)
|
||||||
|
|
||||||
def write_file_pointer(self, content_type, inp, cache_headers=True):
|
def write_file_pointer(self, content_type, inp, cache_headers=True):
|
||||||
"""
|
"""Helper function to write a file pointer to the user."""
|
||||||
Helper function to write a file pointer to the user.
|
|
||||||
Does not do error handling.
|
|
||||||
"""
|
|
||||||
do_gzip = 'gzip' in self.headers.get(HTTP_HEADER_ACCEPT_ENCODING, '')
|
do_gzip = 'gzip' in self.headers.get(HTTP_HEADER_ACCEPT_ENCODING, '')
|
||||||
|
|
||||||
self.send_response(HTTP_OK)
|
self.send_response(HTTP_OK)
|
||||||
@ -354,7 +349,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
self.copyfile(inp, self.wfile)
|
self.copyfile(inp, self.wfile)
|
||||||
|
|
||||||
def set_cache_header(self):
|
def set_cache_header(self):
|
||||||
""" Add cache headers if not in development """
|
"""Add cache headers if not in development."""
|
||||||
if self.server.development:
|
if self.server.development:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -369,7 +364,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
self.date_time_string(time.time()+cache_time))
|
self.date_time_string(time.time()+cache_time))
|
||||||
|
|
||||||
def set_session_cookie_header(self):
|
def set_session_cookie_header(self):
|
||||||
""" Add the header for the session cookie and return session id. """
|
"""Add the header for the session cookie and return session ID."""
|
||||||
if not self.authenticated:
|
if not self.authenticated:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -391,9 +386,9 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
return self.get_cookie_session_id() is not None
|
return self.get_cookie_session_id() is not None
|
||||||
|
|
||||||
def get_cookie_session_id(self):
|
def get_cookie_session_id(self):
|
||||||
"""
|
"""Extract the current session ID from the cookie.
|
||||||
Extracts the current session id from the
|
|
||||||
cookie or returns None if not set or invalid
|
Return None if not set or invalid.
|
||||||
"""
|
"""
|
||||||
if 'Cookie' not in self.headers:
|
if 'Cookie' not in self.headers:
|
||||||
return None
|
return None
|
||||||
@ -417,7 +412,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def destroy_session(self):
|
def destroy_session(self):
|
||||||
""" Destroys session. """
|
"""Destroy the session."""
|
||||||
session_id = self.get_cookie_session_id()
|
session_id = self.get_cookie_session_id()
|
||||||
|
|
||||||
if session_id is None:
|
if session_id is None:
|
||||||
@ -433,9 +428,10 @@ def session_valid_time():
|
|||||||
|
|
||||||
|
|
||||||
class SessionStore(object):
|
class SessionStore(object):
|
||||||
""" Responsible for storing and retrieving http sessions """
|
"""Responsible for storing and retrieving HTTP sessions."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
""" Set up the session store """
|
"""Setup the session store."""
|
||||||
self._sessions = {}
|
self._sessions = {}
|
||||||
self._lock = threading.RLock()
|
self._lock = threading.RLock()
|
||||||
|
|
||||||
@ -468,7 +464,7 @@ class SessionStore(object):
|
|||||||
self._sessions.pop(key, None)
|
self._sessions.pop(key, None)
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
""" Creates a new session. """
|
"""Create a new session."""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
session_id = util.get_random_string(20)
|
session_id = util.get_random_string(20)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.ifttt
|
Support to trigger Maker IFTTT recipes.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
This component enable you to trigger Maker IFTTT recipes.
|
|
||||||
|
|
||||||
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/ifttt/
|
https://home-assistant.io/components/ifttt/
|
||||||
@ -38,15 +36,14 @@ def trigger(hass, event, value1=None, value2=None, value3=None):
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup the ifttt service component. """
|
"""Setup the IFTTT service component."""
|
||||||
|
|
||||||
if not validate_config(config, {DOMAIN: ['key']}, _LOGGER):
|
if not validate_config(config, {DOMAIN: ['key']}, _LOGGER):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
key = config[DOMAIN]['key']
|
key = config[DOMAIN]['key']
|
||||||
|
|
||||||
def trigger_service(call):
|
def trigger_service(call):
|
||||||
""" Handle ifttt trigger service calls. """
|
"""Handle IFTTT trigger service calls."""
|
||||||
event = call.data.get(ATTR_EVENT)
|
event = call.data.get(ATTR_EVENT)
|
||||||
value1 = call.data.get(ATTR_VALUE1)
|
value1 = call.data.get(ATTR_VALUE1)
|
||||||
value2 = call.data.get(ATTR_VALUE2)
|
value2 = call.data.get(ATTR_VALUE2)
|
||||||
|
@ -31,8 +31,10 @@ CONF_USERNAME = 'username'
|
|||||||
CONF_PASSWORD = 'password'
|
CONF_PASSWORD = 'password'
|
||||||
CONF_SSL = 'ssl'
|
CONF_SSL = 'ssl'
|
||||||
CONF_VERIFY_SSL = 'verify_ssl'
|
CONF_VERIFY_SSL = 'verify_ssl'
|
||||||
|
CONF_BLACKLIST = 'blacklist'
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
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
|
||||||
@ -52,6 +54,7 @@ def setup(hass, config):
|
|||||||
ssl = util.convert(conf.get(CONF_SSL), bool, DEFAULT_SSL)
|
ssl = util.convert(conf.get(CONF_SSL), bool, DEFAULT_SSL)
|
||||||
verify_ssl = util.convert(conf.get(CONF_VERIFY_SSL), bool,
|
verify_ssl = util.convert(conf.get(CONF_VERIFY_SSL), bool,
|
||||||
DEFAULT_VERIFY_SSL)
|
DEFAULT_VERIFY_SSL)
|
||||||
|
blacklist = conf.get(CONF_BLACKLIST, [])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
influx = InfluxDBClient(host=host, port=port, username=username,
|
influx = InfluxDBClient(host=host, port=port, username=username,
|
||||||
@ -67,7 +70,8 @@ def setup(hass, config):
|
|||||||
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, '') \
|
||||||
|
or state.entity_id in blacklist:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.input_boolean
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Component to keep track of user controlled booleans for within automation.
|
Component to keep track of user controlled booleans for within automation.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation
|
For more details about this component, please refer to the documentation
|
||||||
@ -86,7 +84,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class InputBoolean(ToggleEntity):
|
class InputBoolean(ToggleEntity):
|
||||||
""" Represent a boolean input. """
|
"""Representation of a boolean input."""
|
||||||
|
|
||||||
def __init__(self, object_id, name, state, icon):
|
def __init__(self, object_id, name, state, icon):
|
||||||
"""Initialize a boolean input."""
|
"""Initialize a boolean input."""
|
||||||
@ -97,22 +95,22 @@ class InputBoolean(ToggleEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
"""If entitiy should be polled."""
|
"""If entity should be polled."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Name of the boolean input."""
|
"""Return name of the boolean input."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Icon to be used for this entity."""
|
"""Returh the icon to be used for this entity."""
|
||||||
return self._icon
|
return self._icon
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""True if entity is on."""
|
"""Return true if entity is on."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.input_select
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Component to offer a way to select an option from a list.
|
Component to offer a way to select an option from a list.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation
|
For more details about this component, please refer to the documentation
|
||||||
@ -92,7 +90,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class InputSelect(Entity):
|
class InputSelect(Entity):
|
||||||
""" Represent a select input. """
|
"""Representation of a select input."""
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def __init__(self, object_id, name, state, options, icon):
|
def __init__(self, object_id, name, state, options, icon):
|
||||||
@ -110,22 +108,22 @@ class InputSelect(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Name of the select input. """
|
"""Return the name of the select input."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
""" Icon to be used for this entity. """
|
"""Return the icon to be used for this entity."""
|
||||||
return self._icon
|
return self._icon
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" State of the component. """
|
"""Return the state of the component."""
|
||||||
return self._current_option
|
return self._current_option
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" State attributes. """
|
"""Return the state attributes."""
|
||||||
return {
|
return {
|
||||||
ATTR_OPTIONS: self._options,
|
ATTR_OPTIONS: self._options,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.insteon_hub
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Support for Insteon Hub.
|
Support for Insteon Hub.
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
@ -24,8 +22,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""
|
"""Setup Insteon Hub component.
|
||||||
Setup Insteon Hub component.
|
|
||||||
This will automatically import associated lights.
|
This will automatically import associated lights.
|
||||||
"""
|
"""
|
||||||
if not validate_config(
|
if not validate_config(
|
||||||
@ -58,20 +56,21 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
class InsteonToggleDevice(ToggleEntity):
|
class InsteonToggleDevice(ToggleEntity):
|
||||||
""" Abstract Class for an Insteon node. """
|
"""An abstract Class for an Insteon node."""
|
||||||
|
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
|
"""Initialize the device."""
|
||||||
self.node = node
|
self.node = node
|
||||||
self._value = 0
|
self._value = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the node. """
|
"""Return the the name of the node."""
|
||||||
return self.node.DeviceName
|
return self.node.DeviceName
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
""" Returns the id of this insteon node. """
|
"""Return the ID of this insteon node."""
|
||||||
return self.node.DeviceID
|
return self.node.DeviceID
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
@ -84,11 +83,13 @@ class InsteonToggleDevice(ToggleEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" Returns boolean response if the node is on. """
|
"""Return the boolean response if the node is on."""
|
||||||
return self._value != 0
|
return self._value != 0
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
|
"""Turn device on."""
|
||||||
self.node.send_command('on')
|
self.node.send_command('on')
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
def turn_off(self, **kwargs):
|
||||||
|
"""Turn device off."""
|
||||||
self.node.send_command('off')
|
self.node.send_command('off')
|
||||||
|
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