Compare commits

...

441 Commits

Author SHA1 Message Date
Paulus Schoutsen e9f273e7e0 Merge pull request #7866 from home-assistant/release-0-46
0.46
2017-06-03 19:16:35 -07:00
Paulus Schoutsen 7ebf36bb70 Fix MQTT camera test (#7878) 2017-06-03 18:57:05 -07:00
Anders Melchiorsen 4dc4a98caa [light.lifx] Update aiolifx (#7882)
This makes LIFX Gen3 lights work with the current firmware.
2017-06-03 13:21:31 +01:00
Paulus Schoutsen a79f1d4d40 Fix telegram_bot (#7877) 2017-06-03 10:52:00 +01:00
Paulus Schoutsen a33bcdf270 Version bump to 0.46 2017-06-02 00:24:19 -07:00
Paulus Schoutsen f056cbc641 Update frontend 2017-06-02 00:20:53 -07:00
Paulus Schoutsen 4163bcebbc Update netdisco (#7865) 2017-06-02 00:13:17 -07:00
Johan Bloemberg d472d81538 Align switch group handling with light. (#7577) 2017-06-02 00:05:34 -07:00
David-Leon Pohl 2b70b1881a Quickfix Bug #7384 (#7582)
* Quickfix Bug #7384

* Fix devices not available runtime bug
2017-06-02 00:05:07 -07:00
Juggels 12607aeaea Check if media commands are actually applicable (#7595)
* Check if media commands are actually applicable

- Explicitly allow ‘stop’ and ‘play’ on radio streams
- Disallow media commands when the playlist is empty
- Check if command is supported when calling `turn_on` and `turn_off`

* Suppress UPnP error 701 on media commands

* Clean up soco_filter_upnperror

Clean up soco_filter_upnperror and fix small bug in support_previous_track determination
2017-06-02 00:03:10 -07:00
Erik Eriksson 1855f1ae85 fix for https://github.com/home-assistant/home-assistant/issues/7019 (#7618) 2017-06-02 00:02:26 -07:00
Thibault Cohen 613da308f2 Query in InfluxDB sensor is now templatable (#7634) 2017-06-02 00:01:14 -07:00
Teagan Glenn cefacf9ce4 Spotify aliases (#7702)
* Alias support for spotify devices

* Fix log

* Formatting/Fixes

* Remove default arg

* Add default keyword

* None check
2017-06-01 23:53:23 -07:00
Alex Harvey 78887c5d5c Start of migration framework, to allow moving of files in the config … (#7740)
* Start of migration framework, to allow moving of files in the config directory to be hidden, ios.conf used as the first one to undergo this change.

* Update const.py

* Update test_config.py

* improvement to syntax
2017-06-01 23:50:04 -07:00
Craig J. Ward 3a92bd78ea fix permissions issue for Insteon Local #6558 (#7860)
* fix unlinked commit

* Update insteon_local.py
2017-06-01 23:36:47 -07:00
Paulus Schoutsen d0021a6171 Make monkey patch work in Python 3.6 (#7848)
* Make monkey patch work in Python 3.6

* Update dockerfiles back to 3.6

* Lint

* Do not set env variable for dockerfile

* Lint
2017-06-01 23:23:39 -07:00
Anders Melchiorsen e2cfdbff06 Disallow ambiguous color descriptors in the light.turn_on schema (#7765)
* Disallow ambiguous color descriptors in the light.turn_on schema

* Update tests
2017-06-01 23:05:05 -07:00
abmantis 9480f41210 dont use default for switch name, so that the object id is used (#7845) 2017-06-01 22:58:57 -07:00
Boris K 1b5f6aa1b9 Optimize history_stats efficiency and database usage (#7858) 2017-06-01 22:52:55 -07:00
Eugenio Panadero 2065426b16 log time delay of domain setup in info level (#7808)
* log time delay of domain setup in info level

 * when setup problems appear, it's difficult to debug which are the components that took a lot to set up. This minimal change goes further than the 'slow setup warning' and measures the setup time interval for each domain.

* use timer as in helpers/entity
2017-06-01 22:44:44 -07:00
Adam Mills beb8c05d91 Use expected behvaior for above/below (#7857) 2017-06-01 22:43:24 -07:00
Adam Mills cf42303afb Rename time trigger 'after' to 'at' (#7846) 2017-06-01 22:40:27 -07:00
Daniel Perna 4bcbeef480 Bumped pyhomematic version (#7861) 2017-06-01 22:33:53 -07:00
Adam Mills e0712ba329 Expose the node name on the zwave node entity (#7787) 2017-06-01 22:33:16 -07:00
Fabian Affolter 66d6f5174d Allow 'base_url' (fixes #7784) (#7796) 2017-05-31 09:08:53 -07:00
Marcelo Moreira de Mello 9762e1613d Introduced support to Netgear Arlo Cameras (#7826)
*  Introduced support to Netgear Arlo Cameras

* Using async_setup_platform() and applied other changes

* Removed unecessary variables

* Using asyncio for sensor/arlo

* Update arlo.py

* Removed entity_namespace
2017-05-31 09:25:25 +02:00
Phil Hawthorne bb92ef5497 Downgrade Docker to Python 3.5 to solve Segmentation Faults (#7799)
Downgrades the Dockerfiles used by Home Assistant to Python 3.5, after
Python 3.6 base image was causing segmentation faults.

See home-assistant/home-assistant#7752
2017-05-30 23:56:20 -07:00
Paulus Schoutsen 9f5bfe28d1 Add initial benchmark framework (#7827)
* Add initial benchmark framework

* Use timer from timeit
2017-05-30 21:34:40 -07:00
Marcelo Moreira de Mello 8ee32a8fbd Added persistent error message if cover.myq fails to load (#7700)
* Show persistent error if cover.myq fails

* Fixed typo on getLogger()

* Added ValueError on except condition

* Make pylint happy

* Removed DEFAULT_ENTITY_NAMESPACE since it is not being used
2017-05-30 23:17:32 +02:00
Fabian Affolter 052cd3fc53 Upgrade PyMVGLive to 1.1.4 (#7832) 2017-05-30 18:26:26 +02:00
Fabian Affolter 0ccaf97924 Update docstrings and log messages (#7709) 2017-05-30 11:52:26 +02:00
happyleavesaoc 96b20b3a97 update snapcast media player (#7079)
* update snapcast

* fix docstrings

* bump dep version

* address snapcast review comments

* add snapcast group volume support

* fix snapcast requirements

* update snapcast client entity id

* snapshot/restore functions

* refactor snapshot/restore services

* clean up

* update snapcast req

* bump version

* fix async updates
2017-05-30 11:34:39 +02:00
Daniel Høyer Iversen 91806bfa2a Flux led fix (#7829)
* Update flux_led.py

* style fix
2017-05-30 10:46:18 +02:00
Fabian Affolter 1c4e097bed Upgrade pysnmp to 4.3.7 (#7828) 2017-05-30 09:08:57 +02:00
Pascal Vizeli 2df6aabbf3 Cleanup telegram / Add url to webhook (#7824)
* Cleanup telegram / Add url to webhook

* fix lint

* Fix lint
2017-05-30 06:55:06 +02:00
John Mihalic 81b2111751 Bump aiohttp to 2.1.0 (#7825) 2017-05-30 06:54:16 +02:00
Eugenio Panadero f7e0d13fe6 Telegram send image: fix mimetype detection (#7802)
* Add `name` var to BytesIO content to get recognized

Sometimes the python-telegram-bot doesn't recognize the mimetype of the file and looks after a name variable to deduce it. Fixes #7413

* bytesio stream recycle less explicit
2017-05-29 22:59:44 +02:00
Johan Bloemberg 5e5c0daa87 Allow configuring DSMR5 protocol. (#7535)
* Allow configuring DSMR5 protocol.

* Give good example.

* Using dev branch until released upstream.

* Update to dsmr_parser supporting v5 arguments.
2017-05-29 16:19:50 +02:00
Fabian Affolter a7277db4d7 Upgrade mypy to 0.511 (#7809)
Add an optional extended description…
2017-05-29 15:39:24 +02:00
Fabian Affolter ba44b7edb3 Upgrade sqlalchemy to 1.1.10 (#7807) 2017-05-29 15:38:56 +02:00
Lev Aronsky 8fcc750998 Added handling of an AssertionError from pxssh failed login (#7750)
* Added handling of an AssertionError from pxssh failed login

* Destory and re-create pxssh instance, to fix behavior upon router restart.
2017-05-29 11:22:20 +02:00
Teagan Glenn eff619a58f Rest notify data (#7757)
* Rest notify data

* Cleanup

* Fix spaces
2017-05-29 11:20:23 +02:00
Andy Castille fc1bb58247 Rachio (Sprinklers) (#7600)
* Rachio platform started

* Rachio tests

* detect bad api token

* Documentation, Code cleanup

* Docstrings end with a period, log uses %

* Fix arguments, default run time is now 10 minutes

* Fix typo, remove todo (GH issue exists)

* Revert polymer submodule commit

* Use a RachioPy version with SSL cert validation

* Update requirements
2017-05-29 11:15:27 +02:00
Fabian Affolter c12b8f763c Upgrade pysnmp to 4.3.6 (#7806) 2017-05-29 10:28:31 +02:00
Dan Cinnamon ef51d8518a Bump pyenvisalink to version 2.1 (#7803) 2017-05-29 10:27:36 +02:00
Fabian Affolter 8b7894fb86 Upgrade slacker to 0.9.50 (#7797) 2017-05-29 10:26:56 +02:00
Fabian Affolter 010f098df3 Upgrade Sphinx to 1.6.2 (#7805) 2017-05-29 10:26:33 +02:00
Oliver 1f3bb51821 Add Marantz SSDP discovery / Detect error string in AppCommand.xml body (#7779) 2017-05-29 10:26:10 +02:00
CTLS 10367eb250 Fix home/stay in concord232 (#7789) 2017-05-28 12:06:18 +02:00
sander76 7fb5488058 Powerview to async (#7682)
* first commit

* first commit

* first commit

* first commit

* changing requirements

* updated requirements_all.txt

* various changes as suggested in the comments.

* using global values for dict keys.
2017-05-26 22:19:19 +02:00
Paulus Schoutsen e68bd0457c Fix more deprecation warnings (#7778)
* Remove setting up an hbmqtt broker

* Don't pass loop to web.Application in tests

* Use .query instead of deprecated .GET for aiohttp requests

* Fix closing file resource

* Do not use asyncio mark

* Notify.html5 - PyJWT: Use options to disable verify

* Yamaha: Test was still using deprecated ip

* Remove pytest-asyncio
2017-05-26 13:12:17 -07:00
Eugenio Panadero 910020bc5f Fix Telegram Bot send file to multiple targets, snapshots of HA cameras, variable templating, digest auth (#7771)
* fix double template rendering when messages come from notify.telegram

* fix 'chat' information not present in callback queries

* better inline keyboards with yaml

To make a row of InlineKeyboardButtons you pass:
- a list of tuples like: `[(text_b1, data_callback_b1), (text_b2, data_callback_b2), ...]
- a string like: `/cmd1, /cmd2, /cmd3`
- or a string like: `text_b1:/cmd1, text_b2:/cmd2`

Example:
```yaml
data:
   message: 'TV is off'
   disable_notification: true
   inline_keyboard:
     - TV ON:/service_call switch.turn_on switch.tv, Other:/othercmd
     - /help, /init
```

* fix send file to multiple targets

* fix message templating, multiple file targets, HA cameras

- Allow templating for caption, url, file, longitude and latitude fields
- Fix send a file to multiple targets
- Load data with some retrying for HA cameras, which return 500 one or two times sometimes (generic cams, always!).
- Doc in services for new inline keyboards yaml syntax: `Text button:/command`

* HttpDigest authentication as proposed in #7396

* review changes

- Don't use `file` as variable name.
- For loop
- Simplify filter allowed `chat_id`s.

* Don't use `file` as variable name!

* make params outside the while loop

* fix chat_id validation when editing sent messages
2017-05-26 21:05:12 +02:00
Paulus Schoutsen f43db3c615 Replace executor with async_add_job (#7658)
* Remove executor

* Lint

* Lint

* Fix tests
2017-05-26 08:28:07 -07:00
Adam Mills 9e9705d6b2 Support for GE Zwave fan controller (#7767)
* Support for GE Zwave fan controller

* Tests for zwave fan

* Add additional fan workarounds
2017-05-25 22:55:00 -07:00
Paulus Schoutsen 6899c7b6f7 assertEquals is deprecated (#7777) 2017-05-25 22:21:22 -07:00
Paulus Schoutsen d0c9d6b69a Remove usage of event_loop fixture (#7776) 2017-05-25 21:40:36 -07:00
Paulus Schoutsen 81aaeaaf11 Get rid of mock http component app (#7775)
* Remove mock_http_component from config tests

* Remove mock_http_component_app from emulated hue test
2017-05-25 21:13:53 -07:00
Adam Mills 65c3201fa6 Rename of the zwave hass.data constants (#7768)
* Rename of the zwave hass.data constants

* Remove zwave since it is already implied
2017-05-25 21:11:02 -07:00
Anton Sarukhanov 3a843e1817 Add icons to device tracker. (#7759) 2017-05-24 19:12:26 -07:00
Paulus Schoutsen 0c7f8e910e Merge branch 'master' into dev 2017-05-24 19:05:01 -07:00
Hugo Herter 0abde3aa57 Change setup script to use pip install instead of setup.py develop (#7756)
Using `python setup.py develop` did not manage to install the required dependencies.
This updates `script/setup` to use `pip install -e .` instead in order to resolve the required dependencies.
2017-05-24 15:31:51 -07:00
amigian74 775d45ae5a Exclude filter for event types (#7627)
* add exclude filter for event types to recorder component

* corrected long line (279)

* change source code structure
add test for exclude event types

* code cleanup

* change source code structure

* Update __init__.py

* Update test_init.py
2017-05-24 15:23:52 -07:00
Paulus Schoutsen e7d783ca2a Update links.html 2017-05-24 14:47:22 -07:00
cribbstechnologies ef4ef2d383 Template light (#7657)
* starting light template component

* linting/flaking

* starting unit tests from copypasta

* working on unit testing

* forgot to commit the test

* wrapped up unit testing

* adding remote back

* updates post running tox

* Revert "adding remote back"

This reverts commit 852c87ff96.

* adding submodule back from origin

* updating submodule

* removing a line to commit

* re-adding line

* trying to update line endings

* trying to fix line endings

* trying a different approach

* making requested changes, need to fix tests

* flaking

* union rather than intersect; makes a big difference

* more tests passing, not sure why this one's failing

* got it working

* most of the requested changes

* hopefully done now

* sets; the more you know
2017-05-24 14:32:22 -04:00
everix1992 3638b21bcb Added new commands and functionality to the harmony remote component. (#7113)
* Added new commands and functionality to the harmony remote component.

-This includes the ability to optionally specify a number of times to repeat a specific command, such as pressing the volume button multiple times.
-Also added a new command that allows you to send multiple commands to the harmony at once, such as sending a set of channel numbers.
-Updated the unit tests for these changes.

* Fix flake8 coding violations

* Remove send_commands command and make send_command handle a single or list of commands

* Remove send_commands tests

* Update itach and kira remotes for new send_command structure. Fix pyharmony version in requirements_all.txt

* Fix incorrect variable name

* Fix a couple minor issues with remote tests
2017-05-23 17:00:52 -07:00
Stu Gott 54c45f80c1 Fix time_date sensor to update at predictable intervals (#7644)
* Fix time_date sensor to update at predictable intervals

* Delete automations.yaml
2017-05-23 16:00:26 -07:00
Juggels e3307fb1c2 Redesign monitored variables for hp_ilo sensor (#7534)
* Redesign monitored variables

Allow generating specific sensors without the need for template sensors

* Import 3rd party library inside update method

* Remove jsonpath_rw dependency

* Do not interfere with value_template or ilo_data output

Do not interfere with value_template or ilo_data output, this is now the responsibility of the user and should be handled in `configuration.yaml`

Fix UnusedImportStatement

Fix newline after function docstring

* Always output results to state
2017-05-23 14:56:00 -07:00
William Scanlon b5f20c9b64 Always return rgb color of bulbs (#7743) 2017-05-23 14:49:20 -07:00
Anton Sarukhanov 7055fddfb4 Don't block startup more than 60 seconds while waiting for components. (#7739) 2017-05-23 14:29:27 -07:00
Anders Melchiorsen fce09f624b LIFX: disable color features for white-only bulbs (#7742)
The product type is already established in order to decide the Kelvin range
so just reuse that information to disable color features for white-only lights.

Also change the breathe/pulse effects to be more useful for white-only
bulbs. For consistency, color bulbs set to a desaturated (i.e. white-ish)
color get the same default treatment as white-only bulbs.
2017-05-23 22:35:19 +02:00
nordeep be53cc7068 Don't initialize mqtt components which have already been discovered (#7625)
* Don't initialize mqtt components which have already been discovered

* Fix string length

* Fix blank lines, fix constant name

* Remove globals. Remove JSON dump

* Add tests. Update grammar

* PEP8 style issue

* Add hyphen to object_id regex

* PEP8 style fix
2017-05-23 11:08:12 -07:00
Anton Sarukhanov f3dabe21ab Prevent the random template filter from caching its output. Fixes #5678 (#7716) 2017-05-23 10:32:06 -07:00
Brenton Zillins 228fb8c072 Ensure https base_url in telegram bot (#7726) 2017-05-23 10:16:54 -07:00
Lev Aronsky c556b619b7 Asuswrt continuous ssh (#7728)
* Make ssh and telnet connections continuous in asuswrt

* Refactored SSH and Telnet connections into respective classes.

* Fixed several copy-paste typos and errors.

* More typos fixed.

* Small changes to arguments, to pass automated tests.

* Removed unsupported named arguments.

* Fixed a couple of mistakes in Telnet, and other lint errors.

* Added Telnet tests, and added lint exceptions.

* Removed comments from tests, as they irritated the hound.
2017-05-23 09:55:01 -07:00
Paulus Schoutsen 2682996939 Constrain requests to a version (#7725)
Add an optional extended description…
2017-05-23 15:45:22 +02:00
Alex Harvey 6872daab89 update apcacccess used in apcupsd to 0.0.10, which fixes random file drop from apcaccess (#7722) 2017-05-22 17:00:41 -07:00
Paulus Schoutsen 6d183e8bb3 Merge pull request #7686 from home-assistant/release-0-45-1
0.45.1
2017-05-22 11:36:21 -07:00
Paulus Schoutsen cdc8628e5a Allow fetching hass.io panel without auth (#7714) 2017-05-22 11:06:04 -07:00
tobygray dc4b0695b5 device_tracker.ubus: Handle empty results (#7673)
If OpenWRT isn't running the DHCP server then some OpenWRT hardware,
such as TP-Link TL-WDR3600 v1, can't determine the host
corresponding to an associated wifi client. This change handles that
by returning None when the request has no data in the result.
2017-05-22 11:06:04 -07:00
cgtobi 3fb691ead6 Fix playback control of web streams (#7683)
Web streams can't be paused and resumed later. That's why volumio stops them instead of pausing them.
2017-05-22 11:06:04 -07:00
Eugenio Panadero a9926e355f Fix telegram chats (#7689)
* bugfix for Telegram chat_ids

- Negative `chat_id`s for groups.
- Include `chat_id` in event data.
- Handle KeyError when receiving other types of messages, as `new_chat_member` ones, and send them as text.

* unused import

* fix double quote style, fix boolean expr, change warning msg

* mistake

* some more fixes

- fix if condition for msg bad fields.
- return True for a correct but not allowed or not recognized message: if not, the message arrives continuously.
- Allow to receive messages from unauthorized users if they come from authorized groups.

* support for `edited_message`s

- They come as normal messages, except for the 'edited_message' field instead of 'message'.
2017-05-22 11:06:04 -07:00
Paulus Schoutsen 17cbe0c6ce Allow fetching hass.io panel without auth (#7714) 2017-05-22 11:00:02 -07:00
Fabian Affolter 783abc7996 Make 'sender' as requirement for the config (fixes #7698) (#7706) 2017-05-22 15:17:15 +02:00
Fabian Affolter 47355eed41 Upgrade python-telegram-bot to 6.0.1 (#7704) 2017-05-22 13:56:36 +02:00
John Mihalic d5642a5faf Bump pyEight version (#7701) 2017-05-22 07:54:01 +02:00
tobygray ca3f07cdef device_tracker.ubus: Handle empty results (#7673)
If OpenWRT isn't running the DHCP server then some OpenWRT hardware,
such as TP-Link TL-WDR3600 v1, can't determine the host
corresponding to an associated wifi client. This change handles that
by returning None when the request has no data in the result.
2017-05-21 17:26:05 -07:00
LvivEchoes 99ea1e3f4f Continue tracking device over dhcp lease table if wireless adapter not installed (#7690) 2017-05-21 17:18:55 -07:00
Anders Melchiorsen bb8de5845a Sort entities in default groups by name (#7681)
* Sort entities in default groups by name

* Cleanups from review
2017-05-21 17:05:48 -07:00
cgtobi b3cb057aac Fix playback control of web streams (#7683)
Web streams can't be paused and resumed later. That's why volumio stops them instead of pausing them.
2017-05-21 17:05:04 -07:00
Eugenio Panadero 922303fd4b Fix telegram chats (#7689)
* bugfix for Telegram chat_ids

- Negative `chat_id`s for groups.
- Include `chat_id` in event data.
- Handle KeyError when receiving other types of messages, as `new_chat_member` ones, and send them as text.

* unused import

* fix double quote style, fix boolean expr, change warning msg

* mistake

* some more fixes

- fix if condition for msg bad fields.
- return True for a correct but not allowed or not recognized message: if not, the message arrives continuously.
- Allow to receive messages from unauthorized users if they come from authorized groups.

* support for `edited_message`s

- They come as normal messages, except for the 'edited_message' field instead of 'message'.
2017-05-21 17:02:22 -07:00
Adam Mills 8c1181f8e3 Remove defunct INSTALL_OPENZWAVE from Dockerfile (#7697) 2017-05-21 17:01:42 -07:00
John Arild Berentsen 4a0d6e73f4 ZWave: Add reset service to meters (#7676)
* Add reset service for command_class meters.

* Add reset service for command_class meters.

* cast index to const.py
2017-05-21 20:15:24 +02:00
Paulus Schoutsen 171086229a Guard against new and removed state change events (#7687) 2017-05-21 07:41:33 -07:00
Andrey 927024714b Zwave: Apply refresh_node workaround on 1st instance only (#7579)
* Apply refresh_node workaround on 1st instance only

* Add another test
2017-05-21 17:33:42 +03:00
tobygray 24b7fd3694 zoneminder: fix incorrect use of logging.exception. (#7675)
Prior to this change the zoneminder component was attempting to
use logging.exception outside of exception handling code. This
would lead to the traceback module throwing an exception when
trying to work out the traceback for the exception.

This fixes the issue by changing the exception call into a
plain error logging call.
2017-05-21 11:11:33 +02:00
Paulus Schoutsen d6f43ba839 Version bump to 0.45.1 2017-05-20 22:34:59 -07:00
Paulus Schoutsen 3492545ec1 Update state automation to work with new and deleted state changes 2017-05-20 22:34:53 -07:00
Fabian Affolter ceff9981be Merge branch 'master' into dev 2017-05-21 00:47:42 +02:00
Fabian Affolter 70ea16bdc0 Merge pull request #7648 from home-assistant/release-0-45
0.45
2017-05-21 00:34:44 +02:00
Marcelo Moreira de Mello 943958b140 Added support to Amcrest camera to feed using RTSP via ffmpeg (#7646)
* Implemented ffmpeg option on Amcrest camera and upgraded to version 1.2.0

* Added ffmpeg arguments and binary options to Amcrest camera

* Added ffmpeg as dependencies

* Makes lint happy and fixed requirements_all.txt

* Inherent the ffmpeg.binary configuration from ffmpeg component

* Update amcrest.py
2017-05-20 23:55:15 +02:00
John Arild Berentsen 23c5fc0aad Bugfix #7586 (#7661) 2017-05-20 23:53:48 +02:00
Fabian Affolter 45b4ef46cc Align with OpenALPR platform for naming conf variables (#7650) 2017-05-20 23:51:16 +02:00
Andrey 44edf3e105 Switch pymodbus to pypi (#7677) 2017-05-20 21:19:22 +02:00
Anders Melchiorsen 81f0826550 Ignore attribute changes in automation trigger from/to (#7651)
* Ignore attribute changes in automation trigger from/to

* Quote names in deprecation warnings

This makes it somewhat easier to read if the suggestion happens to be
named "to".

* Add test with same state, new attribute value
2017-05-20 15:18:59 -04:00
Barry Williams adde9e6231 Upgrade Openhome library (#7671)
* Added support for openhome devices using transport service

* Style cleanup
2017-05-20 17:43:35 +02:00
Paulus Schoutsen de85d38aa5 Update frontend 2017-05-20 08:08:06 -07:00
Paulus Schoutsen f637a07016 Update frontend 2017-05-20 08:07:32 -07:00
thecynic 9e153119ef Point pylutron to pypi (#7664) 2017-05-20 14:27:35 +03:00
Fabian Affolter b5c54864ac Change line endings to LN (#7660) 2017-05-19 07:39:13 -07:00
Paulus Schoutsen d369d70ca5 Fix tests (#7659)
* Remove global hass

* Http.auth test no longer spin up server

* Remove server usage from http.ban test

* Remove setupModule from test device_sun_light_trigger

* Update common.py
2017-05-19 07:37:39 -07:00
John Arild Berentsen 5aa72562a7 Bugfix #7586 (#7661) 2017-05-19 13:40:26 +02:00
Robbie Trencheny c4da921cb5 Add network_key as a config option (#7637)
* Add network_key as a config option

* Update __init__.py
2017-05-18 23:49:37 -07:00
Robbie Trencheny 7daa92249a Add network_key as a config option (#7637)
* Add network_key as a config option

* Update __init__.py
2017-05-18 23:49:15 -07:00
John Arild Berentsen 4a3d9a956d Final tweaks for Zwave panel (#7652)
* # This is a combination of 3 commits.
# The first commit's message is:
Add seperate zwave panel

# The 2nd commit message will be skipped:

#	unused import

# The 3rd commit message will be skipped:

#	Use get for config

* Add seperate zwave panel

* Modify set_config_parameter to accept setting string values

* descriptions

* Tweaks

* Tweaks

* Tweaks

* Tweaks

* lint

* Fallback if no config parameteres are available

* Update services.yaml

* review changes
2017-05-18 17:41:31 -07:00
Paulus Schoutsen 6662b7f52d Update frontend 2017-05-18 17:41:26 -07:00
Paulus Schoutsen e91fe94585 Update frontend 2017-05-18 17:41:03 -07:00
John Arild Berentsen 88ffe39945 Final tweaks for Zwave panel (#7652)
* # This is a combination of 3 commits.
# The first commit's message is:
Add seperate zwave panel

# The 2nd commit message will be skipped:

#	unused import

# The 3rd commit message will be skipped:

#	Use get for config

* Add seperate zwave panel

* Modify set_config_parameter to accept setting string values

* descriptions

* Tweaks

* Tweaks

* Tweaks

* Tweaks

* lint

* Fallback if no config parameteres are available

* Update services.yaml

* review changes
2017-05-18 17:39:31 -07:00
happyleavesaoc e479324db9 update usps (#7655)
* update usps

* fix doc
2017-05-18 17:30:43 -07:00
happyleavesaoc f65cc68705 bump ups version (#7654) 2017-05-18 23:38:50 +02:00
happyleavesaoc 238921b681 bump fedex version (#7653) 2017-05-18 23:37:39 +02:00
Marcelo Moreira de Mello 0fd415d7fb Added support to Amcrest camera to feed using RTSP via ffmpeg (#7646)
* Implemented ffmpeg option on Amcrest camera and upgraded to version 1.2.0

* Added ffmpeg arguments and binary options to Amcrest camera

* Added ffmpeg as dependencies

* Makes lint happy and fixed requirements_all.txt

* Inherent the ffmpeg.binary configuration from ffmpeg component

* Update amcrest.py
2017-05-18 10:06:24 +02:00
Fabian Affolter 0eb6540fe7 Align with OpenALPR platform for naming conf variables (#7650) 2017-05-18 09:57:38 +02:00
Paulus Schoutsen fc0c8540d3 Version bump to 0.46.0.dev0 2017-05-17 23:03:47 -07:00
Paulus Schoutsen eb473600f6 Version bump to 0.45 2017-05-17 23:03:27 -07:00
Paulus Schoutsen de999d8439 Merge remote-tracking branch 'origin/master' into dev 2017-05-17 23:03:00 -07:00
Paulus Schoutsen 6d97546f40 Update frontend 2017-05-17 22:29:46 -07:00
Paulus Schoutsen e773133bcf Fix automation failing to setup if no automations specified (#7647) 2017-05-17 21:57:50 -07:00
Anders Melchiorsen 3d4b2436db Coerce color_temp to int even when passed in as kelvin (#7640) 2017-05-17 19:20:59 -07:00
Paulus Schoutsen a068efcd47 Abort tests when instances leaked (#7623) 2017-05-18 00:19:40 +02:00
Fabian Affolter f86edd4f24 Seven segments OCR image processing (#7632)
* Add initial seven segments OCR image processing

* Fix typo
2017-05-18 00:07:02 +02:00
Daniel Perna a24aebd5ae Updated dependency (#7638) 2017-05-17 23:57:34 +02:00
Riccardo Canta f3b9e1e988 Osram lightify Removed wrong assignment (#7615)
self._brightness is assigned with the returned value of the
set_luminance() function, which is always equal to None.
2017-05-17 23:26:58 +02:00
corneyl 76b747edd6 Updated limitlessled requirement to v1.0.8 (#7629) 2017-05-17 09:05:34 -07:00
Eugenio Panadero f7d25396a4 Kodi specific service to call Kodi API methods (#7603)
* Kodi specific services to call Kodi API methods

 - new service: `kodi_execute_addon` to run a Kodi Addon with optional parameters. Results of the Kodi API call, if any, are redirected in a Home Assistant event: `kodi_execute_addon_result`.
 - new service: `kodi_run_method` to run a Kodi JSONRPC API method with optional parameters. Results of the Kodi API call are redirected in a Home Assistant event: `kodi_run_method_result`.
 - Add descriptions in services.yaml.
 - Add `timeout` parameter to yaml config (needed to make slow queries to the JSONRPC API, default timeout is set to 5s).
 - Trigger events with the results of the Kodi API calls, with:
 ```
 event_data = {
   'result': api_call_results,
   'result_ok': boolean,
   'input': api_call_parameters,
   'entity_id': 'media_player.kodi'}
```

* no need to clean OrderedDicts; no need for the `kodi_execute_addon` service

* no need for the `kodi_execute_addon` service

* unused import

* naming changes
2017-05-17 08:42:47 -04:00
Fabian Affolter 0e9728d94a Update docstrings (#7630) 2017-05-17 10:10:35 +02:00
Fabian Affolter 3b69de8a1a Upgrade Sphinx to 1.6.1 (#7624) 2017-05-17 09:14:28 +02:00
Fabian Affolter f0b2a6d0e6 Update docstrings and comments (#7626) 2017-05-17 09:14:11 +02:00
Conrad Juhl Andersen d2ed3a131f Add support for disabling tradfri groups (#7593)
* Add support for disabling tradfri groups

* Fixed styleguide problems

* Fix style problems

* Use default for groups when setting up in UI
2017-05-16 23:26:57 -07:00
Anders Melchiorsen ed5f94fd8a Add kelvin/brightness_pct alternatives to light.turn_on (#7596)
* Refactor color profiles to a class

* Refactor into preprocess_turn_on_alternatives

* LIFX: use light.preprocess_turn_on_alternatives

This avoids the color_name duplication and gains support for profile.

* Add kelvin parameter to light.turn_on

* Add brightness_pct parameter to light.turn_on

* LIFX: accept brightness_pct in effects

* Add test of kelvin/brightness_pct conversions
2017-05-16 23:00:46 -07:00
Greg Dowling 9dcc0b5ef5 Bump pyvera - fixes issue with % in brightness levels. (#7622) 2017-05-16 20:01:29 -07:00
Fabian Affolter 1fafa34eb1 Fix typo and update style to match the other platforms (#7621) 2017-05-16 21:57:00 +02:00
Ole-Kenneth 71ed17b836 Add Content-type: image/jpeg for camera proxy (#7581)
* Add Content-type: image/jpeg for camera proxy

* Set content_type in constructur
2017-05-16 09:11:44 -07:00
Philipp Schmitt a7f933966b Update Docker base image to python 3.6 (#7613) 2017-05-16 09:09:21 -07:00
Paulus Schoutsen 641ba014f2 Update frontend 2017-05-15 23:17:33 -07:00
Paulus Schoutsen d5ca6a5aed Force automation ids to always be a string (#7612) 2017-05-15 23:15:06 -07:00
Marc Egli d6081f3dc5 Make miflora monitored_conditions parameter optional (#7598)
* Make miflora monitored_conditions parameter optional.

* Use default keyword instead.

* Use list instead of tuple

* Simplify even more
2017-05-15 23:13:41 -07:00
Fabian Affolter f25347d98d File sensor (#7569)
* Add File sensor

* Use None and return

* Remove I/O

* Use less memory

* No traceback if file is empty
2017-05-15 14:25:46 +02:00
John Mihalic a1dc35fc75 Fix handling of single user (#7587) 2017-05-15 00:46:43 -07:00
Marc Egli 4da91d6a8b Add sonos alarm clock update service (#7521)
* Add sonos alarm clock update service

* Add tests and break lines

* Fix style errors

* Make test work with python<3.6

* Fix last two pylint errors

* fix new line to long errors
2017-05-15 00:42:45 -07:00
jhemzal 5c4a21efac Add posibility to specify snmp protocol version (#7564)
* snmp sensor protocol version configuration option

* fixed lint findings.
2017-05-15 00:34:51 -07:00
Adam Mills e2e58e6acc Automation State Change For timer attribute fix (#7584) 2017-05-15 00:34:30 -07:00
Eugenio Panadero d0304198de SMTP notify enhancements: full HTML emails and custom product_name in email headers (#7533)
* SMTP notify enhancements: HTML emails and customization options

- Send full HTML emails, with or without inline attached images.
- Custom `timeout`.
- Custom `product_name` identifier for the `FROM` and `X-MAILER` email headers.
- New HTML email test

* `sender_name` instead of product_name

 - Change `sender_name` instead of `product_name`.
 - No changes in `X-Mailer` header.
 - `From` header as always unless you define the new `sender_name` parameter.
2017-05-15 00:23:57 -07:00
Paulus Schoutsen 36d7fe72eb Fix websocket api reaching queue (#7590)
* Fix websocket api reaching queue

* Fix outside task message sending

* Fix Py34 tests
2017-05-15 00:10:06 -07:00
Marc Egli 6d245c43fc Pass additional arguments to tox in test_docker (#7591) 2017-05-14 23:21:39 -07:00
Matt N e1a4d51fa2 camera.zoneminder: Handle old versions of zoneminder (#7589) 2017-05-13 23:09:44 -07:00
Paulus Schoutsen 352cca1037 Remove more test requirements (#7574)
* No longer require pyunify during tests

* No longer require cast during tests

* No longer required dependency for tests

* No longer require pymochad for tests

* Astral is a core dependency

* Avoid having to install datadog dependency during tests

* CMUS test doesn't test anything

* Frontier Silicon doesn't test anything

* No longer require mutagen

* Update requirements_test_all.txt

* Remove stale comment
2017-05-13 21:25:54 -07:00
Paulus Schoutsen 206d02d531 Websocket_api: avoid parallel drain (#7576)
* Websocket_api: avoid parallel drain

* Remove send_message method
2017-05-13 16:34:45 -07:00
William Scanlon cfbbade6d1 Additional Wink lock features (#7445)
* Additional Wink lock features
2017-05-13 14:09:00 -04:00
Adam Mills cfea4b17e3 Add tests for zwave network events (#7573) 2017-05-12 23:06:32 -07:00
Stu Gott 9c4bc2a47f Add Kira component to sensor and remote platforms (#7479)
* Add Kira component to sensor and remote platforms

* Test cases for Kira component and platforms
2017-05-12 21:12:47 -07:00
Eugenio Panadero 4cdf0b4969 Fix Kodi specific services registry and add descriptions (#7551)
* Fix Kodi specific services, add descriptions, add more handled exceptions

 - Fixes issue #7528
 - Add descriptions for Kodi specific services in services.yaml.
 - Error handling in Kodi API errors.
 - Make compatible the existent specific service `media_player.kodi_set_shuffle` with the general `media_player.shuffle_set` service (both use the same method but with different named parameter, I think the Kodi specific service should be eliminated, since it is not)

* fix line too long

* removed new services (for another PR); removed `kodi_set_shuffle` service

* requested changes

 - Removed `kodi_set_shuffle` service.
 - Optional `media_name` and `artist_name` parameters. `media_name` defaults to 'ALL'.
 - Guard clause to check if the services are already registered.
2017-05-12 20:48:57 -07:00
bestlibre ad15844cf4 Fix systematic warning in influxdb sensor (#7541) 2017-05-12 20:47:12 -07:00
Kevin Fronczak 25cb7c652b Blink version bump (#7571)
Bumped blink version to support automatic reauthorization when tokens expire. Changed the battery sensor call to a string version so that the battery reports back "Low" or "OK" rather than a cryptic integer
2017-05-12 20:30:07 -07:00
Adam Mills 189023821b Tests for zwave setup features (#7570)
* Tests for zwave setup features

* Add test for frontend panel register
2017-05-12 20:27:44 -07:00
Adam Mills c118be6639 Tests for zwave discovery logic (#7566)
* Tests for zwave discovery logic

* Simplify patching

* Test ignored node
2017-05-12 20:18:20 -07:00
Mitesh Patel 11a3dc268f Support lutron serena shades (#7565)
* Adds support for the Lutron Caseta Serena shades hardware

* fixes typos
2017-05-12 20:17:11 -07:00
Paulus Schoutsen f0ce6c8210 Update netdisco (#7563) 2017-05-12 20:14:17 -07:00
Juggels ed0ec613c3 Comment RasPi specific requirements (#7562) 2017-05-12 20:06:28 -07:00
Johan Bloemberg 4a3048b370 Initialize sun with correct values. (#7559)
* Initialize sun with unknown values.

Initial values should be `unknown` instead of `0`. Otherwise on HA restart the value of `0` is pushed to metrics databases (graphite/influx/recorder).

* Update sun position before emitting initial update

* Simplify based on armills comment.

* Use provided time for calculation.
2017-05-12 16:04:30 -07:00
Per Osbäck fdb7371256 update pywebpush to 1.0.0 (#7561) 2017-05-12 09:25:34 -07:00
florincosta a96a98a260 Add raspihats binary sensor (#7508)
* Added raspihats binary_sensor platform

* Updated .coveragerc to ommit raspihats platforms.

* Using vol.Coerce(int) for validation and casting of I2CHat config address
2017-05-12 09:20:48 -07:00
Anders Melchiorsen 1ab7103aea LIFX: add lifx_set_state service call (#7552)
* Move service helpers to LifxManager

* Add lifx_set_color

This is a synonym for light.turn_on except it does not actually turn on the
light unless asked to.

Thus, turn_on can be implemented just by asking.

* Rename set_color to set_state

* Support power=False with lifx_set_state
2017-05-12 09:19:51 -07:00
Kane610 416b8e0efe Axis component (#7381)
* Added Axis hub, binary sensors and camera

* Added Axis logo to static images

* Added Axis logo to configurator
Added Axis mdns discovery

* Fixed flake8 and pylint comments

* Missed a change from list to function call
V5 of axis py

* Added dependencies to requirements_all.txt

* Clean up

* Added files to coveragerc

* Guide lines says to import function when needed, this makes Tox pass

* Removed storing hass in config until at the end where I send it to axisdevice

* Don't call update in the constructor

* Don't keep hass private

* Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError

* Axis package not in pypi yet

* Do not catch bare excepts. Device schema validations raise vol.Invalid.

* setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains

* Don't expect axis.conf contains correct values

* Improved configuration validation

* Trigger time better explains functionality than scan interval

* Forgot to remove this earlier

* Guideline says double qoutes for sentences

* Return false from discovery if config file contains bad data

* Keys in AXIS_DEVICES are serialnumber

* Ordered imports in alphabetical order

* Moved requirement to pypi

* Moved update callback that handles trigger time to axis binary sensor

* Renamed configurator instance to request_id since that is what it really is

* Removed unnecessary configurator steps

* Changed link in configurator to platform documentation

* Add not-context-manager (#7523)

* Add not-context-manager

* Add missing comma

* Threadsafe configurator (#7536)

* Make Configurator thread safe, get_instance timing issues breaking configurator working on multiple devices

* No blank lines allowed after function docstring

* Fix comment Tox

* Added Axis hub, binary sensors and camera

* Added Axis logo to static images

* Added Axis logo to configurator
Added Axis mdns discovery

* Fixed flake8 and pylint comments

* Missed a change from list to function call
V5 of axis py

* Added dependencies to requirements_all.txt

* Clean up

* Added files to coveragerc

* Guide lines says to import function when needed, this makes Tox pass

* Removed storing hass in config until at the end where I send it to axisdevice

* Don't call update in the constructor

* Don't keep hass private

* Unnecessary lint ignore, following Baloobs suggestion of using NotImplementedError

* Axis package not in pypi yet

* Do not catch bare excepts. Device schema validations raise vol.Invalid.

* setup_device still adds hass object to the config, so the need to remove it prior to writing config file still remains

* Don't expect axis.conf contains correct values

* Improved configuration validation

* Trigger time better explains functionality than scan interval

* Forgot to remove this earlier

* Guideline says double qoutes for sentences

* Return false from discovery if config file contains bad data

* Keys in AXIS_DEVICES are serialnumber

* Ordered imports in alphabetical order

* Moved requirement to pypi

* Moved update callback that handles trigger time to axis binary sensor

* Renamed configurator instance to request_id since that is what it really is

* Removed unnecessary configurator steps

* Changed link in configurator to platform documentation

* No blank lines allowed after function docstring

* No blank lines allowed after function docstring

* Changed discovery to use axis instead of axis_mdns

* Travis CI requested rerun of script/gen_requirements_all.py
2017-05-12 08:51:54 -07:00
Andrey 5b3ef0f76f Treat swing and fan level as optional in Sensibo Climate. (#7560) 2017-05-12 18:28:58 +03:00
Tsvi Mostovicz 452c3a1b25 Support adding different server locations for Microsoft face component (#7532)
* Support adding different server locations

* Rename variables and move CONF_ const into component as requested in review

* Fix unittests

* Forgot to add tests for microsoft_face_identify
2017-05-12 10:53:25 +02:00
Paulus Schoutsen 8da10f670b Only install tox in dev mode (#7557) 2017-05-12 00:01:06 -07:00
Adam Mills b805d8a844 Hide proximity updates in logbook (#7549) 2017-05-11 19:37:32 -07:00
Paulus Schoutsen 76675a54f8 Do not install all dependencies in dev mode (#7548)
* ps - do not install all dependencies

* Comment out blinkt because it depends on GPIO

* Add pip upgrade check back

* Disable import error blinkt

* Update comment

* Fix comment
2017-05-11 19:20:23 -07:00
Trevor 0e246059f9 Add SSL support to NZBGet sensor (#7553) 2017-05-11 23:05:06 +02:00
Fabian Affolter 0e41342a40 Upgrade dweepy to 0.3.0 (#7550) 2017-05-11 22:48:03 +02:00
Adam Mills 04f1054d07 Automatic version bump (#7555) 2017-05-11 22:47:47 +02:00
Fabian Affolter 966bda079e Upgrade sendgrid to 4.1.0 (#7538) 2017-05-11 09:06:22 -07:00
jumpkick ef4587f994 Fix for #7459 (#7544)
* Generate a new updateDate with every call

This should fix #7459
Tests need to be updated in another commit.

* Replace STATIC_TIME with datetime object check

Removing the "DATE" argument from the Alexa component's configuration (because it is now dynamically generated) requires this commit's changes to the test cases to check that the updateDate data is a datetime type rather than a specific hardcoded value ('2016-10-10T19:51:42.0Z').

* Fix brackets
2017-05-11 09:04:17 -07:00
Kane610 2c8f6a0ad0 Threadsafe configurator (#7536)
* Make Configurator thread safe, get_instance timing issues breaking configurator working on multiple devices

* No blank lines allowed after function docstring

* Fix comment Tox
2017-05-11 10:24:36 +03:00
Fabian Affolter 8cdadd2aa0 Add not-context-manager (#7523)
* Add not-context-manager

* Add missing comma
2017-05-11 09:14:52 +02:00
Fabian Affolter 3bdf77ad62 Add myStrom binary sensor (#7530) 2017-05-10 16:58:03 +02:00
Adam Mills 8c90fd19ff Try to request current_location Automatic scope (#7447) 2017-05-10 05:44:52 -07:00
Fabian Affolter 71b4afb780 Update docstrings and log messages (#7526) 2017-05-10 12:06:57 +02:00
corneyl 6e6a000217 Upgrade limitlessled to 1.0.7 (#7525) 2017-05-10 10:45:33 +02:00
Bas Schipper 85e71fc785 Support for the PiFace Digital I/O module (#7494)
* Added rpi_pfio component supporting the PiFace I/O module

* Fixed some code style issues

* Removed global listener

* Update rpi_pfio.py
2017-05-09 22:36:33 -07:00
Fabian Affolter 216199556a Don't interact with hass directly (#7099) 2017-05-09 21:56:17 -07:00
Nuno Sousa 89d950c73a Add password parameter to uvc component (#7499) 2017-05-09 21:54:38 -07:00
Eugenio Panadero b30c352e37 Telegram Bot enhancements with callback queries and new notification services (#7454)
* telegram_bot and notify.telegram enhancements:
- Receive callback queries and produce `telegram_callback` events.
- Custom reply_markup (keyboard or inline_keyboard) for every type of message (message, photo, location & document).
- `disable_notification`, `disable_web_page_preview`, `reply_to_message_id` and `parse_mode` optional keyword args.
- Line break between title and message fields: `'{}\n{}'.format(title, message)`
- Move Telegram notification services to `telegram_bot` component and forward service calls from the telegram notify service to the telegram component, so now the `notify.telegram` platform depends of `telegram_bot`, and there is no need for `api_key` in the notifier configuration. The notifier calls the new notification services of the bot component:
	- telegram_bot/send_message
	- telegram_bot/send_photo
	- telegram_bot/send_document
	- telegram_bot/send_location
	- telegram_bot/edit_message
	- telegram_bot/edit_caption
	- telegram_bot/edit_replymarkup
	- telegram_bot/answer_callback_query
- Added descriptions of the new notification services with a services.yaml file.
- CONFIG_SCHEMA instead of PLATFORM_SCHEMA for the `telegram_bot` component, so only one platform is allowed.
- Async component setup.

* telegram_bot and notify.telegram enhancements: change in requirements_all.txt.
2017-05-09 21:42:17 -07:00
Gergely Imreh 1312ee0f7d sensor.envirophat: do not set up platform if hardware is not attached (#7438)
* sensor.envirophat: do not set up platform if hardware is not attached

Fixes comment from:
https://github.com/home-assistant/home-assistant/pull/7427#discussion_r114703904

* Fix update logic.
2017-05-09 21:29:38 -07:00
Andrey f4915ddb0b Switch basicmodem and python-roku to pypi (#7514) 2017-05-09 20:23:19 -07:00
Marc Egli 43296069c3 Update docker dev environment to python3.6 (#7520)
* Update docker dev environment to python3.6

* comment out disable switches again
2017-05-09 20:16:46 -07:00
John Arild Berentsen 1eaec8f406 Zwave panel api (#7456)
* # This is a combination of 3 commits.
# The first commit's message is:
Add seperate zwave panel

# The 2nd commit message will be skipped:

#	unused import

# The 3rd commit message will be skipped:

#	Use get for config

* Add seperate zwave panel

* more info

* Add usercodeview

* Improve api

* Improve api

* Separate api into own file.

* disable missing import

* review changes

* Tests 1

* Verify that we fetch data from groups

* Tests groups

* config 1

* usercode 1

* Api mods

* Tweak API

* docstrings

* 100% api testing
2017-05-09 18:56:41 -07:00
Paulus Schoutsen 5d820ec188 Add support for automation config panel (#7509)
* Add support for automation config

* Build fromtend

* Lint
2017-05-09 18:44:00 -07:00
Marc Egli d86dfb6336 Fix sonos sleep timer (#7503) 2017-05-09 18:35:51 +02:00
Josh Anderson b34c58386c Correct retrieval of spotify shuffle state (#7505)
Returned on the current playback response itself, not the device
2017-05-09 17:35:30 +02:00
abmantis 5cb3382425 new source only forces "play" if the current state is "playing" (#7506) 2017-05-09 17:34:17 +02:00
Adam Mills 40d27cde0e Refactor sun component for correctness (#7295)
* Refactor sun component for correctness

* Convert datetimes to dates for astral

* Fix tests for updated code

* Fix times now that calcs are fixed

* Move sun functions to helpers

* Fix flake on new file

* Additional tweaks from review

* Update requirements
2017-05-09 00:03:34 -07:00
Oliver 419d97fc06 Fixed potential AttributeError when checking for deleted sources (#7502) 2017-05-09 07:24:18 +02:00
Andrey 1cd51bc6a8 Switch onkyo to pypi (#7497) 2017-05-09 08:13:29 +03:00
Fabian Affolter c12c742297 Upgrade beautifulsoup4 to 4.6.0 (#7491) 2017-05-08 19:39:40 +02:00
Johan Bloemberg ce879b7eb8 Prevent printing of packets. (#7492)
A small bug in the python-rflink library caused packets to be printed. This update prevents this from happening.
2017-05-08 17:04:17 +02:00
Fabian Affolter d7e3962cc0 Upgrade async_timeout to 1.2.1 (#7490) 2017-05-08 17:02:37 +02:00
Anders Melchiorsen 86b34b40a1 LIFX: avoid out-of-bounds hue aborting the colorloop effect (#7495)
The hue is now a float but the hsbk conversion still believed it to be
an integer that could not be larger than 359. The float can in fact be,
for example, 359.9 and this would cause an out-of-bounds error in the
set_color call.

For completeness, the initial hue is also changed to a float.
2017-05-08 16:51:27 +02:00
Paulus Schoutsen 12293d6600 0.44.2 (#7488)
* Version bump to 0.44.2

* Update frontend
2017-05-07 22:07:52 -07:00
Mitesh Patel 66cbdc3043 Uses pypi for deps (#7485) 2017-05-07 17:32:13 -07:00
Paulus Schoutsen e1d1385358 Fix travis 2017-05-07 16:55:22 -07:00
Paulus Schoutsen bafc04ca42 Update tox.ini 2017-05-07 16:53:28 -07:00
Paulus Schoutsen 5717c87097 Update tox.ini 2017-05-07 16:51:46 -07:00
Paulus Schoutsen ab9c394e93 Version bump to 0.44.1 2017-05-07 15:09:53 -07:00
Paulus Schoutsen 5d13f36a4b Merge pull request #7482 from home-assistant/release-0-44-1
0.44.1
2017-05-07 15:08:37 -07:00
Caleb 4165629f97 Update to pyunifi 2.12 (#7468)
* Update to pyunifi 2.12

* Update requirements_all.txt
2017-05-07 14:03:39 -07:00
Marc Egli f87b9b7b85 Fix plant MIN_TEMPERATURE, MAX_TEMPERATURE validation (#7476)
* Fix plant MIN_TEMPERATURE, MAX_TEMPERATURE validation

small_float only allows values from 0 to 1 so we should use float instead

* Do not use vol.All for a single validation
2017-05-07 14:03:14 -07:00
Martin Hjelmare 2ab45441a8 Upgrade pymysensors to 0.10.0 (#7469) 2017-05-07 14:02:53 -07:00
pezinek fcdfebefd9 Forecasts for weather underground (#7062) 2017-05-07 13:56:19 -07:00
Brian Cribbs 9b920b3b40 fixing nits 2017-05-07 13:56:05 -07:00
Brian Cribbs 0c94df9fcf fixing documentation 2017-05-07 13:56:05 -07:00
Brian Cribbs 1ba4435693 repairing functionality for non-zero based ranges 2017-05-07 13:56:05 -07:00
Paulus Schoutsen 00ec50da4b Update frontend 2017-05-07 13:50:07 -07:00
Marc Egli c1056ea4d4 Fix plant MIN_TEMPERATURE, MAX_TEMPERATURE validation (#7476)
* Fix plant MIN_TEMPERATURE, MAX_TEMPERATURE validation

small_float only allows values from 0 to 1 so we should use float instead

* Do not use vol.All for a single validation
2017-05-07 15:15:18 +02:00
Paulus Schoutsen 9440ff881f Remove listening to homeassistant_start with event automation (#7474) 2017-05-06 23:52:39 -07:00
Robbie Trencheny c525ee9daa Make this an error instead of an info 2017-05-06 23:11:11 -07:00
Paulus Schoutsen 79ca47640e Update requirements_test_all.txt 2017-05-06 23:02:12 -07:00
Caleb 41212b90c4 Update to pyunifi 2.12 (#7468)
* Update to pyunifi 2.12

* Update requirements_all.txt
2017-05-06 22:39:21 -07:00
Paulus Schoutsen aa6339818e Test only dependencies (#7472)
* Generate requirements file for tests

* Update tox

* Update validate

* Lint

* Tweak order in travis.yml to run longest job first
2017-05-06 22:37:31 -07:00
Paulus Schoutsen 305309a59e Upgrade Dockerfile to Python 3.6 (#7471) 2017-05-06 20:16:40 -07:00
Paulus Schoutsen ea095de98e Demo: Update old group member thermostat.ecobee -> climate 2017-05-06 19:40:59 -07:00
Paulus Schoutsen e8a33758c1 Capitalize group names in demo 2017-05-06 19:38:48 -07:00
Martin Hjelmare 47034f83f4 Upgrade pymysensors to 0.10.0 (#7469) 2017-05-06 19:10:17 -07:00
Andrey 2c1df75c07 Switch russound, pymysensors, and pocketcasts to pypi (#7449)
* Switch russound to pypi

* Switch pymysensors to pypi

* Switch pocketcasts to pypi
2017-05-07 02:22:38 +02:00
Paulus Schoutsen 43ff95ad3b Merge pull request #7444 from home-assistant/release-0-44
0.44
2017-05-06 10:38:45 -07:00
Josh Wright e2559fd6cf Fix object type for default KNX port
#7429 describes a TypeError that is raised if the port is omitted in the config for the KNX component (integer is required (got type str)). This commit changes the default port from a string to an integer. I expect this will resolve that issue...
2017-05-06 10:27:36 -07:00
Gergely Imreh abe6f9343f sensor.envirophat: add missing requirement (#7451)
Adding requirements that is not explicitly pulled in by the library
that manages the Enviro pHAT.
2017-05-06 10:27:17 -07:00
pezinek 7a70496b11 Forecasts for weather underground (#7062) 2017-05-06 10:11:31 -07:00
Adam Mills 7dd7f509ca Add tests for deprecation helpers (#7452) 2017-05-06 10:10:48 -07:00
Robbie Trencheny 6cc85adb81 Merge pull request #7460 from home-assistant/fix/default-knx-port
Fix object type for default KNX port
2017-05-05 18:03:46 -07:00
Josh Wright 2971a24c56 Fix object type for default KNX port
#7429 describes a TypeError that is raised if the port is omitted in the config for the KNX component (integer is required (got type str)). This commit changes the default port from a string to an integer. I expect this will resolve that issue...
2017-05-05 19:19:24 -04:00
Nuno Sousa 20ded1ba3e Add datadog component (#7158)
* Add datadog component

* Improve test_invalid_config datadog test

* Use assert_setup_component for test setup
2017-05-06 00:34:40 +02:00
Josh Wright 2e4ae3e73d PyPI Openzwave (#7415)
* Remove default zwave config path

PYOZW now has much more comprehensive default handling for the config
path (in src-lib/libopenzwave/libopenzwave.pyx:getConfig()). It looks in
the same place we were looking, plus _many_ more. It will certainly do a
much better job of finding the config files than we will (and will be
updated as the library is changed, so we don't end up chasing it). The
getConfig() method has been there for a while, but was subsntially
improved recently.

This change simply leaves the config_path as None if it is not
specified, which will trigger the default handling in PYOZW.

* Install python-openzwave from PyPI

As of version 0.4, python-openzwave supports installation from PyPI,
which means we can use our 'normal' dependency management tooling to
install it. Yay.

This uses the default 'embed' build (which goes and downloads
statically sources to avoid having to compile anything locally). Check
out the python-openzwave readme for more details.

* Add python-openzwave deps to .travis.yml

Python OpenZwave require the libudev headers to build. This adds the
libudev-dev package to Travis runs via the 'apt' addon for Travis.

Thanks to @MartinHjelmare for this fix.

* Update docker build for PyPI openzwave

Now that PYOZW can be install from PyPI, the docker image build process
can be simplified to remove the explicit compilation of PYOZW.
2017-05-05 14:57:14 -07:00
Gergely Imreh 4b5be750b2 sensor.envirophat: add missing requirement (#7451)
Adding requirements that is not explicitly pulled in by the library
that manages the Enviro pHAT.
2017-05-05 11:37:54 -07:00
florincosta 92411cdc18 Add new raspihats component (#7392)
* Add new raspihats component

* added raspihats to COMMENT_REQUIREMENTS in gen_requirements_all.py

* disabled pylint import errors

* using hass.data for storing i2c-hats manager
2017-05-05 00:02:47 -07:00
Daniel Høyer Iversen 94f7c397d7 Add hass to rfxtrx object (#6844) 2017-05-04 23:51:35 -07:00
Daniel Høyer Iversen 526abdd329 Add hass to rfxtrx object (#6844) 2017-05-04 23:50:53 -07:00
Paulus Schoutsen 61196b1c83 Version bump to 0.45.0.dev0 2017-05-04 21:41:32 -07:00
Paulus Schoutsen 03e3fb77c4 Version bump to 0.44 2017-05-04 21:41:11 -07:00
Anders Melchiorsen bfd6110091 LIFX: handle unavailable lights gracefully
Recent aiolifx allow sending messages to unregistered devices (as a
no-op). This is handy because bulbs can disappear anytime we yield and
constantly testing for availability is both error-prone and annoying.

So keep the aiolifx device around until a new one registers on the same
mac_addr.
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 71f9507df7 LIFX: fix color restore after running effects
State restoration takes up to a second because bulbs can be slow to react.
During this time an effect could keep running, overwriting the state that we
were trying to restore.

Now the effect forgets the light immediately and it thus avoids further
changes while the restored state settles.
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 1c5eb88368 LIFX: avoid warnings about already running updates
Forcing a refresh will log a warning if the periodic async_update happens
to be running already.

So let's do the refresh locally and remove the force_refresh.
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 4be89521d4 LIFX: Update aiolifx requirement
This update silences some warnings (frawau/aiolifx#7).
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 1e4b56c4d4 LIFX: Move random hue initial color to the LIFXEffect base class
It's a reasonable default for several light effects.
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 655b82c1e2 LIFX: Use 3500K as neutral white
This does not really matter because the colorloop uses saturated colors
(without much white). Anyway, just copy the 3500K that the LIFX app uses.
2017-05-04 21:40:35 -07:00
Anders Melchiorsen 81b567b68f LIFX: refresh state after stopping an effect
This clears the internal cache in case polling picked up the state as set by
an effect.

For example, aborting an effect by selecting a new brightness could keep a
color set by the effect.
2017-05-04 21:40:34 -07:00
Paulus Schoutsen 009a9d59ed Update frontend 2017-05-04 21:38:50 -07:00
Paulus Schoutsen 629bf3eefd Update frontend 2017-05-04 21:38:28 -07:00
Paulus Schoutsen 3b237795ba Merge branch 'release-0-44' into dev 2017-05-04 21:23:40 -07:00
Paulus Schoutsen 80e55fb286 Merge remote-tracking branch 'origin/master' into release-0-44 2017-05-04 21:23:18 -07:00
Robbie Trencheny 12910de9ae Merge pull request #7289 from jminardi/jminardi/tplink-logout
Log out of TP-Link router after devices are recorded.
2017-05-04 18:47:25 -07:00
Robbie Trencheny 2f686124c8 Merge pull request #7446 from amelchio/lifx-misc
LIFX: small error corrections
2017-05-04 18:45:14 -07:00
Robbie Trencheny b59ca034ae Merge pull request #7393 from cribbstechnologies/dev
MQTT Cover: Fixed status reporting for range with non-zero base
2017-05-04 18:32:24 -07:00
Anders Melchiorsen 78a3f259d6 LIFX: handle unavailable lights gracefully
Recent aiolifx allow sending messages to unregistered devices (as a
no-op). This is handy because bulbs can disappear anytime we yield and
constantly testing for availability is both error-prone and annoying.

So keep the aiolifx device around until a new one registers on the same
mac_addr.
2017-05-04 22:51:00 +02:00
William Scanlon 1d0acb5a2c Get new token to keep pubnub updates working (#7437) 2017-05-04 13:17:35 -07:00
Daniel Perna ea36c91919 Fix for broken virtual keys (#7439) 2017-05-04 13:15:36 -07:00
Greg Dowling 1d9f1487c3 Bump pyvera version - handle malformed json replies in poll thread. (#7440) 2017-05-04 13:14:36 -07:00
Nolan Gilley d251621f2b Update join (#7443)
* update python-join-api to 0.0.2

* bump python-join-api to 0.0.2
2017-05-04 13:14:14 -07:00
William Scanlon 8d50045971 Suppress logs when octorpint goes offline (#7441)
* Suppress logs when octorpint goes offline

* Fixed line length
2017-05-04 13:13:09 -07:00
Philipp Schmitt cc0299d046 Replace pymailgun with pymailgunner (#7436)
* mailgun: Replace pymailgun with pymailgunner

* Fix imports
2017-05-04 20:34:00 +03:00
Pascal Vizeli 7e539a3cb2 Add support for face recognition with dlib (#7421)
* Add support for face recognition with dlib

* fix lint

* fix lint p2

* update library

* dlib can not build

* fix lint

* Fix int p1

* Update dlib_face_detect.py

* Update dlib_face_detect.py

* Update dlib_face_detect.py
2017-05-04 16:03:50 +02:00
deisi e3bb45c906 Added osramlightify groups. (#7376)
* Added osramlighrify groups.

Allows you to make use of the build in osram lightify groups. Group states get
handeled similar as in the case of phillips hue. A lightify group shows up as
light in the homeassistant webinterface. If one light of the
group is on, the complete group is considered to be on.

To use this feature, first define some groups within your lighrify bridge, then
set add `allow_lightify_groups=true` to you osramlightify config.

It might look like:
````yaml
- platform: osramlightify
  host: IP-ADDRES
  allow_lightify_groups: true
```

* Fixed Pylint errors.

* Included requests.

* Included more requests.

* Fixed setup bridge and removed _light attribute.

* Update osramlightify.py
2017-05-03 23:11:27 -07:00
Gergely Imreh df13352989 Add new sensor: Enviro pHAT (#7427)
* Add new sensor: Enviro pHAT

Add support for the Enviro pHAT for Raspberry Pi, see hardware
info at https://shop.pimoroni.com/products/enviro-phat

* Move update to add_devices call
2017-05-03 22:59:50 -07:00
Nolan Gilley 05a3c463bf update for pypi (#7430) 2017-05-04 07:48:43 +02:00
Fabian Affolter c44ebbefc4 Upgrade temperusb to 1.5.3 (#7428) 2017-05-04 07:46:43 +02:00
Paulus Schoutsen fab533de87 ps - fix websocket yielding pt2 (#7434) 2017-05-03 21:12:08 -07:00
Paulus Schoutsen ad5a9bf5ac wsock.send_json is a coroutine (#7433) 2017-05-03 20:27:03 -07:00
Paulus Schoutsen c5a91393e4 Guard against no content type (#7432) 2017-05-03 20:12:51 -07:00
Anders Melchiorsen 494a776959 LIFX: avoid warnings about already running updates
Forcing a refresh will log a warning if the periodic async_update happens
to be running already.

So let's do the refresh locally and remove the force_refresh.
2017-05-04 00:21:24 +02:00
Anders Melchiorsen 193270c4fb LIFX: Update aiolifx requirement
This update silences some warnings (frawau/aiolifx#7).
2017-05-04 00:21:24 +02:00
Anders Melchiorsen ec490070ca LIFX: Move random hue initial color to the LIFXEffect base class
It's a reasonable default for several light effects.
2017-05-04 00:21:24 +02:00
Anders Melchiorsen 8233f086cd LIFX: Use 3500K as neutral white
This does not really matter because the colorloop uses saturated colors
(without much white). Anyway, just copy the 3500K that the LIFX app uses.
2017-05-04 00:21:24 +02:00
Anders Melchiorsen 99e34539b9 LIFX: fix color restore after running effects
State restoration takes up to a second because bulbs can be slow to react.
During this time an effect could keep running, overwriting the state that we
were trying to restore.

Now the effect forgets the light immediately and it thus avoids further
changes while the restored state settles.
2017-05-04 00:21:24 +02:00
Anders Melchiorsen 71d909483c LIFX: refresh state after stopping an effect
This clears the internal cache in case polling picked up the state as set by
an effect.

For example, aborting an effect by selecting a new brightness could keep a
color set by the effect.
2017-05-04 00:21:24 +02:00
Anders Melchiorsen dbd6f7e4ed Reverse limitlessled color_temp range (#7359)
Reverse limitlessled color_temp range
2017-05-03 23:32:49 +02:00
Andrey 403a721e91 Comment out opencv-python that is not installable on arm (#7426)
* Comment out opencv-python that is not installable on arm

* Disable import-error
2017-05-03 21:08:21 +03:00
Paulus Schoutsen af5439860f Update opencv.py 2017-05-03 08:29:17 -07:00
Paulus Schoutsen 0f94c8a2e7 ps - fix opencv (#7419)
* ps - fix opencv

* fix lint
2017-05-03 12:15:04 +02:00
Fabian Affolter 203f48cadc Update docstrings (#7420) 2017-05-03 10:11:39 +02:00
Luar Roji 1a74c41056 Fixed extra R in variable name. (#7418) 2017-05-03 08:32:19 +02:00
Teagan Glenn b321e0ef80 Opencv (#7261)
* OpenCV

* Fix

* Type-o

* Remove unused opencv camera component discovery.
2017-05-02 21:55:51 -07:00
Gergely Imreh 70ad06d71c light.piglow update (#7408)
* light.piglow: brightness control logic update

Do not reset brightness when brightness is not explicitly supplied.
Based on conversation at:
https://github.com/home-assistant/home-assistant/pull/7377#discussion_r114102005

* light.piglow: add assumed state
2017-05-02 21:21:37 -07:00
Adam Mills c35d09d5f0 Convert automatic device tracker to push updates (#7404)
* Convert automatic device tracker to push updates

* Update test

* Add to coveragerc

* Fire hass events when automatic update received

* Change brace indentation
2017-05-02 21:21:17 -07:00
Henrik Nicolaisen f389266f5f remove charset if set in content type header (#7411)
* remove charset if set in content type header like this “Content-Type: image/jpeg;charset=UTF-8”

* fixed lint error
2017-05-02 21:19:16 -07:00
wokar f9627a5646 applx suggested fix from issue #6573 (#7390) 2017-05-02 21:04:00 -07:00
zeltom 517bd39015 Pilight binary sensor components (#6774)
* Add files via upload

Pilight binary sensor components.

* Pep8 fixed

* Remove unused imports

* Remove STATE_UNKNOWN import

* Grouping import

* New import grouping

* Update pilight.py

* Update pilight.py

* Update pilight.py

* Prevent multiple timer call

* Update .coveragerc

* Fix alphabet ordre

* Fix & clean code (change payload comparaison, delete state function)

* Fix payload comparison and remove state methode

* Fix unused import, whitespaces

* Fix ident error
2017-05-02 20:48:49 -07:00
Andrey 11dc7246af Add Sensibo climate platform (#7379)
* Add Sensibo climate platform

* Force update after running a service

* Add sensibo to .coveragerc

* Use 10s timeout

* Fix schema. Remove print.

* Better handle unit conversions.
2017-05-02 17:23:36 -07:00
Gergely Imreh 8654993b18 light.sensehat: brightness control logic update (#7409)
Do not reset brightness when brightness is not explicitly supplied.
Based on conversation at:
https://github.com/home-assistant/home-assistant/pull/7377#discussion_r114102005
2017-05-02 17:02:13 -07:00
Anders Melchiorsen 1aa3ab52b7 LIFX: Add transition option to colorloop effect (#7410)
This allows for more of a disco mode where lights change so fast that you
actually notice it.

Also change the valid period to the maximum 20 msgs/sec that LIFX bulbs
can handle.
2017-05-03 00:41:43 +02:00
Anders Melchiorsen d68f59ca6c Update LIFX default color for breathe/pulse effects (#7407)
First, move the default away from turn_on so we do not have to test for
the current service.

Next, change the default color away from random. The new default is that
saturated colors will flash white and desatured colors will flash to their
fully satured color. Always with full brightness.

After many experiments, this was the method that best produced results that
are both visually pleasing and always noticeable as a flash.
2017-05-03 00:05:11 +02:00
Fabian Affolter 4d52b0ecd5 Update docstrings (#7405)
* Update docstrings

* Fix lint issues

* Update docstrings
2017-05-02 22:47:20 +02:00
Brian Cribbs 1b2c83145c fixing nits 2017-05-02 15:41:45 -04:00
Gergely Imreh 12c8266942 light.blinkt: update brightness control logic (#7389)
Always use the current brightness, as per discussion at
https://github.com/home-assistant/home-assistant/pull/7377#discussion_r114102005
2017-05-02 18:35:23 +02:00
Fabian Affolter a4f1f6e724 Update docstrings (#7374)
* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update docstring

* Update docstrings

* Update docstrings

* Fix lint issues

* Update docstrings

* Revert changes in dict
2017-05-02 09:18:47 -07:00
amigian74 0e08925259 correct MQTT subscription filter (#7269)
* correct MQTT subscription filter

* wildcard handling (#) fixed

* wildcard handling (#) fixed

* added tests for topic subscription like +/something/#

* function names changed (line too long)

* using raw strings for regular expression
import order changed
2017-05-02 09:18:34 -07:00
Andrey 570c5549a9 Clean up requirements (#7391) 2017-05-02 09:15:02 -07:00
Scott Bradshaw f4f06af0c5 OpenGarage support (#7338)
* OpenGarage.io support

Cleaned up component and ran lint checking

* Fixing lint errors

* Added supported_features and device_class

* Added timeout to HTTP Requests and other changes based on feedback.

* Removed watcher. It provided little value and could cause issues if status was stuck in a state.

* Changes based on feedback. Added error checking for invalid device_key.

* Lint
2017-05-02 08:46:56 -07:00
John Mihalic 752a4b958e Add Eight sleep component (#7275)
* Eight Sleep Implementation

* Update coverage

* Update hass requirements

* Remove unnecessary debug statements

* Bump version to fix date error

* Address comments

* Update requirements
2017-05-02 08:38:27 -07:00
Paulus Schoutsen 350a6fd5fa Lint 2017-05-02 07:47:02 -07:00
Paulus Schoutsen f17c1090e1 Remove path whitelisting for hassio (#7399)
* Remove path whitelisting for hassio

* Update frontend

* Lint
2017-05-02 00:27:50 -07:00
Paulus Schoutsen 8ea6c7319a Migrate updater to aiohttp (#7387)
* Migrate updater to aiohttp

* Fix tests

* Update updater.py

* Docs
2017-05-01 23:29:01 -07:00
Paulus Schoutsen da2521a299 Fix YAML dump (#7388)
* Fix YAML dump

* Add test
2017-05-01 20:09:49 -07:00
Brian Cribbs 098e28534b fixing documentation 2017-05-01 13:34:34 -04:00
Brian Cribbs dc716cd971 repairing functionality for non-zero based ranges 2017-05-01 13:22:54 -04:00
Jack Minardi 7a24e210ae Try again to pass string to error msg 2017-05-01 09:31:23 -04:00
Daniel Perna fafd0d4e4c Fix impulse events, added error event for Homegear (#7349)
* Fix impulse event, added error event

* Requested changes
2017-05-01 06:37:00 +02:00
Robbie Trencheny 8ba7e61ed6 Merge pull request #7385 from robbiet480/dont-validate-ios-identify
Disable iOS device identify schema validation for now
2017-04-30 20:11:21 -07:00
Alok Saboo 86cfc2a0ed Updated docstrings (#7383)
* Updated docstrings

* Updated docstrings

* Updated docstrings

* Update docstrings

* Update more docstrings
2017-04-30 20:10:08 -07:00
Marcelo Moreira de Mello 6aac53399d Upgrade Ring to 0.1.4 (#7386) 2017-04-30 20:08:14 -07:00
Jack Minardi bc0559813c Dont add two strings inside logger call 2017-04-30 22:26:16 -04:00
Jack Minardi dd7690f265 Use % formatting 2017-04-30 21:31:55 -04:00
Jack Minardi b6827ce57a Use throwaray variable name 2017-04-30 21:02:03 -04:00
Robbie Trencheny ae93cf702a Merge pull request #7347 from cribbstechnologies/dev
Adding tilt functionality for MQTT cover
2017-04-30 15:56:45 -07:00
Robbie Trencheny ecf511ff35 Remove unused dependency 2017-04-30 15:55:34 -07:00
Robbie Trencheny 87ef26be22 Disable identify schema validation for now 2017-04-30 15:23:49 -07:00
Brian Cribbs 0fe0f1918a more requested changes 2017-04-30 16:33:29 -04:00
abmantis c085f06df5 Add support for shuffle toggling on Spotify component. (#7339)
* add support for shuffle toggling on Spotify component.

	this also required adding support for shuffle on the
	media_player component.

* lint

* Use ATTR_MEDIA_SHUFFLING for service handler param

* Line too long fix

* fix tests

* add shuffle set to demo mediaplayer

* rename shuffle attribute
2017-04-30 12:41:21 -07:00
ChristianKuehnel 5d7403bd81 Plant (replacement for MiGardener) (#7131)
* new implementation without mqtt

* fixed lint findings

* fixed more lint findings

* fixed final flak8 error

* added unit tests for platform "plant"

* - changed status to "OK" / "problem"
- added attribute "problem" with details on the problems
- removed unused constant
- setting icon to "?" until we have meaningful data

* reformatted code to meet line length requirements
2017-04-30 12:32:32 -07:00
Wojciech Bederski c14b829f27 improve handling of flux_led lights in RGBW mode (#7221)
allows simultaneous control of both RGB and White
channels.
2017-04-30 12:03:03 -07:00
Gergely Imreh f20a81d0c5 light.blinkt: add support for Blinkt! lights on Raspberry Pi (#7377) 2017-04-30 11:48:54 -07:00
Paulus Schoutsen 9afbbbf3fe Remove ordered_dict validator (#7375)
* Remove ordered_dict validator

* Lint

* Update test_config_validation.py
2017-04-30 10:55:03 -07:00
Fabian Affolter 1f4f2d7086 Upgrade voluptuous to 0.10.5 (#7107)
* Upgrade voluptuous to 0.10.5

* Fix tests
2017-04-30 00:42:19 -07:00
Dan Ports 7ff1ded0b5 binary_sensor.workday: fix handling of states vs provinces (#7162)
* binary_sensor.workday: fix handling of states vs provinces

* Add test cases for workday sensor with states

* remove redundant assignment

* Repair unit test to improve coverage

Patch from Wolf-Bastian Pöttner

* Fix handling of invalid states/provinces

* fix indentation to satisfy pylint
2017-04-30 00:31:46 -07:00
Henrik Nicolaisen 8df5de2bb8 optimize remote calls and apps on webostv media_player (#7191)
* - changed updater to only do updated if they succed and handle calls when tv is off better by only doing 1 remote call
- show all sources instead of only connected, to fix source selection when unit is powered off
- fixed sources so they can launch apps and select sources

* fixed lint errors

* show all sources and apps if no custom options are defined in the conf

* fixed indentation for lint

* set _current_source when state is off and fixed timeout exception
2017-04-30 00:30:37 -07:00
Brent Hughes 955e3e0542 Zoneminder: Fixed undefined index error (#7340)
* Zoneminder: Fixed undefined index error

* Add Pascal's correct fix.
2017-04-30 00:18:00 -07:00
Anders Melchiorsen 9f68fd9184 Flux switch: avoid updates when off (#7363)
* Flux switch: avoid updates when off

Calling turn_on when the switch is already on would orphan the existing
time tracker, losing our ability to cancel it when turn_off is called.

* Cleanups

The self.is_on property can now be found from self.unsub_tracker, so
get rid of the self._state attribute.

Add an entry guard to turn_on, making further conditionals unnecessary.
2017-04-30 00:05:29 -07:00
Paulus Schoutsen 85b7433bc4 Update sensehat.py 2017-04-29 22:42:12 -07:00
Fabian Affolter 3ee4d1060f Update docstrings (#7361)
* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update docstrings

* Update tomato.py

* Update isy994.py

* Lint + fix tests

* Lint
2017-04-29 22:04:49 -07:00
Paulus Schoutsen e22e70a01a Update sensehat.py 2017-04-29 21:40:10 -07:00
Paulus Schoutsen cf664e42cc Add assumed state to sensehat light. 2017-04-29 21:39:27 -07:00
Gergely Imreh b815ccc3b8 light.sensehat: plugin to control the 8x8 LED matrix on a Sense hat (#7365)
* light.sensehat: adding plugin to control the 8x8 LED matrix on a Sense Hat

* add new .coveragerc entry

* light.sensehat: formatting and removing unused import

* light.sensehat: add to requirements list

* light.sensehat: update docstrings to the linter's specs

* light.sensehat: add a bit more docstring
2017-04-29 21:34:31 -07:00
onsmam 7b1e948e8d Create knx.py (#7356)
* Create knx.py

light device for the knx component

* Fix doc strings
2017-04-29 21:20:46 -07:00
Jason Carter 607394a0a0 Netdisco now returns a dictionary while it used to be a tuple, fixed (#7350) 2017-04-29 20:54:45 -07:00
LvivEchoes e4ebae55d5 Feature/add mikrotik device tracker (#7366)
* Add Mikroik device tracker platform

* Update coveragerc with mikrotik.py

* Update coveragerc with mikrotik.py

* Fix lint errors
2017-04-29 20:39:11 -07:00
Paulus Schoutsen ce3d8be72b Remove binary sensor platforms implementing state property (#7371)
* Remove binary sensor platforms implementing state property

* Fix workday inheritance
2017-04-29 20:36:50 -07:00
Brian Cribbs a1be80d5d4 added optimistic configuration for tilt state 2017-04-29 19:27:19 -04:00
Brian Cribbs dc3706523a I was thinking *far* too hard about this 2017-04-29 19:19:24 -04:00
Paulus Schoutsen 55731b759f Fix lint 2017-04-29 15:47:21 -07:00
Paulus Schoutsen 064b2cdb9f Remove state property from alarmdecoder binary sensor (#7370) 2017-04-29 15:12:18 -07:00
Anders Melchiorsen 64a7be66b1 Remove global limit on white light temperature (#7206)
* Remove global limit on white light temperature

Here are the supported temperatures of some popular bulbs:

 Philips Hue: 2000K-6500K (the current 500-154 mired range)
 LIFX Color 1000: 2500K-9000K
 IKEA TRÅDFRI: 2200K, 2700K, 4000K

Obviously, Home Assistant cannot enforce a global limit and work properly
with all of these bulbs. So just remove the limit and leave it up to each
platform to work it out.

This commit updates the existing users and adds a clamp to Hue (where the
limit appears to have originated). It does not attempt to update other
platforms that might need extra handling of the larger range that is now
possible.

* Add min_mireds/max_mireds state attributes to lights

* Support min_mireds/max_mireds with LIFX lights
2017-04-29 15:04:20 -07:00
Fabian Affolter ae9f44c708 Upgrade speedtest-cli to 1.0.6 (#7354) 2017-04-30 00:00:04 +02:00
Fabian Affolter 74362df19c Upgrade xmltodict to 0.11.0 (#7355) 2017-04-29 23:59:38 +02:00
Greg Dowling d8afea64af Add debug logging to pyvera events. (#7364) 2017-04-29 17:19:22 +01:00
Daniel Høyer Iversen b853fb2178 Upgrade Flux led lb to 0.19 (#7352) 2017-04-29 07:46:34 +02:00
Brian Cribbs 5b22e57643 some changes requested on PR 2017-04-28 21:41:50 -04:00
Brian Cribbs 88782fa90a added coroutine annotation to async method 2017-04-28 16:32:06 -04:00
Brian Cribbs 4c06cca3e1 left trailing whitespace 2017-04-28 13:58:01 -04:00
Brian Cribbs d3042a8199 forgot to remove constructor arg 2017-04-28 13:57:13 -04:00
Brian Cribbs e61c1ffbc2 fixing hound violations 2017-04-28 13:25:15 -04:00
Brian Cribbs 6cfc1b6af8 removing unnecessary payload value for tilt 2017-04-28 13:15:05 -04:00
Brian Cribbs c120c47bc1 style changes 2017-04-28 12:14:17 -04:00
Brian Cribbs 44dbf2678b adding tests to cover new functionality 2017-04-28 11:32:42 -04:00
Brian Cribbs bbe8b2019b style/lint updates 2017-04-28 10:45:32 -04:00
Fabian Affolter f9c9b3c1b8 Multiple changes (typo, ordering, docstrings, timeouts) (#7343)
* Multiple changes (typo, ordering, docstrings, timeouts)

* Remove debug output

* Catch exception

* Separate URL
2017-04-28 13:25:39 +02:00
Robbie Trencheny 9dd28ac18f Properly return self._unit_of_measurement in the unit_of_measurement function (#7341)
Add an optional extended description…
2017-04-28 12:26:17 +02:00
Thibault Cohen 8114c45b3d Add auxheat to ecobee climate (#6562)
* Add auxheat to ecobee

* Add is_aux_heat_on property in ecobee climate
2017-04-28 11:52:48 +02:00
Anders Melchiorsen 89164d0244 Allow multiple recipients for SMTP notify (#7319)
The existing (singular) configuration keyword is kept for compatibility.
2017-04-28 11:29:30 +02:00
Brian Cribbs 79e134b076 removing accidentally added file 2017-04-27 23:43:54 -04:00
Brian Cribbs 630516f309 slider and tilt open/close seem to work 2017-04-27 23:42:50 -04:00
Fabian Affolter 0298522fd5 Use four-digits year (#7336) 2017-04-27 09:30:34 -07:00
Paulus Schoutsen 9a67111a0f Right fix for Python Open Z-Wave in Docker (#7337) 2017-04-27 09:28:08 -07:00
Adam Mills 6631e9e939 Version bump for automatic (#7329) 2017-04-27 08:01:30 -04:00
Paulus Schoutsen 3e4e84e3a7 Re-enable Open Z-Wave in Dockerfile (#7325)
Turbo fixed it in #7316
2017-04-27 01:02:14 -07:00
John Arild Berentsen ce63bb0842 Fix broken docker build (#7316)
* Fix broken docker build

python3 branch

* Update python_openzwave
2017-04-27 01:00:07 -07:00
Anders Melchiorsen d1121478ab Reduce color_xy_brightness_to_hsv to color_xy_to_hs (#7320)
This makes more sense because the input and output brightness is the same.

We currently convert through RGB which does contain a brightness so we supply
an arbitrary value as input and discard the output.
2017-04-27 00:59:49 -07:00
Paulus Schoutsen 9527390736 Update frontend (#7324)
* Initial version of hassio panel

* Update frontend
2017-04-27 00:57:40 -07:00
Paulus Schoutsen a732271793 HassIO API fix 2017-04-26 23:24:02 -07:00
Paulus Schoutsen b532267596 HassIO API fix 2017-04-26 23:23:18 -07:00
Paulus Schoutsen b14c07a60c Hassio api v3 (#7323)
* HassIO rest API v3

* fix content type

* fix lint

* Update comment

* fix content type

* change proxy handling

* fix handling

* fix register

* fix addons

* fix routing

* Update hassio to just proxy

* Fix tests

* Lint
2017-04-26 22:36:48 -07:00
Jan Losinski 3374169c74 Allow InfluxDB to blacklist domains (#7264)
* Allow InfluxDB to blacklist domains

This adds an option to InfluxDB to blacklist whole domains. This is
useful for domains like automation or script, where no statistic data
is needed.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Add unittest for InfluxDB domain blacklist

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Use common include/exclude config for InfluxDB.

Its now the same syntax as it is for recorder.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Add unittests for InfluxDB include whitelist.

There where no tests for that feature before.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2017-04-26 21:14:52 +02:00
Anders Melchiorsen ffb8872f95 LIFX: use white light when setting a specific temperature (#7256)
* Default to white when setting LIFX temperature

Changing the temperature of a saturated color is possible but not very
interesting. So assume that a change of the temperature implies setting
the color to white, unless a different color is actually specified.

This makes the frontend temperature picker much more useful because it can
now be used to get away from a colored light.

* Default to a neutral white temperature when setting LIFX colors

This means that setting a particular color will always give the same output,
no matter what the temperature was previously at.

* Find brightness after colors

Now the color_temp logic will not see a changed color when setting
temperature+brightness and thus we will actually get a white light in
this situation.

The XY conversion can then not use brightness as input. This is not
an issue because XY only affects hue and saturation, not brightness. So
we can just use an arbitrary value as brightness input.

* Add a simple comment to a complex conditional
2017-04-26 17:21:14 +02:00
Paulus Schoutsen d2fb4675e1 Disable Open Z-Wave in Docker (#7315) 2017-04-26 07:37:05 -07:00
Fabian Affolter 569ea0cc01 Upgrade python-telegram-bot to 5.3.1 (#7311) 2017-04-26 10:50:08 +02:00
Adam Mills 00f034cef2 Version bump of aioautomatic (#7300)
* Version bump of aioautomatic

* Update requirements_all.txt
2017-04-25 20:21:16 -07:00
Henrik Nicolaisen 615691efc3 Issue 7218 update pylgtv to 0.1.7 (#7302)
* update pylgtv module to 0.1.7

* update pylgtv to 0.1.7 requirements
2017-04-25 20:20:54 -07:00
Daniel Perna 760d2f1f0a Upgrade pyhomematic, extend device support (#7303) 2017-04-25 20:20:10 -07:00
Paulus Schoutsen 8bb952e416 Fix breaking SSL in test HTML5 (#7310) 2017-04-25 20:18:23 -07:00
Fabian Affolter f29e0bf53e Don't stack up error messages, fix link, and ordering (#7291) 2017-04-25 12:40:13 +02:00
Alan Fischer 28aab33cd1 Added scene controller support to the vera component, along with proper polling when a vera device needs it (#7234)
Add an optional extended description…
2017-04-25 09:17:25 +02:00
Russell Cloran d79f89e168 Add support for Zigbee Home Automation (#6263)
* Add support for Zigbee Home Automation

* Fewer magic numbers

* Make optional device config work

* Remove non-zha device_tracker stuff

* Always return boolean from is_on

* Only pass through JSON serializable discovery_info

* Update to bellows 0.2.4

* Fewer magic numbers in binary sensor

* Populate const structures from a function

* Update bellows to 0.2.6

* Fewer magic numbers in light

* Take all possible clusters when overriding

* Update bellows to 0.2.7
2017-04-24 22:24:57 -07:00
micw 699cc7213d Feature/rss feed template (#7032)
* rss_feed_template initial checking

* lint

* Remove use of deprecated cgi-escape()

* Switching back to chardet==2.3 (resolve failing tests with 3.0)

* Code and test improvments

* Option 'requires_api_password', default is True
2017-04-24 22:16:47 -07:00
Ron Klinkien f65d8e1254 Adding group control to tradfri light component (#7248)
* Added initial support for tradfri group control

* Tried to keep original variable structure

* pylint and pep8 fixes

* Fixed lint error about docstring

* Removed unneeded stuff, renamed _light. Needs to be released pytradfri version.

* Better naming of variables inside add_devices call.
2017-04-24 21:57:38 -07:00
Paulus Schoutsen fb297981af Upgrade pytradfri to 1.1 (#7290) 2017-04-24 21:57:27 -07:00
Jack Minardi 8bf1c21738 Add space 2017-04-25 00:48:23 -04:00
Jack Minardi 943861a8a3 Remove unused var 2017-04-25 00:45:59 -04:00
Jack Minardi 450fd7f2b5 Log out of router admin interface after devices are recorded. 2017-04-25 00:34:26 -04:00
Jack Minardi 2d5da3e958 Catch KeyError; Add response.text to error message 2017-04-25 00:32:31 -04:00
Paulus Schoutsen 66a63b983e Merge branch 'master' into dev 2017-04-24 21:10:03 -07:00
Paulus Schoutsen 335362ffc4 Recorder: Check for ENTITY_ID key that contains None value (#7287) 2017-04-24 20:51:03 -07:00
Keaton Taylor 17bdb9558a Fixes utf-8 encoding no longer required by python-openzwave0.3.3 (#7266)
* Fixes utf-8 encoding no longer required by libopenzwave0.3.3

Removes byte encoding for values operation mode, fan mode and swing
mode.

* Fix zwave climate tests for utf-8 change
2017-04-24 17:59:51 -07:00
Patrick Easters 9738bffc3f Updating ping binary sensor with Windows support (#7253)
Fixed ping command syntax and updated regex match
2017-04-24 23:32:12 +02:00
Pascal Vizeli f58d200ecb Fix HassIO bug with supervisor update & log (#7282) 2017-04-24 23:22:40 +02:00
Jan Losinski 215987d5a7 Add script to import state events to InfluxDB (#7254)
* Add script to import state events to InfluxDB

This adds a script to import recorded events from a recorder database
to a InfluxDB instance. This can be useful for initial importing after
setup of a InfluxDB.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Fix step argument handling in Influx import

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Correct typo in InfluxDB Importer

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>

* Update influxdb_import.py
2017-04-24 23:01:09 +02:00
Nils Uliczka 16227704d7 Fix telegram_polling no first_name or last_name (#7281)
* Default to 'N/A' if sender has no first_name or last_name

* Fixed as requested
2017-04-24 22:08:06 +02:00
Fabrizio Furnari aad375b713 Add https certificate expiry sensor (#7272)
* fixing rebase issues

* cert_expiry: added .coveragerc entry

* cert_expiry: renamed to SCAN_INTERVAL, removed Throttle

* cert_expiry: better socket exception management

* cert_expiry: splitted line too long

* Update cert_expiry.py

* Fix hass style
2017-04-24 22:01:00 +02:00
LvivEchoes 104d372dfa Add support for Ukrainian Language in Google TTS (#7278) 2017-04-24 21:43:56 +02:00
Stuart Mumford 7960206e2e Refactor matrix notify service (#7122)
* Refactor matrix notify service.

This refactor aims to close #6118 by making the save / restore of the
authentication tokens much more resilient to failure.

It also refactors the module so that all the functionality is part of the class
and that a login failure causes the service to fail on setup rather than at
message send time.

* Make the linter overlords happy

* Improve logger levels and messages

* small style change

* Fix indentation issue
2017-04-24 21:43:02 +02:00
Anders Melchiorsen 4f5ec3e360 Update aiolifx (#7279)
This contains a fix for TypeError bug seen only with LIFX Z.
2017-04-24 21:09:14 +02:00
Pascal Vizeli ead457f312 WIP: HassIO allow to access to container logs. (#7271)
* HassIO allow to access to container logs.

* Add unittest & make a fixture for env

* Add unittest to check if no env exists

* Fix lint
2017-04-24 15:16:28 +02:00
Daniel Høyer Iversen 575f57a24e Rfxtrx upgrade lib 0.18 (#7273) 2017-04-24 13:42:26 +02:00
Klaas Hoekema 64da8cd47d Work around bad content-type in Hook api response (#7267) 2017-04-23 23:58:17 -07:00
Martin Hjelmare 0e662c4007 Fix telegram webhooks (#7236)
* Always register the view if a webhook exists.
* Return True if platform is set up succesfully, False otherwise.
* Remove the webhook when home assistant stops. Webhooks and long
  polling are mutually excklusive. If a webhook is left after home
  assistant is stopped, a polling telegram bot is unable to be set up,
  on next start of home assistant.
2017-04-23 23:20:04 -07:00
Greg Dowling 6a8a656fef Workround for wemo subscription bug. (#7245) 2017-04-23 23:18:43 -07:00
Fabian Affolter cfc023e128 Don't use len(SEQUENCE) as condition value (#7249)
* Don't use len(SEQUENCE) as condition value

* Update volvooncall.py
2017-04-23 20:41:09 -07:00
Fabian Affolter 15b2473224 Iterating the dictionary directly (#7251) 2017-04-23 20:16:52 -07:00
clayton craft 48eeb55198 Add notice regarding submission of analytics (#7263) 2017-04-23 19:59:26 -07:00
Oliver 4cd024d91e Pushed to version 0.4.0 of denonavr which also includes experimental support for Marantz receivers (#7250) 2017-04-23 21:00:00 +02:00
Fabian Affolter fa4a912a86 Use consts and string formatting (#7243) 2017-04-23 13:54:39 +02:00
Fabian Affolter 209da6f338 Upgrade aiohttp_cors to 0.5.3 (#7213) 2017-04-23 09:25:58 +02:00
Fabian Affolter ec5e9fcd0d Upgrade paho-mqtt to 1.2.3 (#7214) 2017-04-23 09:25:34 +02:00
Fabian Affolter e9eb7edda6 Upgrade speedtest-cli to 1.0.5 (#7215) 2017-04-23 09:25:11 +02:00
Fabian Affolter b60b06a062 Upgrade mutagen to 1.37.0 (#7216) 2017-04-23 09:24:53 +02:00
Fabian Affolter efe8b46576 Upgrade pygatt to 3.1.1 (#7220)
* Upgrade pygatt to 3.1.1

* Fix mess
2017-04-23 09:24:26 +02:00
Anders Melchiorsen 91b8eea6ad LIFX: avoid "Unable to remove unknown listener" warning (#7235)
Forget the cancelled update handler so it is not cancelled a second time
later on (if when <= BULB_LATENCY) and thus invoking the warning.
2017-04-23 09:24:08 +02:00
Adam Mills b6a4a0d9af Refactor lyft sensor update (#7233) 2017-04-23 09:23:00 +02:00
Matthew Garrett 7b3cc9fe1f Bump a couple of dependencies (#7231)
* avion light: Bump python-avion dependency version

The dependencies in python-avion weren't sufficiently strict. This is now
fixed, but means we need to depend on a new version.

* decora light: Bump python-decora dependency

There's a new version of python-decora with a reliability fix, so depend on
that.
2017-04-23 09:20:58 +02:00
Dan Ports 2c39038507 lyft sensor: re-enable Prime Time rate attribute (#6982)
Turns out this does work correctly even without a user login
(assuming that sandbox mode is disabled)
2017-04-22 21:16:06 +02:00
Fabian Affolter 5bfe5b3f70 Remove superfluous comments and update ordering (#7227)
* Remove superfluous comments and update ordering

* Fix pylint issues
2017-04-22 21:13:04 +02:00
Alok Saboo d229787fa6 Fixed typo and clarified details for Lifx effects (#7226)
* Fixed typo

* Update services.yaml

* Clarified service details for Lifx effects
2017-04-22 20:43:17 +02:00
John Arild Berentsen 1836c7a358 Zwave cover workaround for graber shades. (#7204)
* wierd pylint complaint

* Workaround for Graber csz1 shades

* logging

* Try direct

* Try direct

* Use workaround

* Review changes and tests

* test

* reset test

* Use Bright and Dim also as open and close is
2017-04-22 16:23:39 +02:00
Joakim af Sandeberg 1b83ce8759 Pushbullet notification sensor (#7182)
* Added the pushbullet sensor component

* Updated requirements_all.txt and .coveragerc with the new sensor

* Updated acording to houndci-bots comments

* Some more changes

* Final change by the hound (?)

* Fixes from balloobs review and from houndci-bot

This changes the sensors information to only contain one attribute
as information, and the rest as device_state_attributes.

* Added leading space to comments

* Added docstrings, removed API_KEY from log, changed imports

* The hound is at it again

* Fix remaining issues

* Fix pylint issue
2017-04-22 14:01:30 +02:00
Fabian Affolter 8c72a57344 Bump version to 0.44.0.dev0 (#7217) 2017-04-22 11:52:24 +02:00
Martin Hjelmare 3f47bf6b77 Fix tradfri lights (#7212)
* Remove leftover use of slugify

* The IKEA manufacturer key is now exactly as found in device info.

* Fix bitwise addition of supported features
2017-04-22 00:31:23 -07:00
Paulus Schoutsen 40f480c24e tradfri: Improve color temp support detection (#7211) 2017-04-21 23:32:51 -07:00
Henrik Nicolaisen 8e716780b7 Issue 6749 updated pylgtv to 0.1.6 to fix thread leak in asyncio loop (#7199)
* updated pylgtv module to fix problems with timeouts

* - update pylgtv to 0.1.6
- handle new TimeoutError exception from pylgtv

* used full name for exception handling of concurrent.futures._base.TimeoutError

* the exception handling should now follow the rules

* float typecasting should not be necessary

* use asyncio for TimeoutError it’s an alias for concurrent.futures.TimeoutError
2017-04-21 20:24:21 -07:00
Sean Dague b77b22b01a Fix arwn platform to update hass state when events are received (#7202)
The arwn platform was refactored to be asyncio friendly, however in
doing so one thing was missed which was explicitly telling hass when
something interesting has happened. This led to the very interesting
to debug issue that the state cards were all out of date, even though
the graphs were not.
2017-04-21 20:22:36 -07:00
Nikolas Beutler 1194690c42 Update ios.py (#7160)
* Update ios.py

as discussed. the part: 
       if battery_state == ios.ATTR_BATTERY_STATE_FULL:
            returning_icon_level = DEFAULT_ICON_LEVEL
kinda screws up the charging icon.

i might just miss a logical solution for that though.
let me know what you think. it might not be beautiful but i think its an overall improve over the current "double battery" solution

* Update ios.py

chound fix and full_battery_charge fix

* Update ios.py

removed new line

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* Update ios.py

* merged request from robbie

* Update ios.py

* Update ios.py

* Update ios.py
2017-04-21 20:16:59 -07:00
Anders Melchiorsen 2657668a86 Support xy_color with LIFX lights (#7208) 2017-04-21 20:16:36 -07:00
Pierre Ståhl f5dd25c87f Capture and log pip install error output (#7200)
Add an optional extended description…
2017-04-21 14:15:05 +02:00
Pascal Vizeli 0acc52b23b HassIO API v2 (#7201)
Add an optional extended description…
2017-04-21 12:20:19 +02:00
Anders Melchiorsen d4b085081a LIFX light effects (#7145)
* Refactor into find_hsbk

This will be useful for new methods that also have to find passed in colors.

* Add AwaitAioLIFX

This encapsulates the callback and Event that aiolifx needs and thus avoids an
explosion of those when new calls are added.

The refresh_state is now generally useful, so move it into its own method.

* Initial effects support for LIFX

These effects are useful as notifications. They mimic the breathe and pulse
effects from the LIFX HTTP API:

    https://api.developer.lifx.com/docs/breathe-effect
    https://api.developer.lifx.com/docs/pulse-effect

However, this implementation runs locally with the LIFX LAN protocol.

* Saturate LIFX no color value

Now the color is "full saturation, no brightness". This avoids a lot of
temporary white when fading from the "no color" value and into a real color.

* Organize LIFX effects in classes

This is to move the setup/restore away from the actual effect, making it quite
simple to add additional effects.

* Stop running LIFX effects on conflicting service calls

Turning the light on/off or starting a new effect will now stop the running
effect.

* Present default LIFX effects as light.turn_on effects

This makes the effects (with default parameters) easily accessible from
the UI.

* Add LIFX colorloop effect

This cycles the HSV colors, so that is added as an internal way to set a
color.

* Move lifx to its own package and split effects into a separate file

* Always show LIFX light name in logs

The name is actually the easiest way to identify a bulb so just using it
as a fallback was a bit odd.

* Compact effect getter

* Always use full brightness for random flash color

This is a stopgap. When a bit more infrastructure is in place, the intention
is to turn the current hue some degrees. This will guarantee a flash color
that is both unlike the current color and unlike white.

* Clear effects concurrently

We have to wait for the bulbs, so let us wait for all of them at once.

* Add lifx_effect_stop

The colorloop effect is most impressive if run on many lights. Testing
this has revealed the need for an easy way to stop effects on all lights
and return to the initial state of each bulb. This new call does just that.

Calling turn_on/turn_off could also stop the effect but that would not
restore the initial state.

* Always calculate the initial effect color

To fade nicely from power off, the breathe effect needs to keep an
unchanging hue. So give up on using a static start color and just find the
correct hue from the target color.

The colorloop effect can start from anything but we use a random color
just to keep things a little interesting during power on.

* Fix lint

* Update .coveragerc
2017-04-20 22:46:12 -07:00
872 changed files with 23585 additions and 9910 deletions
+42
View File
@@ -20,6 +20,12 @@ omit =
homeassistant/components/android_ip_webcam.py
homeassistant/components/*/android_ip_webcam.py
homeassistant/components/arlo.py
homeassistant/components/*/arlo.py
homeassistant/components/axis.py
homeassistant/components/*/axis.py
homeassistant/components/bbb_gpio.py
homeassistant/components/*/bbb_gpio.py
@@ -59,6 +65,9 @@ omit =
homeassistant/components/isy994.py
homeassistant/components/*/isy994.py
homeassistant/components/kira.py
homeassistant/components/*/kira.py
homeassistant/components/lutron.py
homeassistant/components/*/lutron.py
@@ -77,15 +86,27 @@ omit =
homeassistant/components/octoprint.py
homeassistant/components/*/octoprint.py
homeassistant/components/opencv.py
homeassistant/components/*/opencv.py
homeassistant/components/qwikswitch.py
homeassistant/components/*/qwikswitch.py
homeassistant/components/rachio.py
homeassistant/components/*/rachio.py
homeassistant/components/raspihats.py
homeassistant/components/*/raspihats.py
homeassistant/components/rfxtrx.py
homeassistant/components/*/rfxtrx.py
homeassistant/components/rpi_gpio.py
homeassistant/components/*/rpi_gpio.py
homeassistant/components/rpi_pfio.py
homeassistant/components/*/rpi_pfio.py
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
@@ -154,6 +175,13 @@ omit =
homeassistant/components/tado.py
homeassistant/components/*/tado.py
homeassistant/components/zha/__init__.py
homeassistant/components/zha/const.py
homeassistant/components/*/zha.py
homeassistant/components/eight_sleep.py
homeassistant/components/*/eight_sleep.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/concord232.py
homeassistant/components/alarm_control_panel/nx584.py
@@ -165,6 +193,8 @@ omit =
homeassistant/components/binary_sensor/flic.py
homeassistant/components/binary_sensor/hikvision.py
homeassistant/components/binary_sensor/iss.py
homeassistant/components/binary_sensor/mystrom.py
homeassistant/components/binary_sensor/pilight.py
homeassistant/components/binary_sensor/ping.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/browser.py
@@ -182,15 +212,18 @@ omit =
homeassistant/components/climate/oem.py
homeassistant/components/climate/proliphix.py
homeassistant/components/climate/radiotherm.py
homeassistant/components/climate/sensibo.py
homeassistant/components/cover/garadget.py
homeassistant/components/cover/homematic.py
homeassistant/components/cover/myq.py
homeassistant/components/cover/opengarage.py
homeassistant/components/cover/rpi_gpio.py
homeassistant/components/cover/scsgate.py
homeassistant/components/cover/wink.py
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
homeassistant/components/device_tracker/asuswrt.py
homeassistant/components/device_tracker/automatic.py
homeassistant/components/device_tracker/bbox.py
homeassistant/components/device_tracker/bluetooth_le_tracker.py
homeassistant/components/device_tracker/bluetooth_tracker.py
@@ -201,6 +234,7 @@ omit =
homeassistant/components/device_tracker/icloud.py
homeassistant/components/device_tracker/linksys_ap.py
homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/mikrotik.py
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/device_tracker/ping.py
@@ -222,10 +256,14 @@ omit =
homeassistant/components/foursquare.py
homeassistant/components/hdmi_cec.py
homeassistant/components/ifttt.py
homeassistant/components/image_processing/dlib_face_detect.py
homeassistant/components/image_processing/dlib_face_identify.py
homeassistant/components/image_processing/seven_segments.py
homeassistant/components/joaoapps_join.py
homeassistant/components/keyboard.py
homeassistant/components/keyboard_remote.py
homeassistant/components/light/avion.py
homeassistant/components/light/blinkt.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/decora.py
homeassistant/components/light/flux_led.py
@@ -238,6 +276,7 @@ omit =
homeassistant/components/light/osramlightify.py
homeassistant/components/light/rpi_gpio_pwm.py
homeassistant/components/light/piglow.py
homeassistant/components/light/sensehat.py
homeassistant/components/light/tikteck.py
homeassistant/components/light/tradfri.py
homeassistant/components/light/x10.py
@@ -335,6 +374,7 @@ omit =
homeassistant/components/sensor/broadlink.py
homeassistant/components/sensor/dublin_bus_transport.py
homeassistant/components/sensor/coinmarketcap.py
homeassistant/components/sensor/cert_expiry.py
homeassistant/components/sensor/comed_hourly_pricing.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/crimereports.py
@@ -350,6 +390,7 @@ omit =
homeassistant/components/sensor/eddystone_temperature.py
homeassistant/components/sensor/eliqonline.py
homeassistant/components/sensor/emoncms.py
homeassistant/components/sensor/envirophat.py
homeassistant/components/sensor/fastdotcom.py
homeassistant/components/sensor/fedex.py
homeassistant/components/sensor/fido.py
@@ -391,6 +432,7 @@ omit =
homeassistant/components/sensor/pi_hole.py
homeassistant/components/sensor/plex.py
homeassistant/components/sensor/pocketcasts.py
homeassistant/components/sensor/pushbullet.py
homeassistant/components/sensor/pvoutput.py
homeassistant/components/sensor/qnap.py
homeassistant/components/sensor/sabnzbd.py
+9 -5
View File
@@ -1,13 +1,15 @@
sudo: false
addons:
apt:
packages:
- libudev-dev
matrix:
fast_finish: true
include:
- python: "3.4.2"
env: TOXENV=py34
- python: "3.4.2"
env: TOXENV=requirements
- python: "3.4.2"
env: TOXENV=lint
- python: "3.4.2"
env: TOXENV=py34
# - python: "3.5"
# env: TOXENV=typing
- python: "3.5"
@@ -16,6 +18,8 @@ matrix:
env: TOXENV=py36
- python: "3.6-dev"
env: TOXENV=py36
- python: "3.4.2"
env: TOXENV=requirements
# allow_failures:
# - python: "3.5"
# env: TOXENV=typing
@@ -25,5 +29,5 @@ cache:
- $HOME/.cache/pip
install: pip install -U tox coveralls
language: python
script: tox
script: travis_wait tox
after_success: coveralls
+2 -3
View File
@@ -1,11 +1,10 @@
FROM python:3.5
FROM python:3.6
MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
# Uncomment any of the following lines to disable the installation.
#ENV INSTALL_TELLSTICK no
#ENV INSTALL_OPENALPR no
#ENV INSTALL_FFMPEG no
#ENV INSTALL_OPENZWAVE no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
#ENV INSTALL_COAP_CLIENT no
@@ -27,4 +26,4 @@ RUN pip3 install --no-cache-dir -r requirements_all.txt && \
# Copy source
COPY . .
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
+4 -6
View File
@@ -1,8 +1,6 @@
<ul>
<li><a href="https://community.home-assistant.io">📌 Community Forums</a></li>
<li><a href="https://github.com/home-assistant/home-assistant">🚀 GitHub</a></li>
<li><a href="https://home-assistant.io/">🏡 Homepage</a></li>
<li><a href="https://gitter.im/home-assistant/home-assistant">💬 Gitter</a></li>
<li><a href="https://pypi.python.org/pypi/homeassistant">💾 Download Releases</a></li>
<li><a href="https://home-assistant.io/">Homepage</a></li>
<li><a href="https://community.home-assistant.io">Community Forums</a></li>
<li><a href="https://github.com/home-assistant/home-assistant">GitHub</a></li>
<li><a href="https://gitter.im/home-assistant/home-assistant">Gitter</a></li>
</ul>
<hr>
+13 -3
View File
@@ -1,4 +1,4 @@
"""Starts home assistant."""
"""Start Home Assistant."""
from __future__ import print_function
import argparse
@@ -10,6 +10,7 @@ import threading
from typing import Optional, List
from homeassistant import monkey_patch
from homeassistant.const import (
__version__,
EVENT_HOMEASSISTANT_START,
@@ -17,7 +18,6 @@ from homeassistant.const import (
REQUIRED_PYTHON_VER_WIN,
RESTART_EXIT_CODE,
)
from homeassistant.util.async import run_callback_threadsafe
def attempt_use_uvloop():
@@ -277,7 +277,7 @@ def cmdline() -> List[str]:
def setup_and_run_hass(config_dir: str,
args: argparse.Namespace) -> Optional[int]:
"""Setup HASS and run."""
"""Set up HASS and run."""
from homeassistant import bootstrap
# Run a simple daemon runner process on Windows to handle restarts
@@ -310,6 +310,9 @@ def setup_and_run_hass(config_dir: str,
return None
if args.open_ui:
# Imported here to avoid importing asyncio before monkey patch
from homeassistant.util.async import run_callback_threadsafe
def open_browser(event):
"""Open the webinterface in a browser."""
if hass.config.api is not None:
@@ -371,6 +374,13 @@ def main() -> int:
"""Start Home Assistant."""
validate_python()
if os.environ.get('HASS_MONKEYPATCH_ASYNCIO') == '1':
if sys.version_info[:3] >= (3, 6):
monkey_patch.disable_c_asyncio()
monkey_patch.patch_weakref_tasks()
elif sys.version_info[:3] < (3, 5, 3):
monkey_patch.patch_weakref_tasks()
attempt_use_uvloop()
if sys.version_info[:3] < (3, 5, 3):
+13 -15
View File
@@ -1,4 +1,4 @@
"""Provides methods to bootstrap a home assistant instance."""
"""Provide methods to bootstrap a Home Assistant instance."""
import asyncio
import logging
import logging.handlers
@@ -83,8 +83,7 @@ def async_from_config_dict(config: Dict[str, Any],
conf_util.async_log_exception(ex, 'homeassistant', core_config, hass)
return None
yield from hass.loop.run_in_executor(
None, conf_util.process_ha_config_upgrade, hass)
yield from hass.async_add_job(conf_util.process_ha_config_upgrade, hass)
if enable_log:
async_enable_logging(hass, verbose, log_rotate_days)
@@ -95,7 +94,7 @@ def async_from_config_dict(config: Dict[str, Any],
'This may cause issues.')
if not loader.PREPARED:
yield from hass.loop.run_in_executor(None, loader.prepare, hass)
yield from hass.async_add_job(loader.prepare, hass)
# Merge packages
conf_util.merge_packages_config(
@@ -184,14 +183,13 @@ def async_from_config_file(config_path: str,
# Set config dir to directory holding config file
config_dir = os.path.abspath(os.path.dirname(config_path))
hass.config.config_dir = config_dir
yield from hass.loop.run_in_executor(
None, mount_local_lib_path, config_dir)
yield from hass.async_add_job(mount_local_lib_path, config_dir)
async_enable_logging(hass, verbose, log_rotate_days)
try:
config_dict = yield from hass.loop.run_in_executor(
None, conf_util.load_yaml_config_file, config_path)
config_dict = yield from hass.async_add_job(
conf_util.load_yaml_config_file, config_path)
except HomeAssistantError as err:
_LOGGER.error('Error loading %s: %s', config_path, err)
return None
@@ -206,7 +204,7 @@ def async_from_config_file(config_path: str,
@core.callback
def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
log_rotate_days=None) -> None:
"""Setup the logging.
"""Set up the logging.
This method must be run in the event loop.
"""
@@ -214,12 +212,12 @@ def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
"[%(name)s] %(message)s")
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
datefmt = '%y-%m-%d %H:%M:%S'
datefmt = '%Y-%m-%d %H:%M:%S'
# suppress overly verbose logs from libraries that aren't helpful
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
# Suppress overly verbose logs from libraries that aren't helpful
logging.getLogger('requests').setLevel(logging.WARNING)
logging.getLogger('urllib3').setLevel(logging.WARNING)
logging.getLogger('aiohttp.access').setLevel(logging.WARNING)
try:
from colorlog import ColoredFormatter
@@ -274,7 +272,7 @@ def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
else:
_LOGGER.error(
'Unable to setup error log %s (access denied)', err_log_path)
"Unable to setup error log %s (access denied)", err_log_path)
def mount_local_lib_path(config_dir: str) -> str:
+2 -2
View File
@@ -102,10 +102,10 @@ def reload_core_config(hass):
@asyncio.coroutine
def async_setup(hass, config):
"""Setup general services related to Home Assistant."""
"""Set up general services related to Home Assistant."""
@asyncio.coroutine
def async_handle_turn_service(service):
"""Method to handle calls to homeassistant.turn_on/off."""
"""Handle calls to homeassistant.turn_on/off."""
entity_ids = extract_entity_ids(hass, service)
# Generic turn on/off method requires entity id
@@ -123,8 +123,8 @@ def async_setup(hass, config):
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
for service in SERVICE_TO_METHOD:
@@ -158,8 +158,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_disarm, code)
return self.hass.async_add_job(self.alarm_disarm, code)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
@@ -170,8 +169,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_arm_home, code)
return self.hass.async_add_job(self.alarm_arm_home, code)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
@@ -182,8 +180,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_arm_away, code)
return self.hass.async_add_job(self.alarm_arm_away, code)
def alarm_trigger(self, code=None):
"""Send alarm trigger command."""
@@ -194,8 +191,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(
None, self.alarm_trigger, code)
return self.hass.async_add_job(self.alarm_trigger, code)
@property
def state_attributes(self):
@@ -25,7 +25,7 @@ DEPENDENCIES = ['alarmdecoder']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Perform the setup for AlarmDecoder alarm panels."""
"""Set up for AlarmDecoder alarm panels."""
_LOGGER.debug("AlarmDecoderAlarmPanel: setup")
device = AlarmDecoderAlarmPanel("Alarm Panel", hass)
@@ -44,7 +44,7 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
self._name = name
self._state = STATE_UNKNOWN
_LOGGER.debug("AlarmDecoderAlarm: Setting up panel")
_LOGGER.debug("Setting up panel")
@asyncio.coroutine
def async_added_to_hass(self):
@@ -78,12 +78,12 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
@property
def should_poll(self):
"""No polling needed."""
"""Return the polling state."""
return False
@property
def code_format(self):
"""Regex for code format or None if no code is required."""
"""Return the regex for code format or None if no code is required."""
return '^\\d{4,6}$'
@property
@@ -94,26 +94,23 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
"""Send disarm command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_disarm: %s", code)
_LOGGER.debug("alarm_disarm: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_disarm: sending %s1",
str(code))
_LOGGER.debug("alarm_disarm: sending %s1", str(code))
self.hass.data[DATA_AD].send("{!s}1".format(code))
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_away: %s", code)
_LOGGER.debug("alarm_arm_away: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_away: sending %s2",
str(code))
_LOGGER.debug("alarm_arm_away: sending %s2", str(code))
self.hass.data[DATA_AD].send("{!s}2".format(code))
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_home: %s", code)
_LOGGER.debug("alarm_arm_home: %s", code)
if code:
_LOGGER.debug("AlarmDecoderAlarm::alarm_arm_home: sending %s3",
str(code))
_LOGGER.debug("alarm_arm_home: sending %s3", str(code))
self.hass.data[DATA_AD].send("{!s}3".format(code))
@@ -1,5 +1,4 @@
"""
Interfaces with Alarm.com alarm control panels.
For more details about this platform, please refer to the documentation at
@@ -33,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a Alarm.com control panel."""
"""Set up a Alarm.com control panel."""
name = config.get(CONF_NAME)
code = config.get(CONF_CODE)
username = config.get(CONF_USERNAME)
@@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class Concord232Alarm(alarm.AlarmControlPanel):
"""Represents the Concord232-based alarm panel."""
"""Representation of the Concord232-based alarm panel."""
def __init__(self, hass, url, name):
"""Initialize the Concord232 alarm panel."""
@@ -79,7 +79,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""The characters if code is defined."""
"""Return the characters if code is defined."""
return '[0-9]{4}([0-9]{2})?'
@property
@@ -117,7 +117,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._alarm.arm('home')
self._alarm.arm('stay')
def alarm_arm_away(self, code=None):
"""Send arm away command."""
@@ -8,7 +8,7 @@ import homeassistant.components.alarm_control_panel.manual as manual
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo alarm control panel platform."""
"""Set up the Demo alarm control panel platform."""
add_devices([
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10, False),
])
@@ -70,8 +70,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
device.async_alarm_keypress(keypress)
# Register Envisalink specific services
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
hass.services.async_register(
@@ -104,7 +104,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
@callback
def _update_callback(self, partition):
"""Update HA state, if needed."""
"""Update Home Assistant state, if needed."""
if partition is None or int(partition) == self._partition_number:
self.hass.async_add_job(self.async_update_ha_state())
@@ -39,7 +39,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the manual alarm platform."""
"""Set up the manual alarm platform."""
add_devices([ManualAlarm(
hass,
config[CONF_NAME],
@@ -52,7 +52,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class ManualAlarm(alarm.AlarmControlPanel):
"""
Represents an alarm status.
Representation of an alarm status.
When armed, will be pending for 'pending_time', after that armed.
When triggered, will be pending for 'trigger_time'. After that will be
@@ -62,7 +62,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
def __init__(self, hass, name, code, pending_time,
trigger_time, disarm_after_trigger):
"""Initalize the manual alarm panel."""
"""Init the manual alarm panel."""
self._state = STATE_ALARM_DISARMED
self._hass = hass
self._name = name
@@ -75,7 +75,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
@property
def should_poll(self):
"""No polling needed."""
"""Return the plling state."""
return False
@property
@@ -166,5 +166,5 @@ class ManualAlarm(alarm.AlarmControlPanel):
"""Validate given code."""
check = self._code is None or code == self._code
if not check:
_LOGGER.warning('Invalid code given for %s', state)
_LOGGER.warning("Invalid code given for %s", state)
return check
@@ -45,7 +45,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup the MQTT platform."""
"""Set up the MQTT Alarm Control Panel platform."""
async_add_devices([MqttAlarm(
config.get(CONF_NAME),
config.get(CONF_STATE_TOPIC),
@@ -62,7 +62,7 @@ class MqttAlarm(alarm.AlarmControlPanel):
def __init__(self, name, state_topic, command_topic, qos, payload_disarm,
payload_arm_home, payload_arm_away, code):
"""Initalize the MQTT alarm panel."""
"""Init the MQTT Alarm Control Panel."""
self._state = STATE_UNKNOWN
self._name = name
self._state_topic = state_topic
@@ -80,11 +80,11 @@ class MqttAlarm(alarm.AlarmControlPanel):
"""
@callback
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
"""Run when new MQTT message has been received."""
if payload not in (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED):
_LOGGER.warning('Received unexpected payload: %s', payload)
_LOGGER.warning("Received unexpected payload: %s", payload)
return
self._state = payload
self.hass.async_add_job(self.async_update_ha_state())
@@ -32,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup nx584 platform."""
"""Set up the nx584 platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
@@ -42,15 +42,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
try:
add_devices([NX584Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NX584: %s', str(ex))
_LOGGER.error("Unable to connect to NX584: %s", str(ex))
return False
class NX584Alarm(alarm.AlarmControlPanel):
"""Represents the NX584-based alarm panel."""
"""Representation of a NX584-based alarm panel."""
def __init__(self, hass, url, name):
"""Initalize the nx584 alarm panel."""
"""Init the nx584 alarm panel."""
from nx584 import client
self._hass = hass
self._name = name
@@ -69,7 +69,7 @@ class NX584Alarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""The characters if code is defined."""
"""Return che characters if code is defined."""
return '[0-9]{4}([0-9]{2})?'
@property
@@ -83,20 +83,19 @@ class NX584Alarm(alarm.AlarmControlPanel):
part = self._alarm.list_partitions()[0]
zones = self._alarm.list_zones()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to %(host)s: %(reason)s',
_LOGGER.error("Unable to connect to %(host)s: %(reason)s",
dict(host=self._url, reason=ex))
self._state = STATE_UNKNOWN
zones = []
except IndexError:
_LOGGER.error('nx584 reports no partitions')
_LOGGER.error("nx584 reports no partitions")
self._state = STATE_UNKNOWN
zones = []
bypassed = False
for zone in zones:
if zone['bypassed']:
_LOGGER.debug('Zone %(zone)s is bypassed, '
'assuming HOME',
_LOGGER.debug("Zone %(zone)s is bypassed, assuming HOME",
dict(zone=zone['number']))
bypassed = True
break
@@ -123,25 +123,25 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
if not self._validate_code(code, 'disarming'):
return
self.simplisafe.set_state('off')
_LOGGER.info('SimpliSafe alarm disarming')
_LOGGER.info("SimpliSafe alarm disarming")
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, 'arming home'):
return
self.simplisafe.set_state('home')
_LOGGER.info('SimpliSafe alarm arming home')
_LOGGER.info("SimpliSafe alarm arming home")
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, 'arming away'):
return
self.simplisafe.set_state('away')
_LOGGER.info('SimpliSafe alarm arming away')
_LOGGER.info("SimpliSafe alarm arming away")
def _validate_code(self, code, state):
"""Validate given code."""
check = self._code is None or code == self._code
if not check:
_LOGGER.warning('Wrong code entered for %s', state)
_LOGGER.warning("Wrong code entered for %s", state)
return check
@@ -1,15 +1,20 @@
"""Interfaces with TotalConnect alarm control panels."""
"""
Interfaces with TotalConnect alarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.totalconnect/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN,
CONF_NAME)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['total_connect_client==0.7']
@@ -25,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup a TotalConnect control panel."""
"""Set up a TotalConnect control panel."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
@@ -41,13 +46,13 @@ class TotalConnect(alarm.AlarmControlPanel):
"""Initialize the TotalConnect status."""
from total_connect_client import TotalConnectClient
_LOGGER.debug('Setting up TotalConnect...')
_LOGGER.debug("Setting up TotalConnect...")
self._name = name
self._username = username
self._password = password
self._state = STATE_UNKNOWN
self._client = TotalConnectClient.TotalConnectClient(username,
password)
self._client = TotalConnectClient.TotalConnectClient(
username, password)
@property
def name(self):
@@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Verisure platform."""
"""Set up the Verisure platform."""
alarms = []
if int(hub.config.get(CONF_ALARM, 1)):
hub.update_alarms()
@@ -29,10 +29,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureAlarm(alarm.AlarmControlPanel):
"""Represent a Verisure alarm status."""
"""Representation of a Verisure alarm status."""
def __init__(self, device_id):
"""Initalize the Verisure alarm panel."""
"""Initialize the Verisure alarm panel."""
self._id = device_id
self._state = STATE_UNKNOWN
self._digits = hub.config.get(CONF_CODE_DIGITS)
@@ -55,12 +55,12 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
"""The code format as regex."""
"""Return the code format as regex."""
return '^\\d{%s}$' % self._digits
@property
def changed_by(self):
"""Last change triggered by."""
"""Return the last change triggered by."""
return self._changed_by
def update(self):
@@ -75,24 +75,23 @@ class VerisureAlarm(alarm.AlarmControlPanel):
self._state = STATE_ALARM_ARMED_AWAY
elif hub.alarm_status[self._id].status != 'pending':
_LOGGER.error(
'Unknown alarm state %s',
hub.alarm_status[self._id].status)
"Unknown alarm state %s", hub.alarm_status[self._id].status)
self._changed_by = hub.alarm_status[self._id].name
def alarm_disarm(self, code=None):
"""Send disarm command."""
hub.my_pages.alarm.set(code, 'DISARMED')
_LOGGER.info('verisure alarm disarming')
_LOGGER.info("Verisure alarm disarming")
hub.my_pages.alarm.wait_while_pending()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
hub.my_pages.alarm.set(code, 'ARMED_HOME')
_LOGGER.info('verisure alarm arming home')
_LOGGER.info("Verisure alarm arming home")
hub.my_pages.alarm.wait_while_pending()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
hub.my_pages.alarm.set(code, 'ARMED_AWAY')
_LOGGER.info('verisure alarm arming away')
_LOGGER.info("Verisure alarm arming away")
hub.my_pages.alarm.wait_while_pending()
@@ -4,6 +4,7 @@ Interfaces with Wink Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.wink/
"""
import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
@@ -16,11 +17,12 @@ from homeassistant.components.wink import WinkDevice, DOMAIN
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['wink']
STATE_ALARM_PRIVACY = 'Private'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink platform."""
"""Set up the Wink platform."""
import pywink
for camera in pywink.get_cameras():
@@ -41,6 +43,11 @@ class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel):
"""Initialize the Wink alarm."""
super().__init__(wink, hass)
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['alarm_control_panel'].append(self)
@property
def state(self):
"""Return the state of the device."""
+20 -23
View File
@@ -8,11 +8,10 @@ import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.config_validation as cv
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
@@ -24,19 +23,16 @@ DOMAIN = 'alarmdecoder'
DATA_AD = 'alarmdecoder'
CONF_DEVICE = 'device'
CONF_DEVICE_TYPE = 'type'
CONF_DEVICE_HOST = 'host'
CONF_DEVICE_PORT = 'port'
CONF_DEVICE_PATH = 'path'
CONF_DEVICE_BAUD = 'baudrate'
CONF_ZONES = 'zones'
CONF_DEVICE_HOST = 'host'
CONF_DEVICE_PATH = 'path'
CONF_DEVICE_PORT = 'port'
CONF_DEVICE_TYPE = 'type'
CONF_PANEL_DISPLAY = 'panel_display'
CONF_ZONE_NAME = 'name'
CONF_ZONE_TYPE = 'type'
CONF_PANEL_DISPLAY = 'panel_display'
CONF_ZONES = 'zones'
DEFAULT_DEVICE_TYPE = 'socket'
DEFAULT_DEVICE_HOST = 'localhost'
@@ -87,7 +83,7 @@ CONFIG_SCHEMA = vol.Schema({
@asyncio.coroutine
def async_setup(hass, config):
"""Common setup for AlarmDecoder devices."""
"""Set up for the AlarmDecoder devices."""
from alarmdecoder import AlarmDecoder
from alarmdecoder.devices import (SocketDevice, SerialDevice, USBDevice)
@@ -106,28 +102,28 @@ def async_setup(hass, config):
sync_connect = asyncio.Future(loop=hass.loop)
def handle_open(device):
"""Callback for a successful connection."""
_LOGGER.info("Established a connection with the alarmdecoder.")
"""Handle the successful connection."""
_LOGGER.info("Established a connection with the alarmdecoder")
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
sync_connect.set_result(True)
@callback
def stop_alarmdecoder(event):
"""Callback to handle shutdown alarmdecoder."""
_LOGGER.debug("Shutting down alarmdecoder.")
"""Handle the shutdown of AlarmDecoder."""
_LOGGER.debug("Shutting down alarmdecoder")
controller.close()
@callback
def handle_message(sender, message):
"""Callback to handle message from alarmdecoder."""
"""Handle message from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_PANEL_MESSAGE, message)
def zone_fault_callback(sender, zone):
"""Callback to handle zone fault from alarmdecoder."""
"""Handle zone fault from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_FAULT, zone)
def zone_restore_callback(sender, zone):
"""Callback to handle zone restore from alarmdecoder."""
"""Handle zone restore from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_RESTORE, zone)
controller = False
@@ -157,15 +153,16 @@ def async_setup(hass, config):
if not result:
return False
hass.async_add_job(async_load_platform(hass, 'alarm_control_panel', DOMAIN,
conf, config))
hass.async_add_job(
async_load_platform(hass, 'alarm_control_panel', DOMAIN, conf,
config))
if zones:
hass.async_add_job(async_load_platform(
hass, 'binary_sensor', DOMAIN, {CONF_ZONES: zones}, config))
if display:
hass.async_add_job(async_load_platform(hass, 'sensor', DOMAIN,
conf, config))
hass.async_add_job(async_load_platform(
hass, 'sensor', DOMAIN, conf, config))
return True
+2 -2
View File
@@ -128,8 +128,8 @@ def async_setup(hass, config):
all_alerts[entity.entity_id] = entity
# Read descriptions
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
descriptions = descriptions.get(DOMAIN, {})
+1 -7
View File
@@ -17,7 +17,6 @@ from homeassistant.core import callback
from homeassistant.const import HTTP_BAD_REQUEST
from homeassistant.helpers import template, script, config_validation as cv
from homeassistant.components.http import HomeAssistantView
import homeassistant.util.dt as dt_util
_LOGGER = logging.getLogger(__name__)
@@ -36,7 +35,6 @@ CONF_TEXT = 'text'
CONF_FLASH_BRIEFINGS = 'flash_briefings'
CONF_UID = 'uid'
CONF_DATE = 'date'
CONF_TITLE = 'title'
CONF_AUDIO = 'audio'
CONF_TEXT = 'text'
@@ -88,7 +86,6 @@ CONFIG_SCHEMA = vol.Schema({
CONF_FLASH_BRIEFINGS: {
cv.string: vol.All(cv.ensure_list, [{
vol.Required(CONF_UID, default=str(uuid.uuid4())): cv.string,
vol.Optional(CONF_DATE, default=datetime.utcnow()): cv.string,
vol.Required(CONF_TITLE): cv.template,
vol.Optional(CONF_AUDIO): cv.template,
vol.Required(CONF_TEXT, default=""): cv.template,
@@ -331,10 +328,7 @@ class AlexaFlashBriefingView(HomeAssistantView):
else:
output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL)
if isinstance(item[CONF_DATE], str):
item[CONF_DATE] = dt_util.parse_datetime(item[CONF_DATE])
output[ATTR_UPDATE_DATE] = item[CONF_DATE].strftime(DATE_FORMAT)
output[ATTR_UPDATE_DATE] = datetime.now().strftime(DATE_FORMAT)
briefing.append(output)
+1 -1
View File
@@ -13,7 +13,7 @@ from homeassistant.const import (CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
REQUIREMENTS = ['apcaccess==0.0.4']
REQUIREMENTS = ['apcaccess==0.0.10']
_LOGGER = logging.getLogger(__name__)
+1 -1
View File
@@ -83,7 +83,7 @@ class APIEventStream(HomeAssistantView):
stop_obj = object()
to_write = asyncio.Queue(loop=hass.loop)
restrict = request.GET.get('restrict')
restrict = request.query.get('restrict')
if restrict:
restrict = restrict.split(',') + [EVENT_HOMEASSISTANT_STOP]
+60
View File
@@ -0,0 +1,60 @@
"""
This component provides basic support for Netgear Arlo IP cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/arlo/
"""
import logging
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
import homeassistant.loader as loader
from requests.exceptions import HTTPError, ConnectTimeout
REQUIREMENTS = ['pyarlo==0.0.4']
_LOGGER = logging.getLogger(__name__)
CONF_ATTRIBUTION = 'Data provided by arlo.netgear.com'
DOMAIN = 'arlo'
DEFAULT_BRAND = 'Netgear Arlo'
NOTIFICATION_ID = 'arlo_notification'
NOTIFICATION_TITLE = 'Arlo Camera Setup'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Set up an Arlo component."""
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
persistent_notification = loader.get_component('persistent_notification')
try:
from pyarlo import PyArlo
arlo = PyArlo(username, password, preload=False)
if not arlo.is_connected:
return False
hass.data['arlo'] = arlo
except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Netgar Arlo: %s", str(ex))
persistent_notification.create(
hass, 'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(ex),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
return True
+38 -16
View File
@@ -16,7 +16,7 @@ from homeassistant.core import CoreState
from homeassistant import config as conf_util
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START)
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
from homeassistant.components import logbook
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition
@@ -26,6 +26,7 @@ from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.loader import get_platform
from homeassistant.util.dt import utcnow
import homeassistant.helpers.config_validation as cv
from homeassistant.components.frontend import register_built_in_panel
DOMAIN = 'automation'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
@@ -81,6 +82,8 @@ _TRIGGER_SCHEMA = vol.All(
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.Schema({
# str on purpose
CONF_ID: str,
CONF_ALIAS: cv.string,
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_HIDE_ENTITY, default=DEFAULT_HIDE_ENTITY): cv.boolean,
@@ -139,19 +142,24 @@ def reload(hass):
hass.services.call(DOMAIN, SERVICE_RELOAD)
def async_reload(hass):
"""Reload the automation from config.
Returns a coroutine object.
"""
return hass.services.async_call(DOMAIN, SERVICE_RELOAD)
@asyncio.coroutine
def async_setup(hass, config):
"""Setup the automation."""
"""Set up the automation."""
component = EntityComponent(_LOGGER, DOMAIN, hass,
group_name=GROUP_NAME_ALL_AUTOMATIONS)
success = yield from _async_process_config(hass, config, component)
yield from _async_process_config(hass, config, component)
if not success:
return False
descriptions = yield from hass.loop.run_in_executor(
None, conf_util.load_yaml_config_file, os.path.join(
descriptions = yield from hass.async_add_job(
conf_util.load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml')
)
@@ -215,15 +223,20 @@ def async_setup(hass, config):
DOMAIN, service, turn_onoff_service_handler,
descriptions.get(service), schema=SERVICE_SCHEMA)
if 'frontend' in hass.config.components:
register_built_in_panel(hass, 'automation', 'Automations',
'mdi:playlist-play')
return True
class AutomationEntity(ToggleEntity):
"""Entity to show status of entity."""
def __init__(self, name, async_attach_triggers, cond_func, async_action,
hidden, initial_state):
def __init__(self, automation_id, name, async_attach_triggers, cond_func,
async_action, hidden, initial_state):
"""Initialize an automation entity."""
self._id = automation_id
self._name = name
self._async_attach_triggers = async_attach_triggers
self._async_detach_triggers = None
@@ -346,6 +359,16 @@ class AutomationEntity(ToggleEntity):
self.async_trigger)
yield from self.async_update_ha_state()
@property
def device_state_attributes(self):
"""Return automation attributes."""
if self._id is None:
return None
return {
CONF_ID: self._id
}
@asyncio.coroutine
def _async_process_config(hass, config, component):
@@ -359,6 +382,7 @@ def _async_process_config(hass, config, component):
conf = config[config_key]
for list_no, config_block in enumerate(conf):
automation_id = config_block.get(CONF_ID)
name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key,
list_no)
@@ -383,16 +407,14 @@ def _async_process_config(hass, config, component):
config_block.get(CONF_TRIGGER, []), name
)
entity = AutomationEntity(
name, async_attach_triggers, cond_func, action, hidden,
initial_state)
automation_id, name, async_attach_triggers, cond_func, action,
hidden, initial_state)
entities.append(entity)
if entities:
yield from component.async_add_entities(entities)
return len(entities) > 0
def _async_get_action(hass, config, name):
"""Return an action based on a configuration."""
@@ -400,7 +422,7 @@ def _async_get_action(hass, config, name):
@asyncio.coroutine
def action(entity_id, variables):
"""Action to be executed."""
"""Execute an action."""
_LOGGER.info('Executing %s', name)
logbook.async_log_entry(
hass, name, 'has been triggered', DOMAIN, entity_id)
@@ -430,7 +452,7 @@ def _async_process_if(hass, config, p_config):
@asyncio.coroutine
def _async_process_trigger(hass, config, trigger_configs, name, action):
"""Setup the triggers.
"""Set up the triggers.
This method is a coroutine.
"""
+4 -17
View File
@@ -9,12 +9,12 @@ import logging
import voluptuous as vol
from homeassistant.core import callback, CoreState
from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_START
from homeassistant.core import callback
from homeassistant.const import CONF_PLATFORM
from homeassistant.helpers import config_validation as cv
CONF_EVENT_TYPE = "event_type"
CONF_EVENT_DATA = "event_data"
CONF_EVENT_TYPE = 'event_type'
CONF_EVENT_DATA = 'event_data'
_LOGGER = logging.getLogger(__name__)
@@ -31,19 +31,6 @@ def async_trigger(hass, config, action):
event_type = config.get(CONF_EVENT_TYPE)
event_data = config.get(CONF_EVENT_DATA)
if (event_type == EVENT_HOMEASSISTANT_START and
hass.state == CoreState.starting):
_LOGGER.warning('Deprecation: Automations should not listen to event '
"'homeassistant_start'. Use platform 'homeassistant' "
'instead. Feature will be removed in 0.45')
hass.async_run_job(action, {
'trigger': {
'platform': 'event',
'event': None,
},
})
return lambda: None
@callback
def handle_event(event):
"""Listen for events and calls the action when data matches."""
@@ -31,7 +31,7 @@ def async_trigger(hass, config, action):
if event == EVENT_SHUTDOWN:
@callback
def hass_shutdown(event):
"""Called when Home Assistant is shutting down."""
"""Execute when Home Assistant is shutting down."""
hass.async_run_job(action, {
'trigger': {
'platform': 'homeassistant',
+13 -6
View File
@@ -12,13 +12,14 @@ import homeassistant.util.dt as dt_util
from homeassistant.const import MATCH_ALL, CONF_PLATFORM
from homeassistant.helpers.event import (
async_track_state_change, async_track_point_in_utc_time)
from homeassistant.helpers.deprecation import get_deprecated
import homeassistant.helpers.config_validation as cv
CONF_ENTITY_ID = "entity_id"
CONF_FROM = "from"
CONF_TO = "to"
CONF_STATE = "state"
CONF_FOR = "for"
CONF_ENTITY_ID = 'entity_id'
CONF_FROM = 'from'
CONF_TO = 'to'
CONF_STATE = 'state'
CONF_FOR = 'for'
TRIGGER_SCHEMA = vol.All(
vol.Schema({
@@ -40,10 +41,11 @@ def async_trigger(hass, config, action):
"""Listen for state changes based on configuration."""
entity_id = config.get(CONF_ENTITY_ID)
from_state = config.get(CONF_FROM, MATCH_ALL)
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
to_state = get_deprecated(config, CONF_TO, CONF_STATE, MATCH_ALL)
time_delta = config.get(CONF_FOR)
async_remove_state_for_cancel = None
async_remove_state_for_listener = None
match_all = (from_state == MATCH_ALL and to_state == MATCH_ALL)
@callback
def clear_listener():
@@ -75,6 +77,11 @@ def async_trigger(hass, config, action):
}
})
# Ignore changes to state attributes if from/to is in use
if (not match_all and from_s is not None and to_s is not None and
from_s.last_changed == to_s.last_changed):
return
if time_delta is None:
call_action()
return
@@ -16,8 +16,6 @@ from homeassistant.const import (
from homeassistant.helpers.event import async_track_sunrise, async_track_sunset
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['sun']
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.Schema({
+14 -8
View File
@@ -10,32 +10,38 @@ import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_AFTER, CONF_PLATFORM
from homeassistant.const import CONF_AT, CONF_PLATFORM, CONF_AFTER
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_change
CONF_HOURS = "hours"
CONF_MINUTES = "minutes"
CONF_SECONDS = "seconds"
CONF_HOURS = 'hours'
CONF_MINUTES = 'minutes'
CONF_SECONDS = 'seconds'
_LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.All(vol.Schema({
vol.Required(CONF_PLATFORM): 'time',
CONF_AT: cv.time,
CONF_AFTER: cv.time,
CONF_HOURS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_MINUTES: vol.Any(vol.Coerce(int), vol.Coerce(str)),
CONF_SECONDS: vol.Any(vol.Coerce(int), vol.Coerce(str)),
}), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES,
CONF_SECONDS, CONF_AFTER))
CONF_SECONDS, CONF_AT, CONF_AFTER))
@asyncio.coroutine
def async_trigger(hass, config, action):
"""Listen for state changes based on configuration."""
if CONF_AFTER in config:
after = config.get(CONF_AFTER)
hours, minutes, seconds = after.hour, after.minute, after.second
if CONF_AT in config:
at_time = config.get(CONF_AT)
hours, minutes, seconds = at_time.hour, at_time.minute, at_time.second
elif CONF_AFTER in config:
_LOGGER.warning("'after' is deprecated for the time trigger. Please "
"rename 'after' to 'at' in your configuration file.")
at_time = config.get(CONF_AFTER)
hours, minutes, seconds = at_time.hour, at_time.minute, at_time.second
else:
hours = config.get(CONF_HOURS)
minutes = config.get(CONF_MINUTES)
+2 -2
View File
@@ -14,8 +14,8 @@ from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers import (
condition, config_validation as cv, location)
EVENT_ENTER = "enter"
EVENT_LEAVE = "leave"
EVENT_ENTER = 'enter'
EVENT_LEAVE = 'leave'
DEFAULT_EVENT = EVENT_ENTER
TRIGGER_SCHEMA = vol.Schema({
+314
View File
@@ -0,0 +1,314 @@
"""
Support for Axis devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/axis/
"""
import json
import logging
import os
import voluptuous as vol
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_HOST, CONF_INCLUDE, CONF_NAME,
CONF_PASSWORD, CONF_TRIGGER_TIME,
CONF_USERNAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.components.discovery import SERVICE_AXIS
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
REQUIREMENTS = ['axis==7']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'axis'
CONFIG_FILE = 'axis.conf'
AXIS_DEVICES = {}
EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound',
'daynight', 'tampering', 'input']
PLATFORMS = ['camera']
AXIS_INCLUDE = EVENT_TYPES + PLATFORMS
AXIS_DEFAULT_HOST = '192.168.0.90'
AXIS_DEFAULT_USERNAME = 'root'
AXIS_DEFAULT_PASSWORD = 'pass'
DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_INCLUDE):
vol.All(cv.ensure_list, [vol.In(AXIS_INCLUDE)]),
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_HOST, default=AXIS_DEFAULT_HOST): cv.string,
vol.Optional(CONF_USERNAME, default=AXIS_DEFAULT_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD, default=AXIS_DEFAULT_PASSWORD): cv.string,
vol.Optional(CONF_TRIGGER_TIME, default=0): cv.positive_int,
vol.Optional(ATTR_LOCATION, default=''): cv.string,
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: DEVICE_SCHEMA,
}),
}, extra=vol.ALLOW_EXTRA)
def request_configuration(hass, name, host, serialnumber):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
def configuration_callback(callback_data):
"""Called when config is submitted."""
if CONF_INCLUDE not in callback_data:
configurator.notify_errors(request_id,
"Functionality mandatory.")
return False
callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split()
callback_data[CONF_HOST] = host
if CONF_NAME not in callback_data:
callback_data[CONF_NAME] = name
try:
config = DEVICE_SCHEMA(callback_data)
except vol.Invalid:
configurator.notify_errors(request_id,
"Bad input, please check spelling.")
return False
if setup_device(hass, config):
config_file = _read_config(hass)
config_file[serialnumber] = dict(config)
del config_file[serialnumber]['hass']
_write_config(hass, config_file)
configurator.request_done(request_id)
else:
configurator.notify_errors(request_id,
"Failed to register, please try again.")
return False
title = '{} ({})'.format(name, host)
request_id = configurator.request_config(
hass, title, configuration_callback,
description='Functionality: ' + str(AXIS_INCLUDE),
entity_picture="/static/images/logo_axis.png",
link_name='Axis platform documentation',
link_url='https://home-assistant.io/components/axis/',
submit_caption="Confirm",
fields=[
{'id': CONF_NAME,
'name': "Device name",
'type': 'text'},
{'id': CONF_USERNAME,
'name': "User name",
'type': 'text'},
{'id': CONF_PASSWORD,
'name': 'Password',
'type': 'password'},
{'id': CONF_INCLUDE,
'name': "Device functionality (space separated list)",
'type': 'text'},
{'id': ATTR_LOCATION,
'name': "Physical location of device (optional)",
'type': 'text'},
{'id': CONF_TRIGGER_TIME,
'name': "Sensor update interval (optional)",
'type': 'number'},
]
)
def setup(hass, base_config):
"""Common setup for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
"""Stop the metadatastream on shutdown."""
for serialnumber, device in AXIS_DEVICES.items():
_LOGGER.info("Stopping metadatastream for %s.", serialnumber)
device.stop_metadatastream()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)
def axis_device_discovered(service, discovery_info):
"""Called when axis devices has been found."""
host = discovery_info['host']
name = discovery_info['hostname']
serialnumber = discovery_info['properties']['macaddress']
if serialnumber not in AXIS_DEVICES:
config_file = _read_config(hass)
if serialnumber in config_file:
try:
config = DEVICE_SCHEMA(config_file[serialnumber])
except vol.Invalid as err:
_LOGGER.error("Bad data from %s. %s", CONFIG_FILE, err)
return False
if not setup_device(hass, config):
_LOGGER.error("Couldn\'t set up %s", config['name'])
else:
request_configuration(hass, name, host, serialnumber)
discovery.listen(hass, SERVICE_AXIS, axis_device_discovered)
if DOMAIN in base_config:
for device in base_config[DOMAIN]:
config = base_config[DOMAIN][device]
if CONF_NAME not in config:
config[CONF_NAME] = device
if not setup_device(hass, config):
_LOGGER.error("Couldn\'t set up %s", config['name'])
return True
def setup_device(hass, config):
"""Set up device."""
from axis import AxisDevice
config['hass'] = hass
device = AxisDevice(config) # Initialize device
enable_metadatastream = False
if device.serial_number is None:
# If there is no serial number a connection could not be made
_LOGGER.error("Couldn\'t connect to %s", config[CONF_HOST])
return False
for component in config[CONF_INCLUDE]:
if component in EVENT_TYPES:
# Sensors are created by device calling event_initialized
# when receiving initialize messages on metadatastream
device.add_event_topic(convert(component, 'type', 'subscribe'))
if not enable_metadatastream:
enable_metadatastream = True
else:
discovery.load_platform(hass, component, DOMAIN, config)
if enable_metadatastream:
device.initialize_new_event = event_initialized
device.initiate_metadatastream()
AXIS_DEVICES[device.serial_number] = device
return True
def _read_config(hass):
"""Read Axis config."""
path = hass.config.path(CONFIG_FILE)
if not os.path.isfile(path):
return {}
with open(path) as f_handle:
# Guard against empty file
return json.loads(f_handle.read() or '{}')
def _write_config(hass, config):
"""Write Axis config."""
data = json.dumps(config)
with open(hass.config.path(CONFIG_FILE), 'w', encoding='utf-8') as outfile:
outfile.write(data)
def event_initialized(event):
"""Register event initialized on metadatastream here."""
hass = event.device_config('hass')
discovery.load_platform(hass,
convert(event.topic, 'topic', 'platform'),
DOMAIN, {'axis_event': event})
class AxisDeviceEvent(Entity):
"""Representation of a Axis device event."""
def __init__(self, axis_event):
"""Initialize the event."""
self.axis_event = axis_event
self._event_class = convert(self.axis_event.topic, 'topic', 'class')
self._name = '{}_{}_{}'.format(self.axis_event.device_name,
convert(self.axis_event.topic,
'topic', 'type'),
self.axis_event.id)
self.axis_event.callback = self._update_callback
def _update_callback(self):
"""Update the sensor's state, if needed."""
self.update()
self.schedule_update_ha_state()
@property
def name(self):
"""Return the name of the event."""
return self._name
@property
def device_class(self):
"""Return the class of the event."""
return self._event_class
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_state_attributes(self):
"""Return the state attributes of the event."""
attr = {}
tripped = self.axis_event.is_tripped
attr[ATTR_TRIPPED] = 'True' if tripped else 'False'
location = self.axis_event.device_config(ATTR_LOCATION)
if location:
attr[ATTR_LOCATION] = location
return attr
def convert(item, from_key, to_key):
"""Translate between Axis and HASS syntax."""
for entry in REMAP:
if entry[from_key] == item:
return entry[to_key]
REMAP = [{'type': 'motion',
'class': 'motion',
'topic': 'tns1:VideoAnalytics/tnsaxis:MotionDetection',
'subscribe': 'onvif:VideoAnalytics/axis:MotionDetection',
'platform': 'binary_sensor'},
{'type': 'vmd3',
'class': 'motion',
'topic': 'tns1:RuleEngine/tnsaxis:VMD3/vmd3_video_1',
'subscribe': 'onvif:RuleEngine/axis:VMD3/vmd3_video_1',
'platform': 'binary_sensor'},
{'type': 'pir',
'class': 'motion',
'topic': 'tns1:Device/tnsaxis:Sensor/PIR',
'subscribe': 'onvif:Device/axis:Sensor/axis:PIR',
'platform': 'binary_sensor'},
{'type': 'sound',
'class': 'sound',
'topic': 'tns1:AudioSource/tnsaxis:TriggerLevel',
'subscribe': 'onvif:AudioSource/axis:TriggerLevel',
'platform': 'binary_sensor'},
{'type': 'daynight',
'class': 'light',
'topic': 'tns1:VideoSource/tnsaxis:DayNightVision',
'subscribe': 'onvif:VideoSource/axis:DayNightVision',
'platform': 'binary_sensor'},
{'type': 'tampering',
'class': 'safety',
'topic': 'tns1:VideoSource/tnsaxis:Tampering',
'subscribe': 'onvif:VideoSource/axis:Tampering',
'platform': 'binary_sensor'},
{'type': 'input',
'class': 'input',
'topic': 'tns1:Device/tnsaxis:IO/Port',
'subscribe': 'onvif:Device/axis:IO/Port',
'platform': 'sensor'}, ]
+2 -2
View File
@@ -37,14 +37,14 @@ def setup(hass, config):
# noqa: F821
def setup_output(pin):
"""Setup a GPIO as output."""
"""Set up a GPIO as output."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup(pin, GPIO.OUT)
def setup_input(pin, pull_mode):
"""Setup a GPIO as input."""
"""Set up a GPIO as input."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup(pin, GPIO.IN, # noqa: F821
@@ -57,7 +57,7 @@ class BinarySensorDevice(Entity):
@property
def is_on(self):
"""Return True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return None
@property
@@ -11,7 +11,6 @@ from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import (STATE_ON, STATE_OFF, STATE_OPEN, STATE_CLOSED)
from homeassistant.components.alarmdecoder import (ZONE_SCHEMA,
CONF_ZONES,
CONF_ZONE_NAME,
@@ -27,7 +26,7 @@ _LOGGER = logging.getLogger(__name__)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup AlarmDecoder binary sensor devices."""
"""Set up the AlarmDecoder binary sensor devices."""
configured_zones = discovery_info[CONF_ZONES]
devices = []
@@ -36,10 +35,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
device_config_data = ZONE_SCHEMA(configured_zones[zone_num])
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
device = AlarmDecoderBinarySensor(hass,
zone_num,
zone_name,
zone_type)
device = AlarmDecoderBinarySensor(
hass, zone_num, zone_name, zone_type)
devices.append(device)
async_add_devices(devices)
@@ -58,7 +55,7 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
self._name = zone_name
self._type = zone_type
_LOGGER.debug('AlarmDecoderBinarySensor: Setup up zone: ' + zone_name)
_LOGGER.debug("Setup up zone: %s", self._name)
@asyncio.coroutine
def async_added_to_hass(self):
@@ -69,14 +66,6 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_RESTORE, self._restore_callback)
@property
def state(self):
"""Return the state of the binary sensor."""
if self._type == 'opening':
return STATE_OPEN if self.is_on else STATE_CLOSED
return STATE_ON if self.is_on else STATE_OFF
@property
def name(self):
"""Return the name of the entity."""
@@ -15,7 +15,7 @@ DEPENDENCIES = ['android_ip_webcam']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup IP Webcam binary sensors."""
"""Set up the IP Webcam binary sensors."""
if discovery_info is None:
return
@@ -28,7 +28,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class IPWebcamBinarySensor(AndroidIPCamEntity, BinarySensorDevice):
"""Represents an IP Webcam binary sensor."""
"""Representation of an IP Webcam binary sensor."""
def __init__(self, name, host, ipcam, sensor):
"""Initialize the binary sensor."""
@@ -47,7 +47,7 @@ class IPWebcamBinarySensor(AndroidIPCamEntity, BinarySensorDevice):
@property
def is_on(self):
"""True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return self._state
@asyncio.coroutine
@@ -21,7 +21,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Setup an Online Status binary sensor."""
"""Set up an Online Status binary sensor."""
add_entities((OnlineStatus(config, apcupsd.DATA),))
@@ -0,0 +1,68 @@
"""
Support for Axis binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.axis/
"""
import logging
from datetime import timedelta
from homeassistant.components.binary_sensor import (BinarySensorDevice)
from homeassistant.components.axis import (AxisDeviceEvent)
from homeassistant.const import (CONF_TRIGGER_TIME)
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util.dt import utcnow
DEPENDENCIES = ['axis']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis device event."""
add_devices([AxisBinarySensor(discovery_info['axis_event'], hass)], True)
class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice):
"""Representation of a binary Axis event."""
def __init__(self, axis_event, hass):
"""Initialize the binary sensor."""
self.hass = hass
self._state = False
self._delay = axis_event.device_config(CONF_TRIGGER_TIME)
self._timer = None
AxisDeviceEvent.__init__(self, axis_event)
@property
def is_on(self):
"""Return true if event is active."""
return self._state
def update(self):
"""Get the latest data and update the state."""
self._state = self.axis_event.is_tripped
def _update_callback(self):
"""Update the sensor's state, if needed."""
self.update()
if self._timer is not None:
self._timer()
self._timer = None
if self._delay > 0 and not self.is_on:
# Set timer to wait until updating the state
def _delay_update(now):
"""Timer callback for sensor update."""
_LOGGER.debug("%s Called delayed (%s sec) update.",
self._name, self._delay)
self.schedule_update_ha_state()
self._timer = None
self._timer = track_point_in_utc_time(
self.hass, _delay_update,
utcnow() + timedelta(seconds=self._delay))
else:
self.schedule_update_ha_state()
@@ -42,7 +42,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Beaglebone Black GPIO devices."""
"""Set up the Beaglebone Black GPIO devices."""
pins = config.get(CONF_PINS)
binary_sensors = []
@@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class BBBGPIOBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that uses Beaglebone Black GPIO."""
"""Representation of a binary sensor that uses Beaglebone Black GPIO."""
def __init__(self, pin, params):
"""Initialize the Beaglebone Black binary sensor."""
@@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class BlinkCameraMotionSensor(BinarySensorDevice):
"""A representation of a Blink binary sensor."""
"""Representation of a Blink binary sensor."""
def __init__(self, name, data):
"""Initialize the sensor."""
@@ -18,7 +18,6 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['bloomsky']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
'Rain': 'moisture',
'Night': None,
@@ -31,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the available BloomSky weather binary sensors."""
"""Set up the available BloomSky weather binary sensors."""
bloomsky = get_component('bloomsky')
# Default needed in case of discovery
sensors = config.get(CONF_MONITORED_CONDITIONS, SENSOR_TYPES)
@@ -42,7 +41,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class BloomSkySensor(BinarySensorDevice):
"""Represent a single binary sensor in a BloomSky device."""
"""Representation of a single binary sensor in a BloomSky device."""
def __init__(self, bs, device, sensor_name):
"""Initialize a BloomSky binary sensor."""
@@ -55,7 +54,7 @@ class BloomSkySensor(BinarySensorDevice):
@property
def name(self):
"""The name of the BloomSky device and this sensor."""
"""Return the name of the BloomSky device and this sensor."""
return self._name
@property
@@ -39,7 +39,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Command line Binary Sensor."""
"""Set up the Command line Binary Sensor."""
name = config.get(CONF_NAME)
command = config.get(CONF_COMMAND)
payload_off = config.get(CONF_PAYLOAD_OFF)
@@ -56,7 +56,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class CommandBinarySensor(BinarySensorDevice):
"""Represent a command line binary sensor."""
"""Representation of a command line binary sensor."""
def __init__(self, hass, data, name, device_class, payload_on,
payload_off, value_template):
@@ -67,8 +67,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if zone['number'] not in exclude:
sensors.append(
Concord232ZoneSensor(
hass, client, zone, zone_types.get(zone['number'],
get_opening_type(zone)))
hass, client, zone, zone_types.get(
zone['number'], get_opening_type(zone))
)
)
add_devices(sensors)
@@ -77,7 +78,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
def get_opening_type(zone):
"""Helper function to try to guess sensor type from name."""
"""Return the result of the type guessing from name."""
if 'MOTION' in zone['name']:
return 'motion'
if 'KEY' in zone['name']:
@@ -123,7 +124,7 @@ class Concord232ZoneSensor(BinarySensorDevice):
return bool(self._zone['state'] == 'Normal')
def update(self):
""""Get updated stats from API."""
"""Get updated stats from API."""
last_update = datetime.datetime.now() - self._client.last_zone_update
_LOGGER.debug("Zone: %s ", self._zone)
if last_update > datetime.timedelta(seconds=1):
@@ -8,7 +8,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo binary sensor platform."""
"""Set up the Demo binary sensor platform."""
add_devices([
DemoBinarySensor('Basement Floor Wet', False, 'moisture'),
DemoBinarySensor('Movement Backyard', True, 'motion'),
@@ -16,7 +16,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class DemoBinarySensor(BinarySensorDevice):
"""A Demo binary sensor."""
"""representation of a Demo binary sensor."""
def __init__(self, name, state, device_class):
"""Initialize the demo sensor."""
@@ -13,7 +13,7 @@ ECOBEE_CONFIG_FILE = 'ecobee.conf'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Ecobee sensors."""
"""Set up the Ecobee sensors."""
if discovery_info is None:
return
data = ecobee.NETWORK
@@ -0,0 +1,69 @@
"""
Support for Eight Sleep binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.eight_sleep/
"""
import logging
import asyncio
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.eight_sleep import (
DATA_EIGHT, EightSleepHeatEntity, CONF_BINARY_SENSORS, NAME_MAP)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['eight_sleep']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the eight sleep binary sensor."""
if discovery_info is None:
return
name = 'Eight'
sensors = discovery_info[CONF_BINARY_SENSORS]
eight = hass.data[DATA_EIGHT]
all_sensors = []
for sensor in sensors:
all_sensors.append(EightHeatSensor(name, eight, sensor))
async_add_devices(all_sensors, True)
class EightHeatSensor(EightSleepHeatEntity, BinarySensorDevice):
"""Representation of a Eight Sleep heat-based sensor."""
def __init__(self, name, eight, sensor):
"""Initialize the sensor."""
super().__init__(eight)
self._sensor = sensor
self._mapped_name = NAME_MAP.get(self._sensor, self._sensor)
self._name = '{} {}'.format(name, self._mapped_name)
self._state = None
self._side = self._sensor.split('_')[0]
self._userid = self._eight.fetch_userid(self._side)
self._usrobj = self._eight.users[self._userid]
_LOGGER.debug("Presence Sensor: %s, Side: %s, User: %s",
self._sensor, self._side, self._userid)
@property
def name(self):
"""Return the name of the sensor, if any."""
return self._name
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._state
@asyncio.coroutine
def async_update(self):
"""Retrieve latest state."""
self._state = self._usrobj.bed_presence
@@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Binary Sensor platform fo EnOcean."""
"""Set up the Binary Sensor platform for EnOcean."""
dev_id = config.get(CONF_ID)
devname = config.get(CONF_NAME)
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
@@ -44,7 +44,7 @@ class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
def __init__(self, dev_id, devname, device_class):
"""Initialize the EnOcean binary sensor."""
enocean.EnOceanDevice.__init__(self)
self.stype = "listener"
self.stype = 'listener'
self.dev_id = dev_id
self.which = -1
self.onoff = -1
@@ -53,7 +53,7 @@ class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
@property
def name(self):
"""The default name for the binary sensor."""
"""Return the default name for the binary sensor."""
return self.devname
@property
@@ -80,7 +80,7 @@ class EnOceanBinarySensor(enocean.EnOceanDevice, BinarySensorDevice):
elif value2 == 0x10:
self.which = 1
self.onoff = 1
self.hass.bus.fire('button_pressed', {"id": self.dev_id,
self.hass.bus.fire('button_pressed', {'id': self.dev_id,
'pushed': value,
'which': self.which,
'onoff': self.onoff})
@@ -15,13 +15,14 @@ from homeassistant.components.envisalink import (
SIGNAL_ZONE_UPDATE)
from homeassistant.const import ATTR_LAST_TRIP_TIME
DEPENDENCIES = ['envisalink']
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['envisalink']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup Envisalink binary sensor devices."""
"""Set up the Envisalink binary sensor devices."""
configured_zones = discovery_info['zones']
devices = []
@@ -48,23 +48,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Create the binary sensor."""
"""Set up the FFmpeg binary moition sensor."""
manager = hass.data[DATA_FFMPEG]
# check source
if not manager.async_run_test(config.get(CONF_INPUT)):
return
# generate sensor object
entity = FFmpegMotion(hass, manager, config)
async_add_devices([entity])
class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
"""A binary sensor which use ffmpeg for noise detection."""
"""A binary sensor which use FFmpeg for noise detection."""
def __init__(self, config):
"""Constructor for binary sensor noise detection."""
"""Init for the binary sensor noise detection."""
super().__init__(config.get(CONF_INITIAL_STATE))
self._state = False
@@ -79,7 +77,7 @@ class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
@property
def is_on(self):
"""True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return self._state
@property
@@ -89,10 +87,10 @@ class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
class FFmpegMotion(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
"""A binary sensor which use FFmpeg for noise detection."""
def __init__(self, hass, manager, config):
"""Initialize ffmpeg motion binary sensor."""
"""Initialize FFmpeg motion binary sensor."""
from haffmpeg import SensorMotion
super().__init__(config)
@@ -125,4 +123,4 @@ class FFmpegMotion(FFmpegBinarySensor):
@property
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return "motion"
return 'motion'
@@ -45,23 +45,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Create the binary sensor."""
"""Set up the FFmpeg noise binary sensor."""
manager = hass.data[DATA_FFMPEG]
# check source
if not manager.async_run_test(config.get(CONF_INPUT)):
return
# generate sensor object
entity = FFmpegNoise(hass, manager, config)
async_add_devices([entity])
class FFmpegNoise(FFmpegBinarySensor):
"""A binary sensor which use ffmpeg for noise detection."""
"""A binary sensor which use FFmpeg for noise detection."""
def __init__(self, hass, manager, config):
"""Initialize ffmpeg noise binary sensor."""
"""Initialize FFmpeg noise binary sensor."""
from haffmpeg import SensorNoise
super().__init__(config)
@@ -77,14 +75,12 @@ class FFmpegNoise(FFmpegBinarySensor):
if entity_ids is not None and self.entity_id not in entity_ids:
return
# init config
self.ffmpeg.set_options(
time_duration=self._config.get(CONF_DURATION),
time_reset=self._config.get(CONF_RESET),
peak=self._config.get(CONF_PEAK),
)
# run
yield from self.ffmpeg.open_sensor(
input_source=self._config.get(CONF_INPUT),
output_dest=self._config.get(CONF_OUTPUT),
@@ -94,4 +90,4 @@ class FFmpegNoise(FFmpegBinarySensor):
@property
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return "sound"
return 'sound'
@@ -110,7 +110,7 @@ def start_scanning(config, add_entities, client):
def setup_button(hass, config, add_entities, client, address):
"""Setup single button device."""
"""Set up a single button device."""
timeout = config.get(CONF_TIMEOUT)
ignored_click_types = config.get(CONF_IGNORED_CLICK_TYPES)
button = FlicButton(hass, client, address, timeout, ignored_click_types)
@@ -67,7 +67,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Setup Hikvision binary sensor devices."""
"""Set up the Hikvision binary sensor devices."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
@@ -77,16 +77,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
customize = config.get(CONF_CUSTOMIZE)
if config.get(CONF_SSL):
protocol = "https"
protocol = 'https'
else:
protocol = "http"
protocol = 'http'
url = '{}://{}'.format(protocol, host)
data = HikvisionData(hass, url, port, name, username, password)
if data.sensors is None:
_LOGGER.error('Hikvision event stream has no data, unable to setup.')
_LOGGER.error("Hikvision event stream has no data, unable to setup")
return False
entities = []
@@ -104,7 +104,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
ignore = custom.get(CONF_IGNORED)
delay = custom.get(CONF_DELAY)
_LOGGER.debug('Entity: %s - %s, Options - Ignore: %s, Delay: %s',
_LOGGER.debug("Entity: %s - %s, Options - Ignore: %s, Delay: %s",
data.name, sensor_name, ignore, delay)
if not ignore:
entities.append(HikvisionBinarySensor(
@@ -126,8 +126,8 @@ class HikvisionData(object):
self._password = password
# Establish camera
self.camdata = HikCamera(self._url, self._port,
self._username, self._password)
self.camdata = HikCamera(
self._url, self._port, self._username, self._password)
if self._name is None:
self._name = self.camdata.get_name
@@ -251,7 +251,7 @@ class HikvisionBinarySensor(BinarySensorDevice):
# Set timer to wait until updating the state
def _delay_update(now):
"""Timer callback for sensor update."""
_LOGGER.debug('%s Called delayed (%ssec) update.',
_LOGGER.debug("%s Called delayed (%ssec) update",
self._name, self._delay)
self.schedule_update_ha_state()
self._timer = None
@@ -14,22 +14,22 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['homematic']
SENSOR_TYPES_CLASS = {
"Remote": None,
"ShutterContact": "opening",
"MaxShutterContact": "opening",
"IPShutterContact": "opening",
"Smoke": "smoke",
"SmokeV2": "smoke",
"Motion": "motion",
"MotionV2": "motion",
"RemoteMotion": None,
"WeatherSensor": None,
"TiltSensor": None,
'Remote': None,
'ShutterContact': 'opening',
'MaxShutterContact': 'opening',
'IPShutterContact': 'opening',
'Smoke': 'smoke',
'SmokeV2': 'smoke',
'Motion': 'motion',
'MotionV2': 'motion',
'RemoteMotion': None,
'WeatherSensor': None,
'TiltSensor': None,
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Homematic binary sensor platform."""
"""Set up the Homematic binary sensor platform."""
if discovery_info is None:
return
@@ -56,8 +56,8 @@ class HMBinarySensor(HMDevice, BinarySensorDevice):
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
# If state is MOTION (RemoteMotion works only)
if self._state == "MOTION":
return "motion"
if self._state == 'MOTION':
return 'motion'
return SENSOR_TYPES_CLASS.get(self._hmdevice.__class__.__name__, None)
def _init_data_struct(self):
@@ -67,7 +67,7 @@ class InsteonPLMBinarySensorDevice(BinarySensorDevice):
def is_on(self):
"""Return the boolean response if the node is on."""
sensorstate = self._plm.get_device_attr(self._address, 'sensorstate')
_LOGGER.info('sensor state for %s is %s', self._address, sensorstate)
_LOGGER.info("Sensor state for %s is %s", self._address, sensorstate)
return bool(sensorstate)
@property
@@ -83,5 +83,5 @@ class InsteonPLMBinarySensorDevice(BinarySensorDevice):
@callback
def async_binarysensor_update(self, message):
"""Receive notification from transport that new data exists."""
_LOGGER.info('Received update calback from PLM for %s', self._address)
_LOGGER.info("Received update calback from PLM for %s", self._address)
self._hass.async_add_job(self.async_update_ha_state())
@@ -12,7 +12,6 @@ import homeassistant.components.isy994 as isy
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__)
VALUE_TO_STATE = {
@@ -27,9 +26,9 @@ STATES = [STATE_OFF, STATE_ON, 'true', 'false']
# pylint: disable=unused-argument
def setup_platform(hass, config: ConfigType,
add_devices: Callable[[list], None], discovery_info=None):
"""Setup the ISY994 binary sensor platform."""
"""Set up the ISY994 binary sensor platform."""
if isy.ISY is None or not isy.ISY.connected:
_LOGGER.error('A connection has not been made to the ISY controller.')
_LOGGER.error("A connection has not been made to the ISY controller")
return False
devices = []
@@ -11,7 +11,7 @@ DEPENDENCIES = ['knx']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the KNX binary sensor platform."""
"""Set up the KNX binary sensor platform."""
add_devices([KNXSwitch(hass, KNXConfig(config))])
@@ -4,7 +4,6 @@ Support for MAX! Window Shutter via MAX! Cube.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/maxcube/
"""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
@@ -15,27 +14,24 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Iterate through all MAX! Devices and add window shutters to HASS."""
"""Iterate through all MAX! Devices and add window shutters."""
cube = hass.data[MAXCUBE_HANDLE].cube
# List of devices
devices = []
for device in cube.devices:
# Create device name by concatenating room name + device name
name = "%s %s" % (cube.room_by_id(device.room_id).name, device.name)
name = "{} {}".format(
cube.room_by_id(device.room_id).name, device.name)
# Only add Window Shutters
if cube.is_windowshutter(device):
# add device to HASS
devices.append(MaxCubeShutter(hass, name, device.rf_address))
if len(devices) > 0:
if devices:
add_devices(devices)
class MaxCubeShutter(BinarySensorDevice):
"""MAX! Cube BinarySensor device."""
"""Representation of a MAX! Cube Binary Sensor device."""
def __init__(self, hass, name, rf_address):
"""Initialize MAX! Cube BinarySensorDevice."""
@@ -47,7 +43,7 @@ class MaxCubeShutter(BinarySensorDevice):
@property
def should_poll(self):
"""Polling is required."""
"""Return the polling state."""
return True
@property
@@ -68,9 +64,5 @@ class MaxCubeShutter(BinarySensorDevice):
def update(self):
"""Get latest data from MAX! Cube."""
self._cubehandle.update()
# Get the device we want to update
device = self._cubehandle.cube.device_by_rf(self._rf_address)
# Update our internal state
self._state = device.is_open
@@ -16,9 +16,9 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['modbus']
CONF_COIL = "coil"
CONF_COILS = "coils"
CONF_SLAVE = "slave"
CONF_COIL = 'coil'
CONF_COILS = 'coils'
CONF_SLAVE = 'slave'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COILS): [{
@@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Modbus binary sensors."""
"""Set up the Modbus binary sensors."""
sensors = []
for coil in config.get(CONF_COILS):
sensors.append(ModbusCoilSensor(
@@ -79,7 +79,7 @@ class MqttBinarySensor(BinarySensorDevice):
"""
@callback
def message_received(topic, payload, qos):
"""A new MQTT message has been received."""
"""Handle a new received MQTT message."""
if self._template is not None:
payload = self._template.async_render_with_possible_json_value(
payload)
@@ -95,7 +95,7 @@ class MqttBinarySensor(BinarySensorDevice):
@property
def should_poll(self):
"""No polling needed."""
"""Return the polling state."""
return False
@property
@@ -16,7 +16,7 @@ DEPENDENCIES = []
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for sensors."""
"""Set up the MySensors platform for sensors."""
# Only act if loaded via mysensors by discovery event.
# Otherwise gateway is not setup.
if discovery_info is None:
@@ -0,0 +1,95 @@
"""
Support for the myStrom buttons.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mystrom/
"""
import asyncio
import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice, DOMAIN)
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up myStrom Binary Sensor."""
hass.http.register_view(MyStromView(async_add_devices))
return True
class MyStromView(HomeAssistantView):
"""View to handle requests from myStrom buttons."""
url = '/api/mystrom'
name = 'api:mystrom'
def __init__(self, add_devices):
"""Initialize the myStrom URL endpoint."""
self.buttons = {}
self.add_devices = add_devices
@asyncio.coroutine
def get(self, request):
"""The GET request received from a myStrom button."""
res = yield from self._handle(request.app['hass'], request.query)
return res
@asyncio.coroutine
def _handle(self, hass, data):
"""Handle requests to the myStrom endpoint."""
button_action = list(data.keys())[0]
button_id = data[button_action]
entity_id = '{}.{}_{}'.format(DOMAIN, button_id, button_action)
if button_action not in ['single', 'double', 'long', 'touch']:
_LOGGER.error(
"Received unidentified message from myStrom button: %s", data)
return ("Received unidentified message: {}".format(data),
HTTP_UNPROCESSABLE_ENTITY)
if entity_id not in self.buttons:
_LOGGER.info("New myStrom button/action detected: %s/%s",
button_id, button_action)
self.buttons[entity_id] = MyStromBinarySensor(
'{}_{}'.format(button_id, button_action))
hass.async_add_job(self.add_devices, [self.buttons[entity_id]])
else:
new_state = True if self.buttons[entity_id].state == 'off' \
else False
self.buttons[entity_id].async_on_update(new_state)
class MyStromBinarySensor(BinarySensorDevice):
"""Representation of a myStrom button."""
def __init__(self, button_id):
"""Initialize the myStrom Binary sensor."""
self._button_id = button_id
self._state = None
@property
def name(self):
"""Return the name of the sensor."""
return self._button_id
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._state
def async_on_update(self, value):
"""Receive an update."""
self._state = value
self.hass.async_add_job(self.async_update_ha_state())
+12 -8
View File
@@ -16,15 +16,18 @@ DEPENDENCIES = ['nest']
BINARY_TYPES = ['online']
CLIMATE_BINARY_TYPES = ['fan',
'is_using_emergency_heat',
'is_locked',
'has_leaf']
CLIMATE_BINARY_TYPES = [
'fan',
'is_using_emergency_heat',
'is_locked',
'has_leaf',
]
CAMERA_BINARY_TYPES = [
'motion_detected',
'sound_detected',
'person_detected']
'person_detected',
]
_BINARY_TYPES_DEPRECATED = [
'hvac_ac_state',
@@ -34,7 +37,8 @@ _BINARY_TYPES_DEPRECATED = [
'hvac_heat_x3_state',
'hvac_alt_heat_state',
'hvac_alt_heat_x2_state',
'hvac_emer_heat_state']
'hvac_emer_heat_state',
]
_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \
+ CAMERA_BINARY_TYPES
@@ -43,7 +47,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors."""
"""Set up the Nest binary sensors."""
if discovery_info is None:
return
@@ -93,7 +97,7 @@ class NestBinarySensor(NestSensor, BinarySensorDevice):
@property
def is_on(self):
"""True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return self._state
def update(self):
@@ -7,6 +7,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.netatmo/.
"""
import logging
import voluptuous as vol
from homeassistant.components.binary_sensor import (
@@ -16,10 +17,9 @@ from homeassistant.loader import get_component
from homeassistant.const import CONF_TIMEOUT, CONF_OFFSET
from homeassistant.helpers import config_validation as cv
DEPENDENCIES = ["netatmo"]
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['netatmo']
# These are the available sensors mapped to binary_sensor class
WELCOME_SENSOR_TYPES = {
@@ -34,8 +34,8 @@ PRESENCE_SENSOR_TYPES = {
"Outdoor vehicle": "motion"
}
TAG_SENSOR_TYPES = {
"Tag Vibration": 'vibration',
"Tag Open": 'opening'
"Tag Vibration": "vibration",
"Tag Open": "opening"
}
CONF_HOME = 'home'
@@ -61,7 +61,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup access to Netatmo binary sensor."""
"""Set up the access to Netatmo binary sensor."""
netatmo = get_component('netatmo')
home = config.get(CONF_HOME, None)
timeout = config.get(CONF_TIMEOUT, 15)
@@ -85,35 +85,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for camera_name in data.get_camera_names():
camera_type = data.get_camera_type(camera=camera_name, home=home)
if camera_type == "NACamera":
if camera_type == 'NACamera':
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
for variable in welcome_sensors:
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home, timeout,
offset, camera_type,
variable)])
if camera_type == "NOC":
add_devices([NetatmoBinarySensor(
data, camera_name, module_name, home, timeout,
offset, camera_type, variable)])
if camera_type == 'NOC':
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
camera_name not in config[CONF_CAMERAS]:
continue
for variable in presence_sensors:
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home, timeout,
offset, camera_type,
variable)])
add_devices([NetatmoBinarySensor(
data, camera_name, module_name, home, timeout, offset,
camera_type, variable)])
for module_name in data.get_module_names(camera_name):
for variable in tag_sensors:
camera_type = None
add_devices([NetatmoBinarySensor(data, camera_name,
module_name, home,
timeout, offset,
camera_type,
variable)])
add_devices([NetatmoBinarySensor(
data, camera_name, module_name, home, timeout, offset,
camera_type, variable)])
class NetatmoBinarySensor(BinarySensorDevice):
@@ -121,7 +117,7 @@ class NetatmoBinarySensor(BinarySensorDevice):
def __init__(self, data, camera_name, module_name, home,
timeout, offset, camera_type, sensor):
"""Setup for access to the Netatmo camera events."""
"""Set up for access to the Netatmo camera events."""
self._data = data
self._camera_name = camera_name
self._module_name = module_name
@@ -129,23 +125,23 @@ class NetatmoBinarySensor(BinarySensorDevice):
self._timeout = timeout
self._offset = offset
if home:
self._name = home + ' / ' + camera_name
self._name = '{} / {}'.format(home, camera_name)
else:
self._name = camera_name
if module_name:
self._name += ' / ' + module_name
self._sensor_name = sensor
self._name += ' ' + sensor
camera_id = data.camera_data.cameraByName(camera=camera_name,
home=home)['id']
self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(self._name,
camera_id)
camera_id = data.camera_data.cameraByName(
camera=camera_name, home=home)['id']
self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(
self._name, camera_id)
self._cameratype = camera_type
self.update()
@property
def name(self):
"""The name of the Netatmo device and this sensor."""
"""Return the name of the Netatmo device and this sensor."""
return self._name
@property
@@ -156,9 +152,9 @@ class NetatmoBinarySensor(BinarySensorDevice):
@property
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
if self._cameratype == "NACamera":
if self._cameratype == 'NACamera':
return WELCOME_SENSOR_TYPES.get(self._sensor_name)
elif self._cameratype == "NOC":
elif self._cameratype == 'NOC':
return PRESENCE_SENSOR_TYPES.get(self._sensor_name)
else:
return TAG_SENSOR_TYPES.get(self._sensor_name)
@@ -173,51 +169,44 @@ class NetatmoBinarySensor(BinarySensorDevice):
self._data.update()
self._data.update_event()
if self._cameratype == "NACamera":
if self._cameratype == 'NACamera':
if self._sensor_name == "Someone known":
self._state =\
self._data.camera_data.someoneKnownSeen(self._home,
self._camera_name,
self._timeout*60)
self._data.camera_data.someoneKnownSeen(
self._home, self._camera_name, self._timeout*60)
elif self._sensor_name == "Someone unknown":
self._state =\
self._data.camera_data.someoneUnknownSeen(
self._home, self._camera_name, self._timeout*60)
elif self._sensor_name == "Motion":
self._state =\
self._data.camera_data.motionDetected(self._home,
self._camera_name,
self._timeout*60)
elif self._cameratype == "NOC":
self._data.camera_data.motionDetected(
self._home, self._camera_name, self._timeout*60)
elif self._cameratype == 'NOC':
if self._sensor_name == "Outdoor motion":
self._state =\
self._data.camera_data.outdoormotionDetected(
self._home, self._camera_name, self._offset)
elif self._sensor_name == "Outdoor human":
self._state =\
self._data.camera_data.humanDetected(self._home,
self._camera_name,
self._offset)
self._data.camera_data.humanDetected(
self._home, self._camera_name, self._offset)
elif self._sensor_name == "Outdoor animal":
self._state =\
self._data.camera_data.animalDetected(self._home,
self._camera_name,
self._offset)
self._data.camera_data.animalDetected(
self._home, self._camera_name, self._offset)
elif self._sensor_name == "Outdoor vehicle":
self._state =\
self._data.camera_data.carDetected(self._home,
self._camera_name,
self._offset)
self._data.camera_data.carDetected(
self._home, self._camera_name, self._offset)
if self._sensor_name == "Tag Vibration":
self._state =\
self._data.camera_data.moduleMotionDetected(self._home,
self._module_name,
self._camera_name,
self._timeout*60)
self._data.camera_data.moduleMotionDetected(
self._home, self._module_name, self._camera_name,
self._timeout*60)
elif self._sensor_name == "Tag Open":
self._state =\
self._data.camera_data.moduleOpened(self._home,
self._module_name,
self._camera_name)
self._data.camera_data.moduleOpened(
self._home, self._module_name, self._camera_name)
else:
return None
@@ -41,7 +41,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the NX584 binary sensor platform."""
"""Set up the NX584 binary sensor platform."""
from nx584 import client as nx584_client
host = config.get(CONF_HOST)
@@ -53,7 +53,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
client = nx584_client.Client('http://{}:{}'.format(host, port))
zones = client.list_zones()
except requests.exceptions.ConnectionError as ex:
_LOGGER.error('Unable to connect to NX584: %s', str(ex))
_LOGGER.error("Unable to connect to NX584: %s", str(ex))
return False
version = [int(v) for v in client.get_version().split('.')]
@@ -9,14 +9,12 @@ import logging
import requests
import voluptuous as vol
from homeassistant.const import (
CONF_NAME, STATE_ON, STATE_OFF, CONF_MONITORED_CONDITIONS)
from homeassistant.const import CONF_NAME, CONF_MONITORED_CONDITIONS
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.loader import get_component
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['octoprint']
@@ -38,22 +36,18 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the available OctoPrint binary sensors."""
"""Set up the available OctoPrint binary sensors."""
octoprint = get_component('octoprint')
name = config.get(CONF_NAME)
monitored_conditions = config.get(CONF_MONITORED_CONDITIONS,
SENSOR_TYPES.keys())
monitored_conditions = config.get(
CONF_MONITORED_CONDITIONS, SENSOR_TYPES.keys())
devices = []
for octo_type in monitored_conditions:
new_sensor = OctoPrintBinarySensor(octoprint.OCTOPRINT,
octo_type,
SENSOR_TYPES[octo_type][2],
name,
SENSOR_TYPES[octo_type][3],
SENSOR_TYPES[octo_type][0],
SENSOR_TYPES[octo_type][1],
'flags')
new_sensor = OctoPrintBinarySensor(
octoprint.OCTOPRINT, octo_type, SENSOR_TYPES[octo_type][2],
name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0],
SENSOR_TYPES[octo_type][1], 'flags')
devices.append(new_sensor)
add_devices(devices)
@@ -85,18 +79,10 @@ class OctoPrintBinarySensor(BinarySensorDevice):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the sensor."""
return self.is_on
@property
def is_on(self):
"""Return true if binary sensor is on."""
if self._state:
return STATE_ON
else:
return STATE_OFF
return bool(self._state)
@property
def device_class(self):
@@ -106,10 +92,9 @@ class OctoPrintBinarySensor(BinarySensorDevice):
def update(self):
"""Update state of sensor."""
try:
self._state = self.api.update(self.sensor_type,
self.api_endpoint,
self.api_group,
self.api_tool)
self._state = self.api.update(
self.sensor_type, self.api_endpoint, self.api_group,
self.api_tool)
except requests.exceptions.ConnectionError:
# Error calling the api, already logged in api.update()
return
@@ -0,0 +1,186 @@
"""
Support for Pilight binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.pilight/
"""
import datetime
import logging
import voluptuous as vol
from homeassistant.components import pilight
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA,
BinarySensorDevice,
)
from homeassistant.const import (
CONF_DISARM_AFTER_TRIGGER,
CONF_NAME,
CONF_PAYLOAD,
CONF_PAYLOAD_OFF,
CONF_PAYLOAD_ON
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import track_point_in_time
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
CONF_VARIABLE = 'variable'
DEFAULT_NAME = 'Pilight Binary Sensor'
DEPENDENCIES = ['pilight']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_VARIABLE): cv.string,
vol.Required(CONF_PAYLOAD): vol.Schema(dict),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PAYLOAD_ON, default='on'): cv.string,
vol.Optional(CONF_PAYLOAD_OFF, default='off'): cv.string,
vol.Optional(CONF_DISARM_AFTER_TRIGGER, default=False): cv.boolean
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Pilight Binary Sensor."""
disarm = config.get(CONF_DISARM_AFTER_TRIGGER)
if disarm:
add_devices([PilightTriggerSensor(
hass=hass,
name=config.get(CONF_NAME),
variable=config.get(CONF_VARIABLE),
payload=config.get(CONF_PAYLOAD),
on_value=config.get(CONF_PAYLOAD_ON),
off_value=config.get(CONF_PAYLOAD_OFF),
)])
else:
add_devices([PilightBinarySensor(
hass=hass,
name=config.get(CONF_NAME),
variable=config.get(CONF_VARIABLE),
payload=config.get(CONF_PAYLOAD),
on_value=config.get(CONF_PAYLOAD_ON),
off_value=config.get(CONF_PAYLOAD_OFF),
)])
class PilightBinarySensor(BinarySensorDevice):
"""Representation of a binary sensor that can be updated using Pilight."""
def __init__(self, hass, name, variable, payload, on_value, off_value):
"""Initialize the sensor."""
self._state = False
self._hass = hass
self._name = name
self._variable = variable
self._payload = payload
self._on_value = on_value
self._off_value = off_value
hass.bus.listen(pilight.EVENT, self._handle_code)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._state
def _handle_code(self, call):
"""Handle received code by the pilight-daemon.
If the code matches the defined playload
of this sensor the sensor state is changed accordingly.
"""
# Check if received code matches defined playoad
# True if payload is contained in received code dict
payload_ok = True
for key in self._payload:
if key not in call.data:
payload_ok = False
continue
if self._payload[key] != call.data[key]:
payload_ok = False
# Read out variable if payload ok
if payload_ok:
if self._variable not in call.data:
return
value = call.data[self._variable]
self._state = (value == self._on_value)
self.schedule_update_ha_state()
class PilightTriggerSensor(BinarySensorDevice):
"""Representation of a binary sensor that can be updated using Pilight."""
def __init__(
self,
hass,
name,
variable,
payload,
on_value,
off_value,
rst_dly_sec=30):
"""Initialize the sensor."""
self._state = False
self._hass = hass
self._name = name
self._variable = variable
self._payload = payload
self._on_value = on_value
self._off_value = off_value
self._reset_delay_sec = rst_dly_sec
self._delay_after = None
self._hass = hass
hass.bus.listen(pilight.EVENT, self._handle_code)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._state
def _reset_state(self, call):
self._state = False
self._delay_after = None
self.schedule_update_ha_state()
def _handle_code(self, call):
"""Handle received code by the pilight-daemon.
If the code matches the defined playload
of this sensor the sensor state is changed accordingly.
"""
# Check if received code matches defined playoad
# True if payload is contained in received code dict
payload_ok = True
for key in self._payload:
if key not in call.data:
payload_ok = False
continue
if self._payload[key] != call.data[key]:
payload_ok = False
# Read out variable if payload ok
if payload_ok:
if self._variable not in call.data:
return
value = call.data[self._variable]
self._state = (value == self._on_value)
if self._delay_after is None:
self._delay_after = dt_util.utcnow() + datetime.timedelta(
seconds=self._reset_delay_sec)
track_point_in_time(
self._hass, self._reset_state,
self._delay_after)
self.schedule_update_ha_state()
+21 -8
View File
@@ -35,6 +35,9 @@ SCAN_INTERVAL = timedelta(minutes=5)
PING_MATCHER = re.compile(
r'(?P<min>\d+.\d+)\/(?P<avg>\d+.\d+)\/(?P<max>\d+.\d+)\/(?P<mdev>\d+.\d+)')
WIN32_PING_MATCHER = re.compile(
r'(?P<min>\d+)ms.+(?P<max>\d+)ms.+(?P<avg>\d+)ms')
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -102,7 +105,7 @@ class PingData(object):
if sys.platform == 'win32':
self._ping_cmd = [
'ping', '-n', str(self._count), '-w 1000', self._ip_address]
'ping', '-n', str(self._count), '-w', '1000', self._ip_address]
else:
self._ping_cmd = [
'ping', '-n', '-q', '-c', str(self._count), '-W1',
@@ -114,13 +117,23 @@ class PingData(object):
self._ping_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
out = pinger.communicate()
match = PING_MATCHER.search(str(out).split('\n')[-1])
rtt_min, rtt_avg, rtt_max, rtt_mdev = match.groups()
return {
'min': rtt_min,
'avg': rtt_avg,
'max': rtt_max,
'mdev': rtt_mdev}
_LOGGER.debug("Output is %s", str(out))
if sys.platform == 'win32':
match = WIN32_PING_MATCHER.search(str(out).split('\n')[-1])
rtt_min, rtt_avg, rtt_max = match.groups()
return {
'min': rtt_min,
'avg': rtt_avg,
'max': rtt_max,
'mdev': ''}
else:
match = PING_MATCHER.search(str(out).split('\n')[-1])
rtt_min, rtt_avg, rtt_max, rtt_mdev = match.groups()
return {
'min': rtt_min,
'avg': rtt_avg,
'max': rtt_max,
'mdev': rtt_mdev}
except (subprocess.CalledProcessError, AttributeError):
return False
@@ -0,0 +1,131 @@
"""
Configure a binary_sensor using a digital input from a raspihats board.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.raspihats/
"""
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_NAME, CONF_DEVICE_CLASS, DEVICE_DEFAULT_NAME
)
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice
)
from homeassistant.components.raspihats import (
CONF_I2C_HATS, CONF_BOARD, CONF_ADDRESS, CONF_CHANNELS, CONF_INDEX,
CONF_INVERT_LOGIC, I2C_HAT_NAMES, I2C_HATS_MANAGER, I2CHatsException
)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['raspihats']
DEFAULT_INVERT_LOGIC = False
DEFAULT_DEVICE_CLASS = None
_CHANNELS_SCHEMA = vol.Schema([{
vol.Required(CONF_INDEX): cv.positive_int,
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): cv.string,
}])
_I2C_HATS_SCHEMA = vol.Schema([{
vol.Required(CONF_BOARD): vol.In(I2C_HAT_NAMES),
vol.Required(CONF_ADDRESS): vol.Coerce(int),
vol.Required(CONF_CHANNELS): _CHANNELS_SCHEMA
}])
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_I2C_HATS): _I2C_HATS_SCHEMA,
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the raspihats binary_sensor devices."""
I2CHatBinarySensor.I2C_HATS_MANAGER = hass.data[I2C_HATS_MANAGER]
binary_sensors = []
i2c_hat_configs = config.get(CONF_I2C_HATS)
for i2c_hat_config in i2c_hat_configs:
address = i2c_hat_config[CONF_ADDRESS]
board = i2c_hat_config[CONF_BOARD]
try:
I2CHatBinarySensor.I2C_HATS_MANAGER.register_board(board, address)
for channel_config in i2c_hat_config[CONF_CHANNELS]:
binary_sensors.append(
I2CHatBinarySensor(
address,
channel_config[CONF_INDEX],
channel_config[CONF_NAME],
channel_config[CONF_INVERT_LOGIC],
channel_config[CONF_DEVICE_CLASS]
)
)
except I2CHatsException as ex:
_LOGGER.error(
"Failed to register " + board + "I2CHat@" + hex(address) + " "
+ str(ex)
)
add_devices(binary_sensors)
class I2CHatBinarySensor(BinarySensorDevice):
"""Represents a binary sensor that uses a I2C-HAT digital input."""
I2C_HATS_MANAGER = None
def __init__(self, address, channel, name, invert_logic, device_class):
"""Initialize sensor."""
self._address = address
self._channel = channel
self._name = name or DEVICE_DEFAULT_NAME
self._invert_logic = invert_logic
self._device_class = device_class
self._state = self.I2C_HATS_MANAGER.read_di(
self._address,
self._channel
)
def online_callback():
"""Callback fired when board is online."""
self.schedule_update_ha_state()
self.I2C_HATS_MANAGER.register_online_callback(
self._address,
self._channel,
online_callback
)
def edge_callback(state):
"""Read digital input state."""
self._state = state
self.schedule_update_ha_state()
self.I2C_HATS_MANAGER.register_di_callback(
self._address,
self._channel,
edge_callback
)
@property
def device_class(self):
"""Return the class of this sensor."""
return self._device_class
@property
def name(self):
"""Return the name of this sensor."""
return self._name
@property
def should_poll(self):
"""Polling not needed for this sensor."""
return False
@property
def is_on(self):
"""Return the state of this sensor."""
return self._state != self._invert_logic
@@ -44,7 +44,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the REST binary sensor."""
"""Set up the REST binary sensor."""
name = config.get(CONF_NAME)
resource = config.get(CONF_RESOURCE)
method = config.get(CONF_METHOD)
@@ -114,8 +114,8 @@ class RestBinarySensor(BinarySensorDevice):
try:
return bool(int(response))
except ValueError:
return {"true": True, "on": True, "open": True,
"yes": True}.get(response.lower(), False)
return {'true': True, 'on': True, 'open': True,
'yes': True}.get(response.lower(), False)
def update(self):
"""Get the latest data from REST API and updates the state."""
@@ -41,10 +41,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Raspberry PI GPIO devices."""
pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE)
bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME)
invert_logic = config.get('invert_logic', DEFAULT_INVERT_LOGIC)
"""Set up the Raspberry PI GPIO devices."""
pull_mode = config.get(CONF_PULL_MODE)
bouncetime = config.get(CONF_BOUNCETIME)
invert_logic = config.get(CONF_INVERT_LOGIC)
binary_sensors = []
ports = config.get('ports')
@@ -0,0 +1,93 @@
"""
Support for binary sensor using the PiFace Digital I/O module on a RPi.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rpi_pfio/
"""
import logging
import voluptuous as vol
import homeassistant.components.rpi_pfio as rpi_pfio
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import DEVICE_DEFAULT_NAME
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
ATTR_NAME = 'name'
ATTR_INVERT_LOGIC = 'invert_logic'
ATTR_SETTLE_TIME = 'settle_time'
CONF_PORTS = 'ports'
DEFAULT_INVERT_LOGIC = False
DEFAULT_SETTLE_TIME = 20
DEPENDENCIES = ['rpi_pfio']
PORT_SCHEMA = vol.Schema({
vol.Optional(ATTR_NAME, default=None): cv.string,
vol.Optional(ATTR_SETTLE_TIME, default=DEFAULT_SETTLE_TIME):
cv.positive_int,
vol.Optional(ATTR_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORTS, default={}): vol.Schema({
cv.positive_int: PORT_SCHEMA
})
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the PiFace Digital Input devices."""
binary_sensors = []
ports = config.get('ports')
for port, port_entity in ports.items():
name = port_entity[ATTR_NAME]
settle_time = port_entity[ATTR_SETTLE_TIME] / 1000
invert_logic = port_entity[ATTR_INVERT_LOGIC]
binary_sensors.append(RPiPFIOBinarySensor(
hass, port, name, settle_time, invert_logic))
add_devices(binary_sensors, True)
rpi_pfio.activate_listener(hass)
class RPiPFIOBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that a PiFace Digital Input."""
def __init__(self, hass, port, name, settle_time, invert_logic):
"""Initialize the RPi binary sensor."""
self._port = port
self._name = name or DEVICE_DEFAULT_NAME
self._invert_logic = invert_logic
self._state = None
def read_pfio(port):
"""Read state from PFIO."""
self._state = rpi_pfio.read_input(self._port)
self.schedule_update_ha_state()
rpi_pfio.edge_detect(hass, self._port, read_pfio, settle_time)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return the state of the entity."""
return self._state != self._invert_logic
def update(self):
"""Update the PFIO state."""
self._state = rpi_pfio.read_input(self._port)
@@ -11,7 +11,7 @@ DEPENDENCIES = ['sleepiq']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the SleepIQ sensors."""
"""Set up the SleepIQ sensors."""
if discovery_info is None:
return
@@ -27,5 +27,5 @@ class TcpBinarySensor(BinarySensorDevice, TcpSensor):
@property
def is_on(self):
"""True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return self._state == self._config[CONF_VALUE_ON]
@@ -41,7 +41,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup template binary sensors."""
"""Set up template binary sensors."""
sensors = []
for device, device_config in config[CONF_SENSORS].items():
@@ -57,15 +57,11 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
sensors.append(
BinarySensorTemplate(
hass,
device,
friendly_name,
device_class,
value_template,
hass, device, friendly_name, device_class, value_template,
entity_ids)
)
if not sensors:
_LOGGER.error('No sensors added')
_LOGGER.error("No sensors added")
return False
async_add_devices(sensors, True)
@@ -79,8 +75,8 @@ class BinarySensorTemplate(BinarySensorDevice):
value_template, entity_ids):
"""Initialize the Template binary sensor."""
self.hass = hass
self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device,
hass=hass)
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, device, hass=hass)
self._name = friendly_name
self._device_class = device_class
self._template = value_template
@@ -96,7 +92,7 @@ class BinarySensorTemplate(BinarySensorDevice):
@callback
def template_bsensor_state_listener(entity, old_state, new_state):
"""Called when the target device changes state."""
"""Handle the target device state changes."""
self.hass.async_add_job(self.async_update_ha_state(True))
@callback
@@ -139,8 +135,8 @@ class BinarySensorTemplate(BinarySensorDevice):
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
# Common during HA startup - so just a warning
_LOGGER.warning('Could not render template %s,'
' the state is unknown.', self._name)
_LOGGER.warning("Could not render template %s, "
"the state is unknown", self._name)
return
_LOGGER.error('Could not render template %s: %s', self._name, ex)
_LOGGER.error("Could not render template %s: %s", self._name, ex)
self._state = False
@@ -77,7 +77,7 @@ class ThresholdSensor(BinarySensorDevice):
# pylint: disable=invalid-name
def async_threshold_sensor_state_listener(
entity, old_state, new_state):
"""Called when the sensor changes state."""
"""Handle sensor state changes."""
if new_state.state == STATE_UNKNOWN:
return
+10 -19
View File
@@ -6,22 +6,18 @@ https://home-assistant.io/components/sensor.trend/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice,
ENTITY_ID_FORMAT,
PLATFORM_SCHEMA,
BinarySensorDevice, ENTITY_ID_FORMAT, PLATFORM_SCHEMA,
DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
ATTR_FRIENDLY_NAME,
ATTR_ENTITY_ID,
CONF_SENSOR_CLASS,
CONF_DEVICE_CLASS,
STATE_UNKNOWN,)
ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_SENSOR_CLASS,
CONF_DEVICE_CLASS, STATE_UNKNOWN,)
from homeassistant.helpers.deprecation import get_deprecated
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.event import track_state_change
@@ -47,7 +43,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the trend sensors."""
"""Set up the trend sensors."""
sensors = []
for device, device_config in config[CONF_SENSORS].items():
@@ -60,13 +56,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensors.append(
SensorTrend(
hass,
device,
friendly_name,
entity_id,
attribute,
device_class,
invert)
hass, device, friendly_name, entity_id, attribute,
device_class, invert)
)
if not sensors:
_LOGGER.error("No sensors added")
@@ -82,8 +73,8 @@ class SensorTrend(BinarySensorDevice):
target_entity, attribute, device_class, invert):
"""Initialize the sensor."""
self._hass = hass
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id,
hass=hass)
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, device_id, hass=hass)
self._name = friendly_name
self._target_entity = target_entity
self._attribute = attribute
@@ -95,7 +86,7 @@ class SensorTrend(BinarySensorDevice):
@callback
def trend_sensor_state_listener(entity, old_state, new_state):
"""Called when the target device changes state."""
"""Handle the target device state changes."""
self.from_state = old_state
self.to_state = new_state
hass.async_add_job(self.async_update_ha_state(True))
@@ -3,7 +3,6 @@ Support for VOC.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.volvooncall/
"""
import logging
@@ -14,7 +13,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Volvo sensors."""
"""Set up the Volvo sensors."""
if discovery_info is None:
return
add_devices([VolvoSensor(hass, *discovery_info)])
@@ -28,7 +27,7 @@ class VolvoSensor(VolvoEntity, BinarySensorDevice):
"""Return True if the binary sensor is on."""
val = getattr(self.vehicle, self._attribute)
if self._attribute == 'bulb_failures':
return len(val) > 0
return bool(val)
elif self._attribute in ['doors', 'windows']:
return any([val[key] for key in val if 'Open' in key])
else:
@@ -29,7 +29,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
class WemoBinarySensor(BinarySensorDevice):
"""Represents a WeMo binary sensor."""
"""Representation a WeMo binary sensor."""
def __init__(self, device):
"""Initialize the WeMo sensor."""
@@ -41,10 +41,8 @@ class WemoBinarySensor(BinarySensorDevice):
wemo.SUBSCRIPTION_REGISTRY.on(self.wemo, None, self._update_callback)
def _update_callback(self, _device, _type, _params):
"""Called by the Wemo device callback to update state."""
_LOGGER.info(
'Subscription update for %s',
_device)
"""Handle state changes."""
_LOGGER.info("Subscription update for %s", _device)
updated = self.wemo.subscription_update(_type, _params)
self._update(force_update=(not updated))
@@ -60,7 +58,7 @@ class WemoBinarySensor(BinarySensorDevice):
@property
def unique_id(self):
"""Return the id of this WeMo device."""
return "{}.{}".format(self.__class__, self.wemo.serialnumber)
return '{}.{}'.format(self.__class__, self.wemo.serialnumber)
@property
def name(self):
@@ -69,7 +67,7 @@ class WemoBinarySensor(BinarySensorDevice):
@property
def is_on(self):
"""True if sensor is on."""
"""Return true if sensor is on."""
return self._state
def update(self):
@@ -80,5 +78,5 @@ class WemoBinarySensor(BinarySensorDevice):
try:
self._state = self.wemo.get_state(force_update)
except AttributeError as err:
_LOGGER.warning('Could not update status for %s (%s)',
self.name, err)
_LOGGER.warning(
"Could not update status for %s (%s)", self.name, err)
+20 -14
View File
@@ -4,6 +4,7 @@ Support for Wink binary sensors.
For more details about this platform, please refer to the documentation at
at https://home-assistant.io/components/binary_sensor.wink/
"""
import asyncio
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
@@ -16,23 +17,23 @@ DEPENDENCIES = ['wink']
# These are the available sensors mapped to binary_sensor class
SENSOR_TYPES = {
"opened": "opening",
"brightness": "light",
"vibration": "vibration",
"loudness": "sound",
"noise": "sound",
"capturing_audio": "sound",
"liquid_detected": "moisture",
"motion": "motion",
"presence": "occupancy",
"co_detected": "gas",
"smoke_detected": "smoke",
"capturing_video": None
'opened': 'opening',
'brightness': 'light',
'vibration': 'vibration',
'loudness': 'sound',
'noise': 'sound',
'capturing_audio': 'sound',
'liquid_detected': 'moisture',
'motion': 'motion',
'presence': 'occupancy',
'co_detected': 'gas',
'smoke_detected': 'smoke',
'capturing_video': None
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink binary sensor platform."""
"""Set up the Wink binary sensor platform."""
import pywink
for sensor in pywink.get_sensors():
@@ -83,7 +84,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if camera_sensor.capability() in SENSOR_TYPES:
add_devices([WinkBinarySensorDevice(camera_sensor, hass)])
except AttributeError:
_LOGGER.info("Device isn't a sensor, skipping.")
_LOGGER.info("Device isn't a sensor, skipping")
class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity):
@@ -101,6 +102,11 @@ class WinkBinarySensorDevice(WinkDevice, BinarySensorDevice, Entity):
else:
self.capability = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['binary_sensor'].append(self)
@property
def is_on(self):
"""Return true if the binary sensor is on."""
@@ -11,10 +11,9 @@ import datetime
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
STATE_ON, STATE_OFF, STATE_UNKNOWN, CONF_NAME, WEEKDAYS)
from homeassistant.const import CONF_NAME, WEEKDAYS
import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity
from homeassistant.components.binary_sensor import BinarySensorDevice
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -66,15 +65,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
obj_holidays = getattr(holidays, country)(years=year)
if province:
if province not in obj_holidays.PROVINCES and \
province not in obj_holidays.STATES:
# 'state' and 'prov' are not interchangeable, so need to make
# sure we use the right one
if (hasattr(obj_holidays, "PROVINCES") and
province in obj_holidays.PROVINCES):
obj_holidays = getattr(holidays, country)(prov=province,
years=year)
elif (hasattr(obj_holidays, "STATES") and
province in obj_holidays.STATES):
obj_holidays = getattr(holidays, country)(state=province,
years=year)
else:
_LOGGER.error("There is no province/state %s in country %s",
province, country)
return False
else:
year = datetime.datetime.now().year
obj_holidays = getattr(holidays, country)(prov=province,
years=year)
_LOGGER.debug("Found the following holidays for your configuration:")
for date, name in sorted(obj_holidays.items()):
@@ -92,7 +96,7 @@ def day_to_string(day):
return None
class IsWorkdaySensor(Entity):
class IsWorkdaySensor(BinarySensorDevice):
"""Implementation of a Workday sensor."""
def __init__(self, obj_holidays, workdays, excludes, name):
@@ -101,7 +105,7 @@ class IsWorkdaySensor(Entity):
self._obj_holidays = obj_holidays
self._workdays = workdays
self._excludes = excludes
self._state = STATE_UNKNOWN
self._state = None
@property
def name(self):
@@ -109,7 +113,7 @@ class IsWorkdaySensor(Entity):
return self._name
@property
def state(self):
def is_on(self):
"""Return the state of the device."""
return self._state
@@ -135,14 +139,14 @@ class IsWorkdaySensor(Entity):
def async_update(self):
"""Get date and look whether it is a holiday."""
# Default is no workday
self._state = STATE_OFF
self._state = False
# Get iso day of the week (1 = Monday, 7 = Sunday)
day = datetime.datetime.today().isoweekday() - 1
day_of_week = day_to_string(day)
if self.is_include(day_of_week, dt_util.now()):
self._state = STATE_ON
self._state = True
if self.is_exclude(day_of_week, dt_util.now()):
self._state = STATE_OFF
self._state = False
@@ -0,0 +1,89 @@
"""
Binary sensors on Zigbee Home Automation networks.
For more details on this platform, please refer to the documentation
at https://home-assistant.io/components/binary_sensor.zha/
"""
import asyncio
import logging
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components import zha
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['zha']
# ZigBee Cluster Library Zone Type to Home Assistant device class
CLASS_MAPPING = {
0x000d: 'motion',
0x0015: 'opening',
0x0028: 'smoke',
0x002a: 'moisture',
0x002b: 'gas',
0x002d: 'vibration',
}
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Zigbee Home Automation binary sensors."""
discovery_info = zha.get_discovery_info(hass, discovery_info)
if discovery_info is None:
return
from bellows.zigbee.zcl.clusters.security import IasZone
clusters = discovery_info['clusters']
device_class = None
cluster = [c for c in clusters if isinstance(c, IasZone)][0]
if discovery_info['new_join']:
yield from cluster.bind()
ieee = cluster.endpoint.device.application.ieee
yield from cluster.write_attributes({'cie_addr': ieee})
try:
zone_type = yield from cluster['zone_type']
device_class = CLASS_MAPPING.get(zone_type, None)
except Exception: # pylint: disable=broad-except
# If we fail to read from the device, use a non-specific class
pass
sensor = BinarySensor(device_class, **discovery_info)
async_add_devices([sensor])
class BinarySensor(zha.Entity, BinarySensorDevice):
"""THe ZHA Binary Sensor."""
_domain = DOMAIN
def __init__(self, device_class, **kwargs):
"""Initialize the ZHA binary sensor."""
super().__init__(**kwargs)
self._device_class = device_class
from bellows.zigbee.zcl.clusters.security import IasZone
self._ias_zone_cluster = self._clusters[IasZone.cluster_id]
@property
def is_on(self) -> bool:
"""Return True if entity is on."""
if self._state == 'unknown':
return False
return bool(self._state)
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return self._device_class
def cluster_command(self, aps_frame, tsn, command_id, args):
"""Handle commands received to this cluster."""
if command_id == 0:
self._state = args[0] & 3
_LOGGER.debug("Updated alarm state: %s", self._state)
self.schedule_update_ha_state()
elif command_id == 1:
_LOGGER.debug("Enroll requested")
self.hass.add_job(self._ias_zone_cluster.enroll_response(0, 0))
@@ -23,7 +23,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the ZigBee binary sensor platform."""
"""Set up the ZigBee binary sensor platform."""
add_devices(
[ZigBeeBinarySensor(hass, ZigBeeDigitalInConfig(config))], True)
@@ -20,7 +20,7 @@ DEPENDENCIES = []
def get_device(values, **kwargs):
"""Create zwave entity device."""
"""Create Z-Wave entity device."""
device_mapping = workaround.get_device_mapping(values.primary)
if device_mapping == workaround.WORKAROUND_NO_OFF_EVENT:
# Default the multiplier to 4
@@ -45,12 +45,12 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
self._state = self.values.primary.data
def update_properties(self):
"""Callback on data changes for node values."""
"""Handle data changes for node values."""
self._state = self.values.primary.data
@property
def is_on(self):
"""Return True if the binary sensor is on."""
"""Return true if the binary sensor is on."""
return self._state
@property
@@ -69,7 +69,7 @@ class ZWaveTriggerSensor(ZWaveBinarySensor):
self.invalidate_after = None
def update_properties(self):
"""Called when a value for this entity's node has changed."""
"""Handle value changes for this entity's node."""
self._state = self.values.primary.data
# only allow this value to be true for re_arm secs
if not self.hass:
@@ -83,7 +83,7 @@ class ZWaveTriggerSensor(ZWaveBinarySensor):
@property
def is_on(self):
"""Return True if movement has happened within the rearm time."""
"""Return true if movement has happened within the rearm time."""
return self._state and \
(self.invalidate_after is None or
self.invalidate_after > dt_util.utcnow())
+1 -1
View File
@@ -13,7 +13,7 @@ from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, ATTR_FRIENDLY_NAME, ATTR_ARMED)
from homeassistant.helpers import discovery
REQUIREMENTS = ['blinkpy==0.5.2']
REQUIREMENTS = ['blinkpy==0.6.0']
_LOGGER = logging.getLogger(__name__)
+3 -4
View File
@@ -35,7 +35,7 @@ CONFIG_SCHEMA = vol.Schema({
# pylint: disable=unused-argument
def setup(hass, config):
"""Setup BloomSky component."""
"""Set up the BloomSky component."""
api_key = config[DOMAIN][CONF_API_KEY]
global BLOOMSKY
@@ -67,9 +67,8 @@ class BloomSky(object):
def refresh_devices(self):
"""Use the API to retrieve a list of devices."""
_LOGGER.debug("Fetching BloomSky update")
response = requests.get(self.API_URL,
headers={"Authorization": self._api_key},
timeout=10)
response = requests.get(
self.API_URL, headers={"Authorization": self._api_key}, timeout=10)
if response.status_code == 401:
raise RuntimeError("Invalid API_KEY")
elif response.status_code != 200:
+9 -10
View File
@@ -7,12 +7,10 @@ https://home-assistant.io/components/calendar/
import asyncio
import logging
from datetime import timedelta
import re
from homeassistant.components.google import (CONF_OFFSET,
CONF_DEVICE_ID,
CONF_NAME)
from homeassistant.components.google import (
CONF_OFFSET, CONF_DEVICE_ID, CONF_NAME)
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.config_validation import time_period_str
from homeassistant.helpers.entity import Entity, generate_entity_id
@@ -22,16 +20,18 @@ from homeassistant.util import dt
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
DOMAIN = 'calendar'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SCAN_INTERVAL = timedelta(seconds=60)
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for calendars."""
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, DOMAIN)
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DOMAIN)
yield from component.async_setup(config)
return True
@@ -55,9 +55,8 @@ class CalendarEventDevice(Entity):
self._name = data.get(CONF_NAME)
self.dev_id = data.get(CONF_DEVICE_ID)
self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET)
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT,
self.dev_id,
hass=hass)
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, self.dev_id, hass=hass)
self._cal_data = {
'all_day': False,
@@ -87,7 +86,7 @@ class CalendarEventDevice(Entity):
@property
def device_state_attributes(self):
"""State Attributes for HA."""
"""Return the device state attributes."""
start = self._cal_data.get('start', None)
end = self._cal_data.get('end', None)
start = start.strftime(DATE_STR_FORMAT) if start is not None else None
+81 -81
View File
@@ -1,82 +1,82 @@
"""
Demo platform that has two fake binary sensors.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.util.dt as dt_util
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo binary sensor platform."""
calendar_data_future = DemoGoogleCalendarDataFuture()
calendar_data_current = DemoGoogleCalendarDataCurrent()
add_devices([
DemoGoogleCalendar(hass, calendar_data_future, {
CONF_NAME: 'Future Event',
CONF_DEVICE_ID: 'future_event',
}),
DemoGoogleCalendar(hass, calendar_data_current, {
CONF_NAME: 'Current Event',
CONF_DEVICE_ID: 'current_event',
}),
])
class DemoGoogleCalendarData(object):
"""Setup base class for data."""
"""
Demo platform that has two fake binary sensors.
# pylint: disable=no-self-use
def update(self):
"""Return true so entity knows we have new data."""
return True
class DemoGoogleCalendarDataFuture(DemoGoogleCalendarData):
"""Setup future data event."""
def __init__(self):
"""Set the event to a future event."""
one_hour_from_now = dt_util.now() \
+ dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': one_hour_from_now.isoformat()
},
'end': {
'dateTime': (one_hour_from_now + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Future Event',
}
class DemoGoogleCalendarDataCurrent(DemoGoogleCalendarData):
"""Create a current event we're in the middle of."""
def __init__(self):
"""Set the event data."""
middle_of_event = dt_util.now() \
- dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': middle_of_event.isoformat()
},
'end': {
'dateTime': (middle_of_event + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Current Event',
}
class DemoGoogleCalendar(CalendarEventDevice):
"""A Demo binary sensor."""
def __init__(self, hass, calendar_data, data):
"""The same as a google calendar but without the api calls."""
self.data = calendar_data
super().__init__(hass, data)
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.util.dt as dt_util
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Demo Calendar platform."""
calendar_data_future = DemoGoogleCalendarDataFuture()
calendar_data_current = DemoGoogleCalendarDataCurrent()
add_devices([
DemoGoogleCalendar(hass, calendar_data_future, {
CONF_NAME: 'Future Event',
CONF_DEVICE_ID: 'future_event',
}),
DemoGoogleCalendar(hass, calendar_data_current, {
CONF_NAME: 'Current Event',
CONF_DEVICE_ID: 'current_event',
}),
])
class DemoGoogleCalendarData(object):
"""Representation of a Demo Calendar element."""
# pylint: disable=no-self-use
def update(self):
"""Return true so entity knows we have new data."""
return True
class DemoGoogleCalendarDataFuture(DemoGoogleCalendarData):
"""Representation of a Demo Calendar for a future event."""
def __init__(self):
"""Set the event to a future event."""
one_hour_from_now = dt_util.now() \
+ dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': one_hour_from_now.isoformat()
},
'end': {
'dateTime': (one_hour_from_now + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Future Event',
}
class DemoGoogleCalendarDataCurrent(DemoGoogleCalendarData):
"""Representation of a Demo Calendar for a current event."""
def __init__(self):
"""Set the event data."""
middle_of_event = dt_util.now() \
- dt_util.dt.timedelta(minutes=30)
self.event = {
'start': {
'dateTime': middle_of_event.isoformat()
},
'end': {
'dateTime': (middle_of_event + dt_util.dt.
timedelta(minutes=60)).isoformat()
},
'summary': 'Current Event',
}
class DemoGoogleCalendar(CalendarEventDevice):
"""Representation of a Demo Calendar element."""
def __init__(self, hass, calendar_data, data):
"""Initialize Google Calendar but without the API calls."""
self.data = calendar_data
super().__init__(hass, data)
+78 -79
View File
@@ -1,79 +1,78 @@
"""
Support for Google Calendar Search binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.google_calendar/
"""
# pylint: disable=import-error
import logging
from datetime import timedelta
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import (CONF_CAL_ID, CONF_ENTITIES,
CONF_TRACK, TOKEN_FILE,
GoogleCalendarService)
from homeassistant.util import Throttle, dt
DEFAULT_GOOGLE_SEARCH_PARAMS = {
'orderBy': 'startTime',
'maxResults': 1,
'singleEvents': True,
}
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, disc_info=None):
"""Setup the calendar platform for event devices."""
if disc_info is None:
return
if not any([data[CONF_TRACK] for data in disc_info[CONF_ENTITIES]]):
return
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
add_devices([GoogleCalendarEventDevice(hass, calendar_service,
disc_info[CONF_CAL_ID], data)
for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]])
# pylint: disable=too-many-instance-attributes
class GoogleCalendarEventDevice(CalendarEventDevice):
"""A calendar event device."""
def __init__(self, hass, calendar_service, calendar, data):
"""Create the Calendar event device."""
self.data = GoogleCalendarData(calendar_service, calendar,
data.get('search', None))
super().__init__(hass, data)
class GoogleCalendarData(object):
"""Class to utilize calendar service object to get next event."""
def __init__(self, calendar_service, calendar_id, search=None):
"""Setup how we are going to search the google calendar."""
self.calendar_service = calendar_service
self.calendar_id = calendar_id
self.search = search
self.event = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
service = self.calendar_service.get()
params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
params['timeMin'] = dt.now().isoformat('T')
params['calendarId'] = self.calendar_id
if self.search:
params['q'] = self.search
events = service.events() # pylint: disable=no-member
result = events.list(**params).execute()
items = result.get('items', [])
self.event = items[0] if len(items) == 1 else None
return True
"""
Support for Google Calendar Search binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.google_calendar/
"""
# pylint: disable=import-error
import logging
from datetime import timedelta
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import (
CONF_CAL_ID, CONF_ENTITIES, CONF_TRACK, TOKEN_FILE,
GoogleCalendarService)
from homeassistant.util import Throttle, dt
_LOGGER = logging.getLogger(__name__)
DEFAULT_GOOGLE_SEARCH_PARAMS = {
'orderBy': 'startTime',
'maxResults': 1,
'singleEvents': True,
}
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
def setup_platform(hass, config, add_devices, disc_info=None):
"""Set up the calendar platform for event devices."""
if disc_info is None:
return
if not any([data[CONF_TRACK] for data in disc_info[CONF_ENTITIES]]):
return
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
add_devices([GoogleCalendarEventDevice(hass, calendar_service,
disc_info[CONF_CAL_ID], data)
for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]])
# pylint: disable=too-many-instance-attributes
class GoogleCalendarEventDevice(CalendarEventDevice):
"""A calendar event device."""
def __init__(self, hass, calendar_service, calendar, data):
"""Create the Calendar event device."""
self.data = GoogleCalendarData(calendar_service, calendar,
data.get('search', None))
super().__init__(hass, data)
class GoogleCalendarData(object):
"""Class to utilize calendar service object to get next event."""
def __init__(self, calendar_service, calendar_id, search=None):
"""Set up how we are going to search the google calendar."""
self.calendar_service = calendar_service
self.calendar_id = calendar_id
self.search = search
self.event = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
service = self.calendar_service.get()
params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
params['timeMin'] = dt.now().isoformat('T')
params['calendarId'] = self.calendar_id
if self.search:
params['q'] = self.search
events = service.events() # pylint: disable=no-member
result = events.list(**params).execute()
items = result.get('items', [])
self.event = items[0] if len(items) == 1 else None
return True
+14 -14
View File
@@ -76,7 +76,7 @@ def async_get_image(hass, entity_id, timeout=10):
@asyncio.coroutine
def async_setup(hass, config):
"""Setup the camera component."""
"""Set up the camera component."""
component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
hass.http.register_view(CameraImageView(component.entities))
@@ -121,12 +121,12 @@ class Camera(Entity):
@property
def brand(self):
"""Camera brand."""
"""Return the camera brand."""
return None
@property
def model(self):
"""Camera model."""
"""Return the camera model."""
return None
def camera_image(self):
@@ -138,7 +138,7 @@ class Camera(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.loop.run_in_executor(None, self.camera_image)
return self.hass.async_add_job(self.camera_image)
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
@@ -191,7 +191,7 @@ class Camera(Entity):
@property
def state(self):
"""Camera state."""
"""Return the camera state."""
if self.is_recording:
return STATE_RECORDING
elif self.is_streaming:
@@ -201,7 +201,7 @@ class Camera(Entity):
@property
def state_attributes(self):
"""Camera state attributes."""
"""Return the camera state attributes."""
attr = {
'access_token': self.access_tokens[-1],
}
@@ -233,7 +233,7 @@ class CameraView(HomeAssistantView):
@asyncio.coroutine
def get(self, request, entity_id):
"""Start a get request."""
"""Start a GET request."""
camera = self.entities.get(entity_id)
if camera is None:
@@ -241,7 +241,7 @@ class CameraView(HomeAssistantView):
return web.Response(status=status)
authenticated = (request[KEY_AUTHENTICATED] or
request.GET.get('token') in camera.access_tokens)
request.query.get('token') in camera.access_tokens)
if not authenticated:
return web.Response(status=401)
@@ -251,15 +251,15 @@ class CameraView(HomeAssistantView):
@asyncio.coroutine
def handle(self, request, camera):
"""Hanlde the camera request."""
"""Handle the camera request."""
raise NotImplementedError()
class CameraImageView(CameraView):
"""Camera view to serve an image."""
url = "/api/camera_proxy/{entity_id}"
name = "api:camera:image"
url = '/api/camera_proxy/{entity_id}'
name = 'api:camera:image'
@asyncio.coroutine
def handle(self, request, camera):
@@ -269,7 +269,7 @@ class CameraImageView(CameraView):
image = yield from camera.async_camera_image()
if image:
return web.Response(body=image)
return web.Response(body=image, content_type='image/jpeg')
return web.Response(status=500)
@@ -277,8 +277,8 @@ class CameraImageView(CameraView):
class CameraMjpegStream(CameraView):
"""Camera View to serve an MJPEG stream."""
url = "/api/camera_proxy_stream/{entity_id}"
name = "api:camera:stream"
url = '/api/camera_proxy_stream/{entity_id}'
name = 'api:camera:stream'
@asyncio.coroutine
def handle(self, request, camera):
+31 -11
View File
@@ -12,18 +12,22 @@ import voluptuous as vol
import homeassistant.loader as loader
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_web)
async_get_clientsession, async_aiohttp_proxy_web,
async_aiohttp_proxy_stream)
REQUIREMENTS = ['amcrest==1.1.9']
REQUIREMENTS = ['amcrest==1.2.0']
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
CONF_RESOLUTION = 'resolution'
CONF_STREAM_SOURCE = 'stream_source'
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
DEFAULT_NAME = 'Amcrest Camera'
DEFAULT_PORT = 80
@@ -40,7 +44,8 @@ RESOLUTION_LIST = {
STREAM_SOURCE_LIST = {
'mjpeg': 0,
'snapshot': 1
'snapshot': 1,
'rtsp': 2,
}
CONTENT_TYPE_HEADER = 'Content-Type'
@@ -56,6 +61,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE):
vol.All(vol.In(STREAM_SOURCE_LIST)),
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
})
@@ -92,8 +98,9 @@ class AmcrestCam(Camera):
super(AmcrestCam, self).__init__()
self._camera = camera
self._base_url = self._camera.get_base_url()
self._hass = hass
self._name = device_info.get(CONF_NAME)
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
self._resolution = RESOLUTION_LIST[device_info.get(CONF_RESOLUTION)]
self._stream_source = STREAM_SOURCE_LIST[
device_info.get(CONF_STREAM_SOURCE)
@@ -117,15 +124,28 @@ class AmcrestCam(Camera):
yield from super().handle_async_mjpeg_stream(request)
return
# Otherwise, stream an MJPEG image stream directly from the camera
websession = async_get_clientsession(self.hass)
streaming_url = '{0}mjpg/video.cgi?channel=0&subtype={1}'.format(
self._base_url, self._resolution)
elif self._stream_source == STREAM_SOURCE_LIST['mjpeg']:
# stream an MJPEG image stream directly from the camera
websession = async_get_clientsession(self.hass)
streaming_url = self._camera.mjpeg_url(typeno=self._resolution)
stream_coro = websession.get(
streaming_url, auth=self._token, timeout=TIMEOUT)
stream_coro = websession.get(
streaming_url, auth=self._token, timeout=TIMEOUT)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
else:
# streaming via fmpeg
from haffmpeg import CameraMjpeg
streaming_url = self._camera.rtsp_url(typeno=self._resolution)
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
yield from stream.open_camera(
streaming_url, extra_cmd=self._ffmpeg_arguments)
yield from async_aiohttp_proxy_stream(
self.hass, request, stream,
'multipart/x-mixed-replace;boundary=ffserver')
yield from stream.close()
@property
def name(self):
+92
View File
@@ -0,0 +1,92 @@
"""
This component provides basic support for Netgear Arlo IP cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.arlo/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.components.arlo import DEFAULT_BRAND
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream)
DEPENDENCIES = ['arlo', 'ffmpeg']
_LOGGER = logging.getLogger(__name__)
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FFMPEG_ARGUMENTS):
cv.string,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up an Arlo IP Camera."""
arlo = hass.data.get('arlo')
if not arlo:
return False
cameras = []
for camera in arlo.cameras:
cameras.append(ArloCam(hass, camera, config))
async_add_devices(cameras, True)
return True
class ArloCam(Camera):
"""An implementation of a Netgear Arlo IP camera."""
def __init__(self, hass, camera, device_info):
"""Initialize an Arlo camera."""
super().__init__()
self._camera = camera
self._name = self._camera.name
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
def camera_image(self):
"""Return a still image reponse from the camera."""
return self._camera.last_image
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpeg
video = self._camera.last_video
if not video:
return
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
yield from stream.open_camera(
video.video_url, extra_cmd=self._ffmpeg_arguments)
yield from async_aiohttp_proxy_stream(
self.hass, request, stream,
'multipart/x-mixed-replace;boundary=ffserver')
yield from stream.close()
@property
def name(self):
"""Return the name of this camera."""
return self._name
@property
def model(self):
"""Camera model."""
return self._camera.model_id
@property
def brand(self):
"""Camera brand."""
return DEFAULT_BRAND
+38
View File
@@ -0,0 +1,38 @@
"""
Support for Axis camera streaming.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.axis/
"""
import logging
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['axis']
DOMAIN = 'axis'
def _get_image_url(host, mode):
if mode == 'mjpeg':
return 'http://{}/axis-cgi/mjpg/video.cgi'.format(host)
elif mode == 'single':
return 'http://{}/axis-cgi/jpg/image.cgi'.format(host)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis camera."""
device_info = {
CONF_NAME: discovery_info['name'],
CONF_USERNAME: discovery_info['username'],
CONF_PASSWORD: discovery_info['password'],
CONF_MJPEG_URL: _get_image_url(discovery_info['host'], 'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(discovery_info['host'], 'single'),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
add_devices([MjpegCamera(hass, device_info)])
+4 -4
View File
@@ -21,7 +21,7 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup a Blink Camera."""
"""Set up a Blink Camera."""
if discovery_info is None:
return
@@ -49,19 +49,19 @@ class BlinkCamera(Camera):
@property
def name(self):
"""A camera name."""
"""Return the camera name."""
return self._name
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def request_image(self):
"""An image request from Blink servers."""
"""Request a new image from Blink servers."""
_LOGGER.info("Requesting new image from blink servers")
image_url = self.check_for_motion()
header = self.data.cameras[self._name].header
self.response = requests.get(image_url, headers=header, stream=True)
def check_for_motion(self):
"""A method to check if motion has been detected since last update."""
"""Check if motion has been detected since last update."""
self.data.refresh()
notifs = self.data.cameras[self._name].notifications
if notifs > self.notifications:
+3 -3
View File
@@ -16,7 +16,7 @@ DEPENDENCIES = ['bloomsky']
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup access to BloomSky cameras."""
"""Set up access to BloomSky cameras."""
bloomsky = get_component('bloomsky')
for device in bloomsky.BLOOMSKY.devices.values():
add_devices([BloomSkyCamera(bloomsky.BLOOMSKY, device)])
@@ -26,14 +26,14 @@ class BloomSkyCamera(Camera):
"""Representation of the images published from the BloomSky's camera."""
def __init__(self, bs, device):
"""Setup for access to the BloomSky camera images."""
"""Initialize access to the BloomSky camera images."""
super(BloomSkyCamera, self).__init__()
self._name = device['DeviceName']
self._id = device['DeviceID']
self._bloomsky = bs
self._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
# to download the same image over and over.
self._last_image = ""
+3 -3
View File
@@ -11,7 +11,7 @@ from homeassistant.components.camera import Camera
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Demo camera platform."""
"""Set up the Demo camera platform."""
add_devices([
DemoCamera('Demo camera')
])
@@ -29,8 +29,8 @@ class DemoCamera(Camera):
"""Return a faked still image response."""
now = dt_util.utcnow()
image_path = os.path.join(os.path.dirname(__file__),
'demo_{}.jpg'.format(now.second % 4))
image_path = os.path.join(
os.path.dirname(__file__), 'demo_{}.jpg'.format(now.second % 4))
with open(image_path, 'rb') as file:
return file.read()
+2 -2
View File
@@ -16,10 +16,10 @@ from homeassistant.components.ffmpeg import (
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.aiohttp_client import (
async_aiohttp_proxy_stream)
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['ffmpeg']
DEFAULT_NAME = 'FFmpeg'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@@ -31,7 +31,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a FFmpeg Camera."""
"""Set up a FFmpeg camera."""
if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)):
return
async_add_devices([FFmpegCamera(hass, config)])
+2 -2
View File
@@ -32,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup a Foscam IP Camera."""
"""Set up a Foscam IP Camera."""
add_devices([FoscamCamera(config)])
@@ -60,7 +60,7 @@ class FoscamCamera(Camera):
)
self._name = device_info.get(CONF_NAME)
_LOGGER.info('Using the following URL for %s: %s',
_LOGGER.info("Using the following URL for %s: %s",
self._name, uri_template.format('***', '***'))
def camera_image(self):
+8 -8
View File
@@ -43,7 +43,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@asyncio.coroutine
# pylint: disable=unused-argument
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup a generic IP Camera."""
"""Set up a generic IP Camera."""
async_add_devices([GenericCamera(hass, config)])
@@ -85,8 +85,8 @@ class GenericCamera(Camera):
try:
url = self._still_image_url.async_render()
except TemplateError as err:
_LOGGER.error('Error parsing template %s: %s',
self._still_image_url, err)
_LOGGER.error(
"Error parsing template %s: %s", self._still_image_url, err)
return self._last_image
if url == self._last_url and self._limit_refetch:
@@ -100,11 +100,11 @@ class GenericCamera(Camera):
response = requests.get(url, timeout=10, auth=self._auth)
return response.content
except requests.exceptions.RequestException as error:
_LOGGER.error('Error getting camera image: %s', error)
_LOGGER.error("Error getting camera image: %s", error)
return self._last_image
self._last_image = yield from self.hass.loop.run_in_executor(
None, fetch)
self._last_image = yield from self.hass.async_add_job(
fetch)
# async
else:
try:
@@ -114,10 +114,10 @@ class GenericCamera(Camera):
url, auth=self._auth)
self._last_image = yield from response.read()
except asyncio.TimeoutError:
_LOGGER.error('Timeout getting camera image')
_LOGGER.error("Timeout getting camera image")
return self._last_image
except aiohttp.ClientError as err:
_LOGGER.error('Error getting new camera image: %s', err)
_LOGGER.error("Error getting new camera image: %s", err)
return self._last_image
self._last_url = url

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