Compare commits

...

723 Commits
0.49 ... 0.55

Author SHA1 Message Date
Paulus Schoutsen
8a2d7a3e11 Merge pull request #9704 from home-assistant/release-0-55
0.55
2017-10-07 12:50:52 -07:00
Lewis Juggins
de4f610540 [light.tradfri] Clone all of aiocoap to ensure pinned commit will be present (#9713) 2017-10-07 18:28:55 +02:00
Paulus Schoutsen
770f8bd1c3 Fix coap commit (#9712) 2017-10-07 18:28:55 +02:00
Paulus Schoutsen
1ab942e0a2 Deprecate Python 3.4 support (#9684)
* Deprecate Python 3.4 support

* Update text
2017-10-07 18:28:50 +02:00
Paulus Schoutsen
6541e789fb Version bump to 0.55 2017-10-05 09:23:09 -07:00
Paulus Schoutsen
c1b5772f0f Merge remote-tracking branch 'origin/master' into dev 2017-10-05 09:22:49 -07:00
Paulus Schoutsen
6627c352e6 Update frontend 2017-10-05 09:17:26 -07:00
Fabian Affolter
6de403e0ac Support for The Things Network (#9627)
* Support for The Things network's Data Storage

* Rename platform and other changes (async and dict)

* Rename sensor platform and remove check for 200
2017-10-05 09:12:02 -07:00
Paulus Schoutsen
75f902f57e RFC: Create a secrets file and enable HTTP password by default (#9685)
* Create a secret and enable password by default

* Comment out api password secret

* Lint/fix tests
2017-10-05 09:10:29 -07:00
Lewis Juggins
8db4641455 [light.tradfri] async support with resource observation. (#7815)
* [light.tradfri] Initial support for observe

* Update for pytradfri 2.0

* Fix imports

* Fix missing call

* Don't yield from add devices

* Fix imports

* Minor fixes to async code.

* Imports, formatting

* Docker updates, some minor async code changes.

* Lint

* Lint

* Update pytradfri

* Minor updates for release version

* Build fixes

* Retry observation if failed

* Revert

* Additional logging, fix returns

* Fix rename

* Bump version

* Bump version

* Support transitions

* Lint

* Fix transitions

* Update Dockerfile

* Set temp first

* Observation error handling

* Lint

* Lint

* Lint

* Merge upstream changes

* Fix bugs

* Fix bugs

* Fix bugs

* Lint

* Add sensor

* Add sensor

* Move sensor attrs

* Filter devices better

* Lint

* Address comments

* Pin aiocoap

* Fix bug if no devices

* Requirements
2017-10-05 09:05:38 -07:00
Alok Saboo
89042439b8 Fixed typo in opencv (#9697) 2017-10-05 00:04:39 +02:00
Pascal Vizeli
f34ebf733d HassIO replace config changes (#9695)
* Update flow

* fix tests

* Update hassio.py
2017-10-04 18:31:50 +02:00
bestlibre
84271a2dac Refactoring of onewire sensor component (#9691) 2017-10-04 16:35:58 +02:00
Jeroen ter Heerdt
e753c51e34 Updating clicksendaudio component based on feedback (#9692)
* Updating clicksendaudio component based on feedback

* Updating .coveragerc - forgot to add new file clicksendaudio.py
2017-10-04 16:34:37 +02:00
milanvo
65de739489 Fix restore state by filter out null value row from DB query (#9690) 2017-10-04 14:13:58 +02:00
milanvo
3f9d052218 Add recorder purge service, rework purge timer (#9523)
* Add recorder purge service

* Recorder test to match purge config

* Removed purge timer, move service handler to setup, add service description file

* Tests for recorder purge service

* Recorder purge timer rework, add purge service parameter, tests

* Purge service schema change

* Service description change value range

* First cleanup

* Fix name of config
2017-10-04 14:07:42 +02:00
Daniel Høyer Iversen
4314dc251f Add Tibber sensor (#9661)
* Add Tibber sensor

* remove extra space
2017-10-04 10:31:42 +02:00
Johan Bloemberg
e0de521388 Implement DSMR5 support. (#9686)
* Allow configuring DSMR5 protocol.

* Give good example.

* Using dev branch until released upstream.

* Update to dsmr_parser supporting v5 arguments.

* Update to latest dmsr parser, preventing exceptions thrown where warnings would suffice.

* Update even more

* Update requirements.

* Update requirements
2017-10-04 10:20:08 +02:00
Martin Berg
3a282702d9 Fix Google Calendar/oauth2client warning (#9677)
* Fixes oauth2client warning.

* Fix permission.
2017-10-04 10:01:20 +02:00
Jeroen ter Heerdt
7759ae26fd Adding ignore capability to Egardia component (#9676) 2017-10-04 09:59:38 +02:00
Pascal Vizeli
4be91a103d Support new feature to push API data to hassio (#9679)
* Support new featuer to push API data to hassio

* Add tests & services
2017-10-03 22:52:45 -07:00
Alan Fischer
a4b64dec39 Properly handle an invalid end_time (#9675) 2017-10-03 22:51:08 -07:00
BioSehnsucht
3c0d02f057 Rename input_slider to input_number and add numeric text box option (#9494)
* * Rename input_slider to input_number
* Update input_number to optionally display slider, input box, or both

* input_number support either input box or slider mode, but not both

* input_number : change service from select_value to set_value

* input_number : add test for mode setting to tests
2017-10-03 21:34:13 +02:00
FletcherAU
29e973d060 Fix typo in cancel_command description (#9671)
"wasn't going to use it"
2017-10-03 15:24:59 +02:00
Alan Fischer
12b2cfa9b5 Upgrade pyitachip2ir to 0.0.7 (#9669) 2017-10-03 08:17:36 +02:00
Adam Mills
c4810da82f Unit tests to improve core coverage (#9659)
* Code coverage of logging util

* Improve async util coverage

* Add test coverage for restore_state

* get_random_string test
2017-10-02 20:25:04 -07:00
Mister Wil
0aa22d9d91 Bump abode to 0.11.9 (#9660) 2017-10-02 22:55:26 +02:00
Alok Saboo
670bd0ce48 Update google-api-python-client to 1.6.4 (#9658) 2017-10-02 22:42:23 +02:00
Jeroen ter Heerdt
4803721120 Fixed bugs related to exception handling in pythonegardia. Updating package requirement accordingly (#9663) 2017-10-02 22:41:46 +02:00
Matt White
755a2a8291 mqtt_statestream: Add options to publish attributes/timestamps (#9645) 2017-10-02 17:41:07 +02:00
Fabian Affolter
3bd31b91fb Upgrade googlemaps to 2.5.1 (#9653) 2017-10-02 17:17:22 +02:00
Fabian Affolter
25e00556d0 Upgrade influxdb to 4.1.1 (#9652)
* Upgrade influxdb to 4.1.1

* Upgrade influxdb to 4.1.1
2017-10-02 17:17:08 +02:00
Fabian Affolter
8a90ad9e28 Upgrade netdisco to 1.2.2 (#9649) 2017-10-02 17:16:50 +02:00
Fabian Affolter
3f19be9717 Upgrade discord.py to 0.16.12 (#9648) 2017-10-02 17:16:37 +02:00
Fabian Affolter
13fe5857b3 Upgrade youtube_dl to 2017.10.01 (#9647) 2017-10-02 17:16:09 +02:00
Fabian Affolter
5327d2dd1a Upgrade numpy to 1.13.3 (#9646) 2017-10-02 17:15:50 +02:00
Sam Birch
da4048a9ec Add hysteresis attribute to threshold binary sensor (#9596)
* Added hysteresis attribute to threshold binary sensor

* Added threshold binary sensor hysteresis test case

* Changed threshold binary sensor property name to be more self explanatory

* Pulled default hysteresis value into top level declaration

* Fixed linter errors

* Fixed additional linter errors

* Move comment to docs
2017-10-02 17:15:19 +02:00
Vignesh Venkat
b4551cc127 arlo: Add battery level sensor (#9637)
* arlo: Add battery level sensor

Adds a battery level sensor that monitors the battery level on
Arlo cameras.

* Fix lint issue
2017-10-02 12:38:55 +02:00
Michel Weimerskirch
3337107e79 Facebook Messenger notify component: add support for sending messages to specific page user IDs (#9643) 2017-10-02 11:29:31 +02:00
Paulus Schoutsen
f7609e9cb1 Move group services into their own YAML (#9597)
* Move group services into their own YAML

* Fix lint

* Move persistent notification to package
2017-10-01 23:18:10 -07:00
David Byrne
52671842d5 Fixes broken source links in API docs (#9636)
* Fixes broken source links in API docs

* Removes illegal blank line
2017-10-01 23:10:01 -07:00
Gabor SZOLLOSI
fc4a21e491 raspihats: unmet dependency fix (#9638)
* raspihats: update to 2.2.3 (deps fix)

Raspihats platform update, upstream fixed enum34 requirements, added smbus dependency
Fixes #9547

* raspihats: update to 2.2.3, smbus-cffi dependency

Raspihats platform update, upstream fixed enum34 requirements, added smbus dependency
Fixes #9547

* raspihats: update to 2.2.3

* raspihats: update to 2.2.3, smbus-cffi dependency

* raspihats: update to 2.2.3, smbus-cffi dependency

* raspihats: update to 2.2.3 (deps fix)

Raspihats platform update, upstream fixed enum34 requirements, added smbus dependency
Fixes #9547

* raspihats: update to 2.2.3, smbus-cffi dependency
2017-10-01 23:05:24 -07:00
Teemu R
70c8970555 add myself to codeowners (#9642) 2017-10-01 23:04:33 -07:00
Gabor SZOLLOSI
fa32411ab1 wunderground: fix supported language codes #9631 (#9633)
* removed PU, added TR language code (https://www.wunderground.com/weather/api/d/docs?d=language-support&MR=1), fixes #9631
2017-10-01 12:41:21 +02:00
Lukas Barth
29c40622d3 MQTT climate platform [continuation of #8750] (#9589)
* New climate platform with MQTT

* Use STATE_OFF

* Basic tests for climate.mqtt

* lint

* actualy collect coverage

* First tests and fixes

* Add possibility to receive temperature via MQTT

* Require only either sensor or mqtt topic

* Add mqtt publishing for away mode, hold mode and aux heat.

* Use configurabe on/off payloads

* Add pessimistic mode

* Initialize aux and away with False instead of None

* Remove Sensor

* Use correct scheduling method

* Move all methods to coroutines
2017-09-30 16:29:40 +02:00
Phil Kates
80a15977ff splunk: Handle datetime objects in event payload (#9628)
If an event contained a datetime.datetime object it would cause an
exception in the Splunk component. Most of the media_player
components do this in their `media_position_updated_at` attribute.

Use the JSONEncoder from homeassistant.remote instead of just using the
standard json.dumps encoder.

Fixes #9590
2017-09-30 09:35:25 +02:00
Alan Fischer
e406c57ec9 Switched VeraSensor to use category ids (#9624) 2017-09-29 23:34:14 +02:00
Egor Tsinko
9232fa06e4 Fixed away_mode for Ecobee thermostat. (#9559)
* Fixed away_mode for Ecobee thermostat. Now away mode is properly turned on using indefinite away hold.

* fixed lint warnings

* fixed lint warnings

* - now it is possible to use float values for ecobee temperature holds
- fixed a bug that caused an exception when temperature hold was set in away mode
- added unit tests for ecobee thermostat

* fixed lint errors

* fixed lint errors
2017-09-29 16:57:31 +02:00
Jan Almeroth
94370eda54 Yamaha MusicCast: check known_hosts (#9580)
* Yamaha MusicCast: check known_hosts

- pymusiccast: Version bump

* Update requirements
2017-09-29 16:45:25 +02:00
Fabian Affolter
52561d4f7c Move 'voltage' to const (#9621) 2017-09-29 12:05:02 +02:00
Teemu R
9381f187a4 yeelight: allow turn_off transitions, fixes #9602 (#9605) 2017-09-29 12:04:22 +02:00
Joe Lu
445b0f6f94 Rewrite synology camera by using py-synology package (#9583)
* - Rewrite synology camera by intruducing Api and SurveillanceStation classes to get cameras, motion settings, enable/disable motion detection, etc ...
- Synology camera now shows correct state based on is_recording and is_streaming flag. Also it now supports enable / disable motion detection and show the correct motion detection status
- Newly added Api and SurveillanceStation classes will be moved to a lib but it's here just for review

* - Updated how payload are merged with kwargs so it works with python <3.5

* - Fixed class name conflict

* - Addressed flake8 error

* - Addressed pylint error

* - Moved synology API related code to py-synology lib
- Added py-synology==0.1.1 requirement
- Removed hass from SynologyCamera constructor

* - Updated requirements_all.txt

* - renamed variable back to original

* - Sync call to retrieve camera image should be done in camera_image() instead

* - Sync call to update camera info should be done in update() instead

* - Removed unused import
2017-09-29 12:02:48 +02:00
Marcelo Moreira de Mello
19932bce53 Introducing support to Melnor RainCloud sprinkler systems (#9287)
*  Introducing support to Melnor RainCloud sprinkler systems

* Make monitored_conditions optional for sub-components

*  Part 1/2 - Modified attributes, added DATA_ constant and using battery helper

* Part 2/2 - Refactored self-update hub

* Fixed change requested:
- Dispatcher signal connection
- Don't send raincloud object via dispatcher_send()
- Honoring the dynamic scan_interval value on track_time_interval()

* Inherents async_added_to_hass() on all device classes

* Makes lint happy

* * Refactored RainCloud code to incorporate suggestions.
  Many thanks to @pvizelli and @martinhjelmare!!

* Removed Entity from RainCloud sensor and fixed docstrings

* Update raincloud.py

* Update raincloud.py

* fix lint
2017-09-29 10:08:41 +02:00
pascal
cc5256b8fb Cover component for RFlink (#9432)
* second try on rflink / cover

* no newline at end of file

* changed entity

* fixed comments from pvizeli

* removed :

* removed return 'unknown'

* Fixed comments from Rytilahti

* removed newline

* Reverted to None

* cleanup

* Cleanup
2017-09-29 00:49:03 +02:00
Lukas Barth
236d5f8742 Add an input_datetime (#9313)
* Initial proposal for the input_datetime

* Linting

* Further linting, don't define time validation twice

* Make pylint *and* flake8 happy at the same time

* Move todos to the PR to make lint happy

* Actually validate the type of date/time

* First testing

* Linting

* Address code review issues

* Code review: Remove forgotten print()s

* Make set_datetime a coroutine

* Create contains_at_least_one_key_value CV method, use it

* Add timestamp to the attributes

* Test and fix corner case where restore data is bogus

* Add FIXME

* Fix date/time setting

* Fix Validation

* Merge date / time validation, add tests

* Simplify service data validation

* No default for initial state, allow 'unknown' as state

* cleanup

* fix schema
2017-09-28 23:57:49 +02:00
Paulus Schoutsen
2df433eb0a Migrate Alexa smart home to registry (#9616)
* Migrate Alexa smart home to registry

* Fix tests
2017-09-28 21:26:27 +02:00
Dan Chen
44838937d1 Change TP-Link Switch power statistics attribute names (#9607) 2017-09-28 14:12:02 -05:00
Nolan Gilley
8b6a5eef4c upgrade python-ecobee-api (#9612) 2017-09-28 20:38:15 +02:00
Paulus Schoutsen
6fb55b363a Add OwnTracks over HTTP (#9582)
* Add OwnTracks over HTTP

* Fix tests
2017-09-28 09:49:35 +02:00
Pascal Vizeli
7c8e7d6eb0 Cleanup entity & remove warning (#9606)
* Cleanup entity & remove warning

* Update comment
2017-09-27 16:21:39 -07:00
Aaron Bach
eb2338249f FedEx: Adds "packages" as a unit (#9588)
* Adds "packages" as a unit

* Collaborator-requested changes
2017-09-27 10:44:41 -07:00
Aaron Bach
d499c18e63 Fixes UPS MyChoice exception (#9587)
* Fixes UPS MyChoice exception

* Added unit of measurement

* Collaborator-requested changes
2017-09-27 10:44:32 -07:00
William Scanlon
312de6b3a3 New Wink services. pair new device, rename, and delete, add new lock key code. Add water heater support (#9303)
* Pair new device, rename, delete, and lock key code services. Also add water heater support.

* Fixed tox
2017-09-26 23:17:55 -07:00
Pierre Ståhl
9d839f1f53 Bump pyatv to 0.3.5 (#9586) 2017-09-26 21:01:17 +02:00
Fabian Affolter
475f6f5f82 Upgrade Sphinx to 1.6.4 (#9584) 2017-09-26 13:11:10 +02:00
rbflurry
fd9ceb7381 Replace emulated_hue: with emulated_hue_hidden: for consistency. (#9382)
* Update __init__.py

* fix lint errors

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

Lint errors

* use get_deprecated instead to log old attr

* Updated tests to hide fan.ceiling_fan

* remove space fix lint
2017-09-26 00:31:35 -07:00
Anders Melchiorsen
154b070eae IMAP Unread sensor updated for async and push (#9562)
* IMAP Unread sensor updated for async and push

* Implement renames suggested in review

* Use async_timeout

* Keep push capability in a variable

* Reword for Hound
2017-09-26 00:26:26 -07:00
Mike Megally
8a3dcbf10f Allow customizable turn on action for LG WebOS tv (#9206)
* allow customizable action for webos tv turn on as not all models allow for WOL

* trying to fix the houndci-bot

* last few fixes hopefully

* I guess not

* last time!

* This is a breaking change. I have removed the build-in wake-on-lan functionality and have opted for a script which can be a wake-on-lan switch. I have also removed any reference to wol.

* hoping to fix formatting

* linter errors
2017-09-26 00:03:40 -07:00
joe248
bf176c405a Increase Comed timeout since it sometimes takes a long time for the API to respond (#9536)
* Increase Comed timeout since it sometimes takes a long time for the API to respond

* Rewrite ComEd sensor to use asyncio

* Fix whitespace and build issues
2017-09-25 23:43:02 -07:00
Enrique Gonzalez
cf8e6d8d86 Upgrade lyft_rides to 0.2 (#9578) 2017-09-25 22:34:48 +02:00
Fabian Affolter
4a6a53c1ad Upgrade youtube_dl to 2017.9.24 (#9575) 2017-09-25 22:34:05 +02:00
Timo S
896ba7e3fa Added new statistic attributes (#9433)
* Added new statistic attributes

Added new attributes:
  - Cleaning count
  - Total cleaning time
  - Total cleaning area
  - Time left to change main brush, side brush and filter

* Code corrections

Code corrections

* Remove wronge hanging indentation

* Added new attributes

ATTR_MAIN_BRUSH_LEFT
ATTR_SIDE_BRUSH_LEFT
ATTR_FILTER_LEFT
ATTR_CLEANING_COUNT
ATTR_CLEANED_TOTAL_AREA
ATTR_CLEANING_TOTAL_TIME

* Remove trailing white space

* Corrections of the unit test for new attributes

* Hound corrections

* Init self.clean_history, self.consumable_state

* Hound correction

* - Cleaning time and total cleaning time shown in minutes
- Cleaned area and total cleaned area shown in square meters
- Main brush left, side brush left, filter left time shown in hours
- Display of the unit of measurement

* Remove trailing white spaces

* Fixed wrong continued indentation

* Fixed Hound

* Fixed Hound

* Added new statistic attributes

Added new attributes:
  - Cleaning count
  - Total cleaning time
  - Total cleaning area
  - Time left to change main brush, side brush and filter

* Code corrections

Code corrections

* Remove wronge hanging indentation

* Init self.clean_history, self.consumable_state

* Hound correction

* Remove UOM

* Merge

* Init self.clean_history, self.consumable_state

* Hound correction

* Init self.clean_history, self.consumable_state

* Hound correction

* Removed double declarations
2017-09-25 22:27:27 +02:00
Fabian Affolter
fafc4a6042 Upgrade dsmr_parser to 0.11 (#9576) 2017-09-25 22:19:44 +02:00
marthoc
a298b0790b MQTT Cover: Add availability topic and configurable payloads (#9445)
* MQTT Cover - Add availability_topic for online/offline status

Added topic, configurable payloads, and tests.

* Merge branch 'dev' into mqtt-cover-availability

* Revert "Merge branch 'dev' into mqtt-cover-availability"

This reverts commit 46d29794ba959e0394ff5c9904ae039a6df1d22e.

* Added newline at end of test_mqtt.py

* Fixed lint issue (newline at EOF)

* Fixed lint issue (newline at EOF)

* Updated call signature for other tests

* Fixed availability message callback
2017-09-25 19:35:11 +02:00
Paulus Schoutsen
1baf0da627 Clean up OwnTracks (#9569)
* Clean up OwnTracks

* Address comments
2017-09-25 09:05:09 -07:00
Paulus Schoutsen
fc4cd39cdd Add DuckDNS component (#9556)
* Add DuckDNS component

* Address comments
2017-09-24 15:48:45 -07:00
Fabian Affolter
2486c9af35 Use simplepush module, enable event, and allow encrypted communication (#9568)
* Use simplepush module, enable event, and allow encrypted communication

* Fix check
2017-09-24 15:48:30 -07:00
Johan Bloemberg
515d1bdbd3 Add test cases and fix for device_defaults fire_event option. (#9567)
* Add test cases and fix for device_defaults fire_event option.

* Also for light.

* Change docstring mood.
2017-09-24 15:47:59 -07:00
Paul Sokolovsky
ff7db218b1 Update yeelight to 0.3.3. (#9561)
Fixes basic light control in case complex transition effects were defined
on a light (possibly, externally to Home Assistant):
https://gitlab.com/stavros/python-yeelight/issues/17
2017-09-24 13:19:05 -07:00
Paulus Schoutsen
350b8e09e6 Allow specifying multiple ports for UPNP component (#9560)
* Update UPNP component

* Bump dep

* Fix flakiness in test
2017-09-24 13:08:58 -07:00
Aaron Bach
1b91218a60 Updated Arlo cameras with new attributes (#9565) 2017-09-24 21:44:34 +02:00
Aaron Bach
aa0fc339c0 Various AirVisual bugfixes (#9554)
* Various AirVisual bugfixes

* Updating requirements

* Added better logging for failed data retrieval
2017-09-24 21:25:18 +02:00
Joe Lu
bbf6e9ea47 Added support for ARM_NIGHT for manual_mqtt alarm (#9358)
* - Added support for ARM_NIGHT for manual_mqtt alarm

* - port "Add post_pending_state attribute to manual alarm_control_panel #9291" to manuql_mqtt

* - port "Fixed manual alarm not re-arm after 2nd trigger #9249" to manuql_mqtt

* - port "Add manual alarm_control_panel pending time per state #9264" to manuql_mqtt

* - Updated test_trigger_with_specific_pending to simulate real scenario e.g. arm the system then trigger
2017-09-24 08:57:37 +02:00
happyleavesaoc
84524e0712 fix usps? (#9557) 2017-09-24 08:28:11 +02:00
Mister Wil
e2ce1d05ae Fixed bug with all switch devices being excluded (#9555) 2017-09-24 08:22:15 +02:00
Malte Franken
0d75cd484b GeoRSS sensor (#9331)
* new geo rss events sensor

* SCAN_INTERVAL instead of DEFAULT_SCAN_INTERVAL

* removed redefinition CONF_SCAN_INTERVAL

* definition of self._name not required

* removed unnecessary check and unnecessary parameter

* changed log levels

* fixed default name not used

* streamlined sensor name and entity id generation, removed unnecessary parameter

* fixed issue for entries without geometry data

* fixed tests after code changes

* simplified code

* simplified code; removed unnecessary imports

* fixed invalid variable name

* shorter sensor name and in turn entity id

* increasing test coverage for previously untested code

* fixed indentation and variable usage

* simplified test code

* merged two similar tests

* fixed an issue if no data could be fetched from external service; added test case for this case
2017-09-24 08:12:38 +02:00
Andrey
499382a9a9 Add history_graph component (#9472)
* Add support for multi-entity recent fetch of history. Add graph component

* Rename graph to history_graph. Support fast fetch without current state.

* Address comments
2017-09-23 10:01:48 -07:00
Fabian Affolter
f1aef33dd6 Upgrade pyasn1 to 0.3.6 (#9548) 2017-09-23 18:32:29 +02:00
Tom Matheussen
6c0f4c35f6 Catch no longer existing process in systemmonitor (#9535)
* Catch no longer existing process in systemmonitor

* Update log message

* Again line length
2017-09-23 18:31:25 +02:00
Michael Prokop
08b0629eca Fix a bunch of typos (#9545)
s/Addres /Address /
s/Chnage/Change/
s/Converion/Conversion/
s/Supressing/Suppressing/
s/agains /against /
s/allready/already/
s/analagous/analogous/
s/aquired/acquired/
s/arbitray/arbitrary/
s/argment/argument/
s/aroung/around/
s/attibute/attribute/
s/auxillary/auxiliary/
s/befor /before /
s/commmand/command/
s/conatin/contain/
s/conection/connection/
s/coresponding/corresponding/
s/entites/entities/
s/enviroment/environment/
s/everyhing/everything/
s/expected expected/expected/
s/explicity/explicitly/
s/formated/formatted/
s/incomming/incoming/
s/informations/information/
s/inital/initial/
s/inteface/interface/
s/interupt/interrupt/
s/mimick/mimic/
s/mulitple/multiple/
s/multible/multiple/
s/occured/occurred/
s/occuring/occurring/
s/overrided/overridden/
s/overriden/overridden/
s/platfrom/platform/
s/positon/position/
s/progess/progress/
s/recieved/received/
s/reciever/receiver/
s/recieving/receiving/
s/reponse/response/
s/representaion/representation/
s/resgister/register/
s/retrive/retrieve/
s/reuqests/requests/
s/segements/segments/
s/seperated/separated/
s/sheduled/scheduled/
s/succesfully/successfully/
s/suppport/support/
s/targetting/targeting/
s/thats/that's/
s/the the/the/
s/unkown/unknown/
s/verison/version/
s/while loggin out/while logging out/
2017-09-23 17:15:46 +02:00
Pascal Vizeli
3704a18da5 Bugfix Homematic hub object (#9544)
* Bugfix Homematic hub object

* fix hass instance

* fix state unknow if 0 states
2017-09-23 15:53:48 +02:00
Paulus Schoutsen
d1d9704292 Merge pull request #9532 from home-assistant/release-0-54
0.54
2017-09-22 22:23:54 -07:00
Alok Saboo
283cd80a7f Bump python_openzwave to 0.4.0.35 (#9542)
* Bump python_openzwave to 0.4.0.35

* Cleanup
2017-09-22 22:01:57 -07:00
happyleavesaoc
7da8cb225f update usps (#9540)
* update usps

* fix syntax issue
2017-09-22 22:01:56 -07:00
Mister Wil
a3a73b418a Update AbodePy to 0.11.8 (#9537)
* Update requirements_all.txt

* Update abode.py
2017-09-22 22:01:56 -07:00
happyleavesaoc
a8784f9adf update usps (#9540)
* update usps

* fix syntax issue
2017-09-22 21:53:16 -07:00
Alok Saboo
e7c08921eb Bump python_openzwave to 0.4.0.35 (#9542)
* Bump python_openzwave to 0.4.0.35

* Cleanup
2017-09-22 19:00:35 -07:00
Mister Wil
5e35beb41a Update AbodePy to 0.11.8 (#9537)
* Update requirements_all.txt

* Update abode.py
2017-09-22 22:37:16 +02:00
Daniel Høyer Iversen
e980ced0b7 flux led lib 0.20 (#9533) 2017-09-22 10:39:53 +02:00
Paulus Schoutsen
fee922c4be Version bump to 0.55.0.dev0 2017-09-21 21:19:04 -07:00
Paulus Schoutsen
6fa8c2afe5 Version bump to 0.54 2017-09-21 21:18:43 -07:00
Paulus Schoutsen
675fb2010d Update frontend 2017-09-21 21:18:17 -07:00
Paulus Schoutsen
e4c0cec7f1 Merge remote-tracking branch 'origin/master' into dev 2017-09-21 21:14:27 -07:00
Anders Melchiorsen
d978d58436 LIFX: improve performance of setting multi-zone lights to a single color (#9526)
With this optimization we can send a single UDP packet to the light rather
than one packet per zone (up to 80 packets for LIFX Z). This removes a
potential multi-second latency on the frontend color picker.
2017-09-21 23:32:31 +02:00
Julius Mittenzwei
5fd9220812 Fix typo within cover/knx https://github.com/XKNX/xknx/issues/64 (#9527) 2017-09-21 21:55:33 +02:00
marthoc
7cd7b43d25 MQTT Binary Sensor - Add availability_topic for online/offline status (#9507)
* MQTT Binary Sensor - Add availability_topic for online/offline status

Added topic, configurable payloads, and tests.

* Relocated state subscribe function

Moved state subscribe function to follow the state listener function.
2017-09-21 17:02:11 +02:00
Sébastien RAMAGE
c26fb9906f Add reload service to python_script (#9512)
* Add reload service

* add reload test

* Use global variable

* remove white space ....

* adjust as suggested

* remove annoying white space....

* fix travis

* fix travis, again

* rename Load_scripts to Discover_scripts

Travis complains that "Load_scripts" is an invalid name (I don't know why)

* Update python_script.py
2017-09-21 17:00:45 +02:00
Mahasri Kalavala
58cc3a2d7a added services.yaml integration for input_boolean (#9519)
* added services.yaml integration to input_boolean

* added services integration for input_boolean

* removed trailing spaces
2017-09-21 16:58:12 +02:00
Daniel Høyer Iversen
b8a03f1283 update xiaomi aqara lib (#9520) 2017-09-21 08:53:40 +02:00
Vignesh Venkat
2e66898bec abode: Set device_type in state attributes (#9515)
This gets displayed when clicking on the binary sensors. It is
useful to distinguish different devices with the same name (e.g.
the room name) but different types.
2017-09-20 20:51:09 +02:00
Vignesh Venkat
2531d54515 abode: Bump abodepy dependency to 0.11.7 (#9504)
* abode: Bump abodepy dependency to 0.11.7

Fixes cases where one's abode account has a nest thermostat
linked (https://github.com/MisterWil/abodepy/pull/17).

* abode: Bump abodepy dependency to 0.11.7

Fixes cases where one's abode account has a nest thermostat
linked (https://github.com/MisterWil/abodepy/pull/17).

* update requirements_all.txt
2017-09-20 12:18:05 +02:00
Daniel Perna
3aa08f6c91 Bumped pyhomematic, additional device support (#9506)
Add an optional extended description…
2017-09-20 12:17:30 +02:00
Daniel Høyer Iversen
7314ec7a42 Xiaomi pycryptodome (#9511)
* Switch to use pycryptodome for xiaomi_gw
2017-09-20 11:43:25 +02:00
Julius Mittenzwei
a5155a2609 renamed add_devices to async_add_devices according to hass naming scheme (second try after failed #9485) (#9505) 2017-09-20 01:15:20 -04:00
Anders Melchiorsen
3dbf951086 LIFX: fix multi-zone color restore after effects (#9492)
The aiolifx 0.6.0 release fixes an issue where an effect color could
remain set after stopping the effect. This only affected multi-zone
lights (i.e. LIFX Z) and only if the effect was stopped by setting
the light brightness or the color (but not both).

The aiolifx 0.6.0 release also defaults end_index to start_index+7,
so we can remove that argument.

Finally, aiolifx_effects 0.1.2 adds support for aiolifx 0.6.0.
2017-09-19 22:27:00 +02:00
Pascal Vizeli
1bbaa00976 Revert "renamed add_devices to async_add_devices according to hass naming scheme (#9485)" (#9503)
This reverts commit a5a970709f.
2017-09-19 19:51:15 +02:00
Julius Mittenzwei
a5a970709f renamed add_devices to async_add_devices according to hass naming scheme (#9485)
* renamed add_devices to async_add_devices according to hass naming scheme

* replaced some occurencies of async_add_entites to async_add_devices

* fixed unit test

* fixed unit test
2017-09-19 17:06:52 +02:00
Daniel Høyer Iversen
185ada2354 switch to pypi for xiaomi gw (#9498) 2017-09-19 05:36:59 -04:00
Kane610
dcaa5fe443 Solve Recorder component failing when using Axis component (#9293)
* Bump Axis requirement to v10
Fix issues related to non JSON serializable items and recorder component (8297)
Add support to configure HTTP port (8403)

* Changed local port definition to CONF_PORT

* On request config is now sent to the camera platform as well, and in order better explain what is what the old internal config is now device_config and hass own config is the only one referenced as config

* Missed to add device_config to setup in discovered device

* Bump to V12 that has got a dependency fix

* Update requirements_all

* Add port configuration to automatically discovered devices
Allow setup to pass without Axis being configured in configuration.yaml
2017-09-19 10:09:47 +02:00
Fabian Affolter
8ea7e4bb55 Upgrade blockchain to 1.4.0 (#9489) 2017-09-19 10:04:11 +02:00
Fabian Affolter
252ee35d61 Upgrade coinmarketcap to 4.1.1 (#9490) 2017-09-19 10:03:40 +02:00
Mister Wil
e41b00fb4d Bump version of abodepy (#9491) 2017-09-19 06:53:03 +02:00
Fabian Affolter
a05afd58e9 Upgrade async_timeout to 1.4.0 (#9488) 2017-09-18 23:03:02 +02:00
Julius Mittenzwei
0f7c35859b Small improvement of KNX Covers (#9476)
* Refactoring of Cover abstraction. Fixes
https://github.com/XKNX/xknx/issues/57 and
https://github.com/home-assistant/home-assistant/issues/9414

* Requested changes by pvizeli
2017-09-18 21:44:26 +02:00
Colin Dunn
15c3ea0d86 Fix universal media_player mute (#9462) 2017-09-18 15:42:31 -04:00
nilzen
392588e519 Worx Landroid sensor (#9416)
* Worx Landroid sensor

* Move component into sensor folder

* Update .coveragerc

* Remove incorrect file

* Code cosmetics

* Code cosmetics

* Trailing whitespace

* Add docstrings and update module name

* Remove hyphen in component file name

* Fix redefined-builtin and no-self-use

* Update filename in .coveragerc

* Fixed pvizelis requested changes

* Update worxlandroid.py
2017-09-18 17:47:23 +02:00
c-soft
3996c609b4 Added satel_integra alarm panel and binary sensor platform (#9336)
* Added satel_integra alarm panel and binary sensor platform

* Fixed several issues after review: import cleanup, reduced messaging levels to debug, other.

* Fixes after review: removed dead code, improved loop, sorted imports.

* Changes after review, not yet working

* Changes after review - wrapped async code, killed ensure_future, moved async_load_platform into jobs
2017-09-18 17:42:31 +02:00
Mister Wil
c44397e257 Abode services, events, lights, cameras, automations, quick actions. (#9310)
* Updated to latest AbodePy version. Added services and events. Added new device types. Added exclude, light, and polling config options.

* Disable the event service if polling is enabled.

* Addressed all CR's

* Removed duplicated super call.

* Name config option now used. Removed deprecated DEFAULT_NAME.

* Modified partial to move event to first param.
2017-09-18 17:39:41 +02:00
Marcel Holle
5851944f80 Telnet switch (#8913)
* Added telnet switch.

* Lint.

* Coverage

* Added port parameter to Telnet switch.

* Removed optimistic attribute from Telnet switch.

* Code cleanup.
2017-09-18 17:35:35 +02:00
Blender3D
77fb1baeb6 Added support for the DTE Energy Bridge v2 (#9431)
* Added optional 'version' option to switch between sensor versions.

* Reduced line lengths

* Removed error for invalid sensor version
2017-09-18 17:33:58 +02:00
Sebastian Muszynski
94dcf36d7c Xiaomi Gateway: Allow static configuration of a gateway without discovery (#9464)
* Configuration parameter "host" introduced. Will skip the discovery of the host.

* Provide a proper default port. Log message reformatted.

* PyXiaomiGateway version bumped: The new feature was introduced with v0.4.0.

* requirements_all.txt updated.

* Native default for config parameter used.
2017-09-18 17:29:58 +02:00
Fabian Affolter
ced642c862 Upgrade pyasn1 to 0.3.5 and pyasn1-modules to 0.1.4 (#9474) 2017-09-18 07:45:27 +02:00
Michael Prokop
71e06c566f Fix typo in services.yaml (#9475)
s/varaible/variable/
2017-09-18 07:45:07 +02:00
happyleavesaoc
fd97c23cde fitbit fixes (#9460) 2017-09-17 21:13:26 +02:00
milanvo
2219dcaee5 Fix recorder does not vacuum SQLite DB on purge (#9469) 2017-09-17 21:10:53 +02:00
Andy Castille
5f24cc229d DoorBird Component (#9281)
* DoorBird Component

* add newlines at end of files

* fix lint

* fix doorbird components conventions

* fix doorbird domain import and log strings

* don't redundantly add switches

* Remove return statement from setup_platform
2017-09-17 20:47:30 +02:00
Fabian Affolter
811f6b4092 Upgrade youtube_dl to 2017.9.15 (#9456) 2017-09-17 13:41:23 +02:00
Fabian Affolter
6ccf039c95 Upgrade uber_rides to 0.6.0 (#9457) 2017-09-17 13:40:58 +02:00
Fabian Affolter
f2c605ba1b Upgrade sqlalchemy to 1.1.14 (#9458) 2017-09-17 13:40:25 +02:00
Greg Dowling
c54b2c43d4 Merge pull request #9465 from home-assistant/bump_pyvera
Bump pyvera to handle non english language controllers.
2017-09-17 10:58:20 +01:00
Walter Huf
8a3f8457e8 Adds MQTT Fan Discovery (#9463) 2017-09-17 11:32:22 +02:00
rbflurry
bda6d2c696 Ios notify camera fix (#9427)
* Update __init__.py

* Update ios.py

* Update __init__.py
2017-09-17 11:30:17 +02:00
pavoni
840072e92f Bump pyvera - handle non english language controllers. 2017-09-17 10:11:57 +01:00
Paul Krischer
258ad8fc16 Fix issue 5728: Emulated Hue UPnP crashes on special characters. (#9453) 2017-09-16 22:21:09 +02:00
Boyi C
e2866a1339 Load WebComponent polyfill on header. (#9438)
* Load WebComponent polyfill on header.
On Chrome 53, `document.registerElement` exists but `window.customElements` does not exist.
Fix for Tencent X5 browser on Android(Chrome 53 based).

* Move the block just before app panel loading.
Remove async for new script block.
2017-09-16 13:00:54 -07:00
Mike Christianson
308152f48c fix for Twitter notifications without media (#9448) 2017-09-16 12:59:49 -07:00
Pascal Vizeli
c2bbc2f74e Alexa smart home native support (#9443)
* Init commit for alexa component

* more struct component

* Add mapping for device/component to alexa action

* finish discovery

* First version with support on/off/percent

* fix spell

* First init tests

* fix tests & lint

* add tests & fix bugs

* optimaze tests

* more tests

* Finish tests

* fix lint

* Address paulus comments

* fix lint

* Fix lint p2

* Optimaze & paulus comment
2017-09-16 12:35:28 -07:00
Adam Stone
73a15ddd64 Fix emulated hue warning message (#9452) 2017-09-16 11:17:27 -04:00
Aaron Bach
f3fc571cd5 Add city/state/country options and fix bugs for airvisual (#9436)
* Added city/state/country options and fixed several bugs

* Added some slightly better error logging

* Making collaborator-requested changes
2017-09-16 10:32:24 +02:00
Andrey Kupreychik
78bb0da5a0 Added Zyxel Keenetic NDMS2 based routers support for device tracking (#9315)
* Added Zyxel Keenetic NDMS2 based routers support for device tracking

* Review feedback

* Review feedback+

* Review feedback: removed unneeded code
2017-09-16 10:29:24 +02:00
Fabian Affolter
515982a692 Refactor Swiss Public Transport sensor (#9129)
* Refactor Swiss Public Transport sensor

* Minor change
2017-09-15 23:13:30 -07:00
Kyle Hendricks
7b0628421d Fix for DTE Energy Bridge returning the wrong units from time to time (#9246)
The DTE Energy Bridge seems to return the current energy
usage randomly in either W or kW.  The only way to tell the difference
is if there is a decimal or not in the result.

Also added some tests.
2017-09-15 23:12:06 -07:00
Matt White
04bed51277 mqtt_statestream: Update to append 'state' to topic for future use with mqtt discovery (#9446) 2017-09-15 23:05:58 -07:00
Michaël Arnauts
a7bce5f9e6 Allow empty hostnames when detecting devices with the aruba device_tracker. (#9440) 2017-09-15 22:55:53 -07:00
Ted Drain
26c98512c8 Polymer access to log file broken when using new log file command line (#9437)
* Changed api.py to use new log file name

* Only serve log file if logs are active

* Changed log file location to be in hass.data
2017-09-15 22:25:32 -07:00
Pascal Vizeli
5de39fd118 Optimaze vacuum mqtt platform (#9439)
* Optimaze vacuum mqtt platform

* fix lint

* Update mqtt.py
2017-09-15 18:50:22 +02:00
John Boiles
175b4ae5e0 Basic MQTT vacuum support (#9386)
* Basic MQTT vacuum support

* PR feedback

* Support for fan_speed and send_command services

* Fix configurable topics

* Use configurable bools for cleaning/docked/stopped state

* Fix language in docstring

* PR feedback

* Remove duplicate vacuum/state topic defaults

* Fix incorrect template for docked value

* Move direction like default mqtt platfom/components

* fix None on templates

* fix tests

* fix int

* fix tests

* ready to merge
2017-09-15 15:39:19 +02:00
Martin Donlon
0100af0fa6 Fix russound_rio for python 3.4 (#9428)
Bumped russound_rio dependency to 0.1.4 which includes a fix for python
3.4.2 (asyncio.async vs asyncio.ensure_future)
2017-09-15 11:40:40 +02:00
Adam Mills
1c8253f762 Bump version of aioautomatic (#9435) 2017-09-14 20:37:51 -04:00
Daniel Høyer Iversen
4126b8bd13 Rename xiaomi #9425 (#9426)
* rename xiaomi to xiaomi_aqara

* rename xiaomi vacuum and xiaomi phillips light to xiaomi miio

* update discovery and tests

* style

* update discovery and tests

* Still use Philips as name
2017-09-14 18:49:03 -04:00
Joe Lu
9c603d932d Add manual alarm_control_panel pending time per state (#9264)
* - Enhanced manual alarm_control_panel config so that you can specify different pending time for different alarm state

* - Fixed demo alaram control panel

* - Updated configuration structure for state specific pending times

* - Addressed comment

* Address code review comments

* - Fixed failing tests
- Updated demo alarm component to use new per state pending_time setting

* - Removing previously added comment which might have caused build to fail?

* - moved "copy.deepcopy(config)" out of loop so config is only copied once
2017-09-14 20:08:45 +02:00
Sébastien RAMAGE
20f3e3dcf9 Improve Python script (#9417)
* add datetime and support for unpacksequence

add datetime to builtin and support for unpacksequence
a,b = (1,2)
for a,b in [(1,2),(3,4)]

* add test for python_script

* fix test

* restore previous test

restore previous tests, removed by mistake sorry...

* fix test

* Update test_python_script.py

* fix travis

* fix test

* Update test_python_script.py

* Add files via upload

* fix travis...
2017-09-14 07:52:47 -07:00
rollbrettler
371d1cc872 Fix displaying of friendly_name for light template component (#9413) 2017-09-14 10:13:01 +02:00
giangvo
3430c1c8bc update broadlink.py to add support for MP1 switch (#9222)
* update broadlink.py to add support for MP1 switch

* fix code styles

* fix code styles

* optimize state fetching on mp1

* fix code styles

* fix code styles

* fix code styles

* fix code styles

* fix variable

* remove default None

* use string.format
2017-09-13 23:41:52 -07:00
pdanilew
f5dee2c27d MPD small improvements (#9301)
* Power button restored.

* Added volume step and mute

* Removed network operations from property + pylint made happy.
2017-09-13 23:38:07 -07:00
Sebastian Muszynski
5db55b306e Bump python-mirobo for improved device support and introduce API changes. (#9424) 2017-09-13 23:18:22 -07:00
Pierre Ståhl
ba5e8d133d Fix artwork bug in Apple TV (#9415)
* Fix artwork bug in Apple TV

* Clean up some None checks
2017-09-13 22:30:29 -07:00
Jay Stevens
c94b3a7bf9 Add support for Todoist platform (#9236)
* Added basic Todoist support

Creating a new platform for Todoist - https://todoist.com

* Added more robust support for creating new custom projects.

This means you can now specify things such as 'all tasks due today', 'all tasks due this week', etc.

* Changed logging from warning to info.

* Added label and comment support.

* Added support for overdue tasks.

* Changed logging to info instead of warning; fixed labels.

* Added ability to filter projects by name.

* Rename 'extra_projects' to 'custom_projects'.

* Updated code to follow proper HASS style guidelines.

* Got new_task service running.

* Update .coveragerc.

* Remove old try-catch block.

This is left over from before we validated the inputs using the service schema.

* Updated to use PLATFORM_SCHEMA.

* Updated component to use Todoist API.

* Removed commented-out code.

This also removes functionality regarding finding out how many comments a task has.
This functionality may be added back in the future.

* Clarified TodoistProjectData, removed fetching comments.

* Fixed bug where projects were grabbing all tasks.

* Fixed bug where due dates were being ignored.

* Removed debug logging.

* Fixed linter errors.

* Fixed Todoist docstring to be in line with HASS' style rules.

* Organized imports.

* Fixed voluptuous schema.

* Moved ID lookups into .

* Moved ID lookups into setup_platform.

* Cleaned up setup_platform a bit.

* Cleaned up Todoist service calls.

* Changed debug logging level.

* Fixed issue with configuration not validating.

* Changed from storing the token to storing an API instance.

* Use dict instead of Project object.

* Updated to use list comprehension where possible.

* Fixed linter errors.

* Use constants instead of literals.

* Changed logging to use old-style string formatting.

* Removed unneeded caching.

* Added comments explaining 'magic' strings.

* Fixed bug where labels were always on the whitelist.

* Fixed linter error.

* Stopped checking whitelist length explicitly.
2017-09-14 07:27:12 +02:00
spektren
28d312803b full RGB support for users of tradfri GW (#9411)
* Update tradfri.py

## 201709013: set_hex_color() seems not to work in pytradfri api - set_rgb_color() does 
## -> changed function set_hex_color() to set_rgb_color() 
## tested w. IKEA tradfri GW and zigbee rgb PWM module (dresden elektronik FLS-PP lp)

* Update tradfri.py

Setup: 
Home Assistant 0.53.0
pytradfri 2.2 
IKEA tradfri gateway fw 1.1.0015
zigbee rgb PWM module (dresden elektronik FLS-PP lp) 

Issue: 
pytradfri's set_hex_color() does not work for arbitrary colors with the current IKEA tradfri gateway. Only setting rgb hex values (param 5706) of some predefined colors has the desired effect. Others will fall back to one predefined value. I assume, the GW doesn't allow for values deviating from the predefined values. 

However, pytradfri's set_rgb_color() does also work for arbitrary colors. Latest pytradfri (2.2/PR51?) will convert rgb to xy and send xy thru the GW (param 5709 and 5710). 

 -> changed the function used from set_hex_color() to set_rgb_color() in HA's component\light\tradfri

Result: 
Full RGB support with arbitrary colors with my setup. 

Unfortunately I cannot test tradfri GW with other bulbs (no have hue/lightify bulbs). 
___ 

Predefined colors from <https://github.com/ggravlingen/pytradfri/pull/51>: 
 this.f3891b = new HashMap();
        this.f3891b.put("f5faf6", new C1386c(0.3804d, 0.3804d, "f5faf6", 0.54d));
        this.f3891b.put("f1e0b5", new C1386c(0.4599d, 0.4106d, "CCT_LIGHT_NEUTRAL", 0.61d));
        this.f3891b.put("efd275", new C1386c(0.5056d, 0.4152d, "efd275", 0.66d));
        this.f3891b.put("dcf0f8", new C1386c(0.3221d, 0.3317d, "dcf0f8", 0.45d));
        this.f3891b.put("eaf6fb", new C1386c(0.3451d, 0.3451d, "eaf6fb", 0.48d));
        this.f3891b.put("f5faf6", new C1386c(0.3804d, 0.3804d, "f5faf6", 0.54d));
        this.f3891b.put("f2eccf", new C1386c(0.4369d, 0.4041d, "f2eccf", 0.59d));
        this.f3891b.put("CCT_LIGHT_NEUTRAL", new C1386c(0.4599d, 0.4106d, "CCT_LIGHT_NEUTRAL", 0.61d));
        this.f3891b.put("efd275", new C1386c(0.5056d, 0.4152d, "efd275", 0.66d));
        this.f3891b.put("ebb63e", new C1386c(0.5516d, 0.4075d, "ebb63e", 0.68d));
        this.f3891b.put("e78834", new C1386c(0.58d, 0.38d, "e78834", 0.69d));
        this.f3891b.put("e57345", new C1386c(0.58d, 0.35d, "e57345", 0.67d));
        this.f3891b.put("da5d41", new C1386c(0.62d, 0.34d, "da5d41", 0.7d));
        this.f3891b.put("dc4b31", new C1386c(0.66d, 0.32d, "dc4b31", 0.73d));
        this.f3891b.put("e491af", new C1386c(0.5d, 0.28d, "e491af", 0.57d));
        this.f3891b.put("e8bedd", new C1386c(0.45d, 0.28d, "e8bedd", 0.53d));
        this.f3891b.put("d9337c", new C1386c(0.5d, 0.24d, "d9337c", 0.55d));
        this.f3891b.put("c984bb", new C1386c(0.34d, 0.19d, "c984bb", 0.38d));
        this.f3891b.put("8f2686", new C1386c(0.31d, 0.12d, "8f2686", 0.33d));
        this.f3891b.put("4a418a", new C1386c(0.17d, 0.05d, "4a418a", 0.18d));
        this.f3891b.put("6c83ba", new C1386c(0.2d, 0.1d, "6c83ba", 0.22d));
        this.f3891b.put("a9d62b", new C1386c(0.4099999964237213d, 0.5099999904632568d, "a9d62b", 0.654d));
        this.f3891b.put("d6e44b", new C1386c(0.44999998807907104d, 0.4699999988079071d, "d6e44b", 0.65d));
2017-09-13 22:24:46 -07:00
Antony Messerli
07cb7b3d54 Bump uvcclient to 0.10.1 to work with beta NVR releases (#9423) 2017-09-13 22:21:58 -07:00
Tor Magnus
5b453ca53a Added more devices and types to onewire (#9404)
* Added more devices and sensor types.

* flake8 fixes

* Resolved feedback in pull https://github.com/home-assistant/home-assistant/pull/9404

* Fixed issue where values would get mixed up across restarts of HA
2017-09-13 22:14:38 -07:00
morberg
b21bfe50d7 Add LC_CTYPE to environment variables in macOS (#9227)
* Add LANG to environment variables

Some componentes, e.g. tradfri, will not work properly unless LANG is an UTF-8 environment.

* Set LC_CTYPE to UTF-8
2017-09-13 21:35:25 -07:00
Ted Drain
411c9620c1 Added log-file command line flag (#9422) 2017-09-13 21:22:42 -07:00
Lukas Barth
89d6784fa0 Fix copy&paste mistake (#9378) 2017-09-13 17:00:46 +02:00
Paul Sokolovsky
d90801f6dd components/xiaomi: Add initial discovery using NetDisco. (#9283)
There's a kind of duplication of functionality between NetDisco and
"xiaomi" component, the latter features its own "discovery" in addition
to general HomeAssistant discovery service, based on NetDisco. As such,
this patch is pretty simple: the only purpose of NetDisco discovery
is "plug and play", "zero configuration" discovery that Xiaomi Gateway
device is present on the local network, and triggering of "xiaomi"
component loading, which then "rediscovers" the gateway using its own
method.
2017-09-12 20:44:42 -07:00
Paulus Schoutsen
2c8967d0d5 Update netdisco to 1.2.0 (#9408) 2017-09-12 20:43:35 -07:00
Pierre Ståhl
f5ffef3f72 Support specifying no Apple TVs (#9394) 2017-09-12 19:57:31 -07:00
happyleavesaoc
c8da95c1e8 fix mopar sensor (#9389)
* fix mopar sensor

* fix typo

* bump mopar dep version
2017-09-12 19:50:28 -07:00
Fabian Affolter
fdf2d24a8b Upgrade psutil to 5.3.1 (#9403) 2017-09-13 00:54:25 +02:00
Paulus Schoutsen
05192e678e Break up Alexa per functionality (#9400)
* Break up Alexa per functionality

* Lint

* Lint
2017-09-12 21:24:44 +02:00
Jeff McGehee
29b62f814f Allow multiple observations of same entity (#9391)
* Allow multiple observations of same entity

Why:

* There may be different probabilities for multiple states of the same
entity.

This change addresses the need by:

* Keeping a list of observations for each entity to check on each state
change of the given entity.
* Adding a numeric id to each observation so that they can be
effectively added and removed from `self.current_obs`.
* Adding a test to confirm functionality.

* fix overzealous indenting
2017-09-12 18:52:09 +02:00
Paulus Schoutsen
c9fc3fae6e Update cloud auth (#9357)
* Update cloud logic

* Lint

* Update test requirements

* Address commments, fix tests

* Add credentials
2017-09-12 18:47:04 +02:00
Pascal Vizeli
90f9a6bc0a Cleanup and simplitfy the async state update (#9390)
* Cleanup and simplitfy the async state update

* Update test_entity.py
2017-09-12 10:01:03 +02:00
Paulus Schoutsen
1afdde61e8 Merge pull request #9395 from home-assistant/release-0-53-1
0.53.1
2017-09-11 22:43:56 -07:00
viswa-swami
2a8620f806 Fixing foscam library dependency/requirements (#9387)
* Added support to enable/disable motion detection for foscam cameras. This support was added in 0.48.1 as a generic service for cameras. Motion detection can be enabled/disabled for foscam cameras with this code-set.

* Fixed the violation identified by hound-bot

* Fixed the comment posted by HoundCI-Bot regarding using imperative mood statement for pydocstyle

* Fixed the error that travis-ci bot found.

* As per comment from @balloob, Instead of directly using the URL to talk to foscam, used a 3rd party foscam library to communicate with it. This library already has support to enable/disable motion detection and also APIs to change the motion detection schedule etc. Need to add more support in the pyfoscam 3rd party library for checking if motion was detected or even if sound was detected. Once that is done, we can add that into HASS as well.

* Lint

* Removed the requests library import which is not used anymore

* Updating requirements_all.txt based on the code-base of home assistant that i have. Generated using the gen_requirements_all.py script

* Updating requirements_all.txt and requirements_test_all.txt generated by gen_requirements_all.py after latest pull from origin/dev

* Updated requirements_all.txt with script

* Updated the foscam camera code to fix lint errors

* Fixed houndci violation

* Updating the foscam library dependency/requirements.

* Fixing the requirements_all file. Somehow when i generated, it generated duplicate entry for the same dependency
2017-09-11 21:53:20 -07:00
Alok Saboo
804d06d0d3 Fixes #9379 - Added additional string check in Wunderground sensor (#9380)
* Added additional string check

* optimaze
2017-09-11 21:53:20 -07:00
Mike Christianson
202d4d8105 Fixes #9353 (#9354)
Follow [Twitter's guidance](https://dev.twitter.com/rest/reference/post/media/upload-finalize) for media uploads: "If and (only if) the response of the FINALIZE command contains a processing_info field, it may also be necessary to use a STATUS command and wait for it to return success before proceeding to Tweet creation."
2017-09-11 21:53:19 -07:00
Paulus Schoutsen
04dccb4246 Version bump to 0.53.1 2017-09-11 21:53:01 -07:00
Paulus Schoutsen
7f5c4cd1e5 Update frontend 2017-09-11 21:52:48 -07:00
Paulus Schoutsen
c84a099b0f Update frontend 2017-09-11 21:50:33 -07:00
viswa-swami
659dc2e557 Fixing foscam library dependency/requirements (#9387)
* Added support to enable/disable motion detection for foscam cameras. This support was added in 0.48.1 as a generic service for cameras. Motion detection can be enabled/disabled for foscam cameras with this code-set.

* Fixed the violation identified by hound-bot

* Fixed the comment posted by HoundCI-Bot regarding using imperative mood statement for pydocstyle

* Fixed the error that travis-ci bot found.

* As per comment from @balloob, Instead of directly using the URL to talk to foscam, used a 3rd party foscam library to communicate with it. This library already has support to enable/disable motion detection and also APIs to change the motion detection schedule etc. Need to add more support in the pyfoscam 3rd party library for checking if motion was detected or even if sound was detected. Once that is done, we can add that into HASS as well.

* Lint

* Removed the requests library import which is not used anymore

* Updating requirements_all.txt based on the code-base of home assistant that i have. Generated using the gen_requirements_all.py script

* Updating requirements_all.txt and requirements_test_all.txt generated by gen_requirements_all.py after latest pull from origin/dev

* Updated requirements_all.txt with script

* Updated the foscam camera code to fix lint errors

* Fixed houndci violation

* Updating the foscam library dependency/requirements.

* Fixing the requirements_all file. Somehow when i generated, it generated duplicate entry for the same dependency
2017-09-11 21:43:55 -07:00
Mike Christianson
10c0744c4a Fixes #9353 (#9354)
Follow [Twitter's guidance](https://dev.twitter.com/rest/reference/post/media/upload-finalize) for media uploads: "If and (only if) the response of the FINALIZE command contains a processing_info field, it may also be necessary to use a STATUS command and wait for it to return success before proceeding to Tweet creation."
2017-09-11 21:37:36 -07:00
felix schwenzel
51ff6009a3 typo in waypoint import topic preventing waypoint import (#9338)
owntracks (tested on ios version 9.6.3/de_DE) publishes single waypoints under the topic owntracks/<user>/<device>/waypoint (singular).
owntrack publishes a waypoint export (publish) under the topic owntracks/<user>/<device>/waypoints (plural).

the owntracks component did not catch my waypoint export to mqtt, only single waypoint updates (i.e. after editing a waypoint or creating a new one). these single waypoints were rejected „because of missing or malformatted data“. when i changed the WAYPOINT_TOPIC to 'owntracks/{}/{}/waypoints', owntracks imported my published waypoint list, after i triggered it under Setting / Publish Waypoints.
2017-09-11 23:20:09 +02:00
Alok Saboo
6d01838632 Fixes #9379 - Added additional string check in Wunderground sensor (#9380)
* Added additional string check

* optimaze
2017-09-11 22:57:40 +02:00
Matt White
31f189da82 Added mqtt_statestream component (#9286)
* Added mqtt_statestream component

* Added tests for mqtt_statestream component

* mqtt_statestream: add test for valid new_state

* mqtt_statestream: Don't set initialized state

* mqtt_statestream: Switch to using async_track_state_change

* Cleanup
2017-09-11 22:08:12 +02:00
Alok Saboo
c7ecebfd07 Round off probability to 2 decimals. (#9365)
* Round off probablity to 2 decimals.

* Update tests

* remove debug print
2017-09-11 21:14:51 +02:00
John Arild Berentsen
cc1979691e Add polling interval service and setting available through zwave node entity panel (#9056)
* Add polling interval to value panel

* Blank lines removal

* Update tests

* Remove old config method

* Raound 1

* Round 2

* Comment spacing

* Expose value_id in attributes
2017-09-11 20:30:48 +02:00
morberg
e2fc9669f0 Add /usr/sbin to PATH (#9364)
The `braviatv`platform needs the `arp` command to finalize configuration. This resides in `/usr/sbin`, at least on macOS 10.10.
2017-09-11 09:31:05 +02:00
David
7307ab878a Bump pywebpush and pyJWT versions (#9355)
* Update html5.py

Bump pywebpush and PyJWT versions

* Update requirements_all.txt

* Update requirements_test_all.txt
2017-09-10 22:25:46 +02:00
Anders Melchiorsen
160c7fc685 Add HTTP Basic auth to RESTful Switch (#9162)
* Add HTTP Basic auth to RESTful Switch

* Remove redundant hass passing

* Initialize to current state

The state used to be None until the first periodic poll.

This commit refactors async_update so it can be used during setup as well,
allowing the state to start out with the correct value.

* Refactor turn_on/turn_off device communication

* Remove lint

* Fix Travis errors
2017-09-09 10:20:48 -07:00
Paulus Schoutsen
313a9e3984 Merge branch 'master' into dev 2017-09-09 00:53:00 -07:00
Paulus Schoutsen
ba310d3bd1 Version bump to 0.54.0.dev0 2017-09-09 00:52:47 -07:00
Paulus Schoutsen
3f2eba0932 Version bump to 0.53 2017-09-09 00:51:52 -07:00
Paulus Schoutsen
2d72cff575 Merge pull request #9327 from home-assistant/release-0-53
0.53
2017-09-09 00:31:53 -07:00
John Mihalic
3065575777 Bump pyHik version to add IO support (#9341) 2017-09-09 00:06:57 -07:00
Paulus Schoutsen
74bfcde814 Cleanup input_text (#9326) 2017-09-09 00:06:57 -07:00
Aaron Bach
c539b5c12b Adds the AirVisual air quality sensor platform (#9320)
* Adds the AirVisual air quality sensor platform

* Updated .coveragerc

* Removed some un-needed code

* Adding strangely-necessary pylint disable

* Removing a Python3.5-specific dict combiner method

* Restarting stuck coverage test

* Added units to AQI sensor (to get nice graph)

* Making collaborator-requested changes

* Removing unnecessary parameter from data object
2017-09-09 00:06:56 -07:00
Sergey Isachenko
d2d876945b Fix for potential issue with tesla initialization (#9307)
Fix for potential issue with tesla initialization
2017-09-09 00:06:56 -07:00
John Mihalic
2defb85fb2 Bump pyHik version to add IO support (#9341) 2017-09-09 00:06:06 -07:00
Paulus Schoutsen
7036a7845c Update frontend 2017-09-08 23:08:58 -07:00
Paulus Schoutsen
c44972c2c9 Update frontend 2017-09-08 23:08:38 -07:00
Paulus Schoutsen
fc7ffba9ae Merge branch 'master' into release-0-53 2017-09-08 21:52:29 -07:00
Paulus Schoutsen
5ec5552803 Cleanup input_text (#9326) 2017-09-08 21:19:49 -07:00
Aaron Bach
d1ef47384d Adds the AirVisual air quality sensor platform (#9320)
* Adds the AirVisual air quality sensor platform

* Updated .coveragerc

* Removed some un-needed code

* Adding strangely-necessary pylint disable

* Removing a Python3.5-specific dict combiner method

* Restarting stuck coverage test

* Added units to AQI sensor (to get nice graph)

* Making collaborator-requested changes

* Removing unnecessary parameter from data object
2017-09-08 16:05:51 +02:00
Sergey Isachenko
3b2bf1d567 Fix for potential issue with tesla initialization (#9307)
Fix for potential issue with tesla initialization
2017-09-07 18:20:27 +02:00
Julius Mittenzwei
77d0ad1797 Stable and asynchronous KNX library. (#8725)
* First draft of XKNX module for Home-Assistant

* XKNX does now take path of xknx.yaml as parameter

* small fix, telegram_received_callback has different signature

* changed method of registering callbacks of devices

* removed non async command lines from xknx

* telegram_received_cb not needed within HASS module

* updated requirements

* Configuration if XKNX should connect via Routing or Tunneling

* bumping version to 0.6.1

* small fix within xknx plugin

* bumped version

* XKNX-Switches are now BinarySensors and Logic from Sensor was moved to BinarySensor

* renamed Outlet to Switch

* pylint

* configuration of KNX lights via HASS config, yay!

* changed name of attribute

* Added configuration for xknx to switch component

* added support for sensors within hass configuration

* added support for climate within hass configuration

* Thermostat -> Climate

* added configuration support for binary_sensors

* renamed Shutter to Cover

* added configuration support for cover

* restructured file structure according to HASS requirements

* pylint

* pylint

* pylint

* pylint

* pylint

* pylint

* updated version

* pylint

* pylint

* pylint

* added setpoint support for climate devices

* devices are now in a different module

* more asyncio :-)

* pydocstyle

* pydocstyle

* added actions to binary_sensor

* allow more than one automation

* readded requirement

* Modifications suggested by hound

* Modifications suggested by hound

* Modifications suggested by hound

* Modifications suggested by hound

* xknx now imported as local import

* hound *sigh*

* lint

* 'fixed' coverage.

* next try for getting gen_requirements_all.py working

* removed blank line

* XKNX 0.7.1 with logging functionality, replaced some print() calls with _LOGGER

* updated requirements_all.txt

* Fixes issue https://github.com/XKNX/xknx/issues/51

* https://github.com/XKNX/xknx/issues/52 added raw access to KNX bus from HASS component.

* bumped version - 0.7.3 contains some bugfixes

* bumped version - 0.7.3 contains some bugfixes

* setting setpoint within climate device has to be async

* bumped version to 0.7.4

* bumped version

* https://github.com/XKNX/xknx/issues/48 Adding HVAC support.

* pylint suggestions

* Made target temperature and set point required attributes

* renamed value_type to type within sensor configuration

* Issue https://github.com/XKNX/xknx/issues/52 : added filter functionality for not flooding the event bus.

* suggestions by pylint

* Added notify support for knx platform.

* logging error if discovery_info is None.

* review suggestions by @armills

* line too long

* Using discovery_info to notifiy component which devices should be added.

* moved XKNX automation to main level.

* renamed xknx component to knx.

* reverted change within .coveragerc

* changed dependency

* updated docstrings.

* updated version of xknx within requirements_all.txt

* moved requirement to correct position

* renamed configuration attribute

* added @callback-decorator and async_prefix.

* added @callback decorator and async_ prefix to register_callbacks functions

* fixed typo

* pylint suggestions

* added angle position and invert_position and invert_angle to cover.knx

* typo

* bumped version within requirements_all.txt

* bumped version

* Added support for HVAC controller status
2017-09-07 00:11:55 -07:00
Sebastian Muszynski
9a7089bad3 Platform not ready behavior fixed. (#9325)
Expose the device model as sensor attribute.
Device initialized log message added. Provides device model, firmware and hardware version.
2017-09-07 00:01:59 -07:00
Mister Wil
894200d87d Fixed bug with devices not being discovered correctly. (#9311) 2017-09-06 09:11:32 -07:00
Alok Saboo
fad914de8c Version bump dlib to 1.0.0 (#9316) 2017-09-06 07:35:34 -07:00
ohmer1
5971a7c009 Optionally disable ssl certificate validity check. (#9181)
* Optionally disable ssl certificate validity check.

* Fix lines too long.

* Fix formatting.

* Force build CI

* Fix "Method could be a function (no-self-use)"
2017-09-06 08:58:13 +03:00
Mike Christianson
e7a5f7bcdf Follow Twitter guidelines for media upload by conforming to the "STATUS" phase, when required, and by providing "media_category" information. These will, for example, allow users to upload videos that exceed the basic 30 second limit. (#9261)
See:
 - https://twittercommunity.com/t/media-category-values/64781/7
 - https://twittercommunity.com/t/duration-too-long-maximim-30000/68760
 - https://dev.twitter.com/rest/reference/get/media/upload-status.html
2017-09-05 18:49:40 -07:00
Konstantin Belyalov
9ade8002ac Add new config variable to MQTT light (#9304)
* Add new config variable to MQTT light

* Address reviewer's issues: refactor template render part.

* Update mqtt.py
2017-09-06 01:01:03 +02:00
Joe Lu
788275da32 Add post_pending_state attribute to manual alarm_control_panel (#9291)
Add post_pending_state attribute to manual alarm_control_panel
2017-09-05 20:26:59 +02:00
Erik Eriksson
418ccc820a Handle the case where no registration number is available (instead display VIN (vehicle identification number)). (#9073) 2017-09-05 09:10:01 -07:00
Jan Almeroth
e4bb8b0444 Introducing a media_player component for Yamaha Multicast devices (#9258)
* Introducing media_player yamaha_multicast

* Fix pep8_max_line_length

* Revert "Fix pep8_max_line_length"

This reverts commit 664c25d6571e2f49f635aea332a848655f220c36.

* Revert "Introducing media_player yamaha_multicast"

This reverts commit a4fb64b53a79f68966d4af80fe9304d357bcd832.

* Introducing media_player for Yamaha MultiCast Devices

* Add missing Docstrings

* Adding Requirements

* Add Geofency device tracker (#9106)

* Added Geofency device tracker

Added Geofency device tracker

* fix pylint error

* review fixes

* merge coroutines

* Version bump

* Version bump

* D210: No whitespaces allowed surrounding docstring text

* Fix linting

* Version bump

* Revert "Add Geofency device tracker (#9106)"

This reverts commit c240d907d2f1fadecf831b3d5bb4e026ce3f892d.

* Fix Invalid method names

* Fix update_status timer

* Fix Invalid class name "mcDevice"

* Fix Access to a protected members

* Introducing source_list setter

* Fix logging

* Version bump

* D400: First line should end with a period (not 'e')

* Removed unnecessary logging

* Minor changes

Thanks to comments from @andrey-git
2017-09-05 19:07:58 +03:00
BioSehnsucht
552abf7da5 Add input_text component (#9112) 2017-09-05 09:04:07 -07:00
runningman84
9ede0f57e6 Added DWD WarnApp Sensor (#8657)
* Added DWD WarnApp Sensor

* Fixed some idents and spaces

* Removed unused imports

* Removed comment

* Some fixes

* Added throttle

* Renamed sensor to dwd weather warnings

* Renamed test file

* shorten lines

* shorten lines

* Implemented changes requested by fabaff

* added ATTRIBUTION

* move ATTRIBUTION to existing method

* fixed lint tests

* Fix linter issues

* Fix linter issues

* Fix linter

* Fixed linter
2017-09-05 08:40:47 -07:00
Phil Cole
0b1677de6d Expose hue group 0 (#8663)
* Tado Fix #8606

Handle case where 'mode' and 'fanSpeed' are missing JSON. Based on
changes in commit
adfb608f86

* Expose hue group 0 to HA #8652

If allow_hue_groups is set expose "All Hue Lights" group for "special
group 0".  This does add an additional Hue API call for every refresh
(approx 30 secs) to get the status of the special group 0 because it's
not included in the full API pull that currently occurs.

* Revert "Expose hue group 0 to HA #8652"

This reverts commit db7fe47ec72a4907f8a59ebfb47bc4a6dfa41e89.

* Expose hue group 0 to HA #8652

If allow_hue_groups is set expose "All Hue Lights" group for "special
group 0".  This does add an additional Hue API call for every refresh
(approx 30 secs) to get the status of the special group 0 because it's
not included in the full API pull that currently occurs.

* Changes per review by balloob

1) Use all_lights instead of all_lamps
2) Fix line lengths and trailing whitespace
3) Move "All Hue Lights" to GROUP_NAME_ALL_HUE_LIGHTS constant

* Make "All Hue Lights" a constant

* Fix trailing whitespace
2017-09-05 08:38:12 -07:00
Sean Gollschewsky
968ed6ef5b Ensure display-name does not exceed 12 characters for CecAdapter. (#9268)
* Ensure display-name does not exceed 12 characters for CecAdapter.

* Miscalculated offset.
2017-09-05 18:11:02 +03:00
Pascal Vizeli
a28ac37a91 Update jinja to 2.9.6 (#9306)
* Update jinja 2.10

* Update requirements_all.txt

* Update package_constraints.txt

* Update package_constraints.txt

* Update requirements_all.txt

* Update setup.py
2017-09-05 17:03:24 +02:00
Dan Sarginson
5ba39c849e Fix for Honeywell Round thermostats (#9308)
This fixes an issue (#8554) whereby the Honeywell thermostats stopped
working after a period of hours or days. We do this by forgetting the
authorisation token that was sent back to us when we first logged in,
which causes the underlying evohomeclient library to perform the full
login procedure again.
2017-09-05 07:06:28 -04:00
Brian Hopkins
984cae5310 Upgrade mycroftapi to 2.0 (#9309)
* updating mycroftapi version

* updating mycroftapi version
2017-09-05 07:05:31 -04:00
upsert
c3a91000ac Improved Lutron Caseta shade support (#9302) 2017-09-05 11:30:36 +02:00
Pascal Vizeli
ed699896cb Core track same state for a period / Allow on platforms (#9273)
* Core track state period / Allow on platforms

* Add tests

* fix lint

* fix tests

* add new tracker to automation state

* update schema

* fix bug

* revert validate string

* Fix bug

* Set arguments to async_check_funct

* add logic into numeric_state

* fix numeric_state

* Add tests

* fix retrigger state

* cleanup

* Add delay function to template binary_sensor

* Fix tests & lint

* add more tests

* fix lint

* Address comments

* fix test & lint
2017-09-05 02:01:01 +02:00
Tom Matheussen
67828cb7a2 Handle spotify failing to refresh access_token (#9295)
* Handle spotify failing to refresh access_token

* Remove whitespace
2017-09-04 20:47:40 +02:00
Andreas Jacobsen
54de3d89d1 Added intent_type to exception log (#9289) 2017-09-04 13:40:08 +02:00
Jeroen ter Heerdt
1b5e574a76 Fixing bug when using egardiaserver - package requirement updated to 1.0.20. (#9294)
* Bumping pythonegardia package requirement up to .18

* Updating requirements_all to reflect updated pythonegardia package .18

* Catching up with reality and updating egardia.py

Requirements_all reflects updated package requirement for python-egardia of 1.0.20
2017-09-04 13:34:56 +02:00
Daniel Høyer Iversen
e6207684bf rfxtrx lib upgrade (#9288) 2017-09-04 10:19:58 +02:00
Fabian Affolter
7c7a5a4a15 Upgrade python-telegram-bot to 8.0.0 (#9282) 2017-09-03 17:21:51 -04:00
Fabian Affolter
5dfd60a029 Upgrade youtube_dl to 2017.9.2 (#9279) 2017-09-03 17:21:35 -04:00
Paul Sokolovsky
38e1b81ff6 discovery: If unknown NetDisco service discovered, log about it. (#9280)
Otherwise, known services are logged, ignored are logged, but unknown -
not. Logging them is quite helpful for someone working on adding new
discovery service to NetDisco/HA, and would help to decouple NetDisco
library further: another project may use a generic NetDisco library,
and contribute new service to it, which won't be automatically supported
by HA. But logging about it would be a good hint to HA users that they
can look into supporting it.
2017-09-03 16:27:13 -04:00
Dan Ports
68343ac81f insteon_plm: fix typo in attributes (#9284) 2017-09-03 15:42:05 -04:00
emlt
7694c31814 Change attribute names (#9277)
Remove spaces and capitals in attribute names to be consistent with sensors and other switches.
2017-09-03 16:07:12 +02:00
Greg Dowling
db36b5cd23 Merge pull request #9274 from home-assistant/bump_pywemo
Bump pywemo, handle more ports.
2017-09-03 12:13:10 +01:00
pavoni
a78f5e0970 Bump pywemo, handle more ports. 2017-09-03 11:31:55 +01:00
Abílio Costa
0889e38cb1 flux: fix for when stop_time is after midnight (#8932)
* flux: fix for when stop_time is after midnight

* flux: fix imports

* flux: add missing check when now is after midnight

* flux: one more try; should fix all use cases now

* flux switch: fix lint

* flux switch: add new tests

* flux switch: fix tests lint

* flux switch: fix tests docstrings
2017-09-02 18:02:11 +02:00
Gunnar Helgason
f51163f803 Add Geofency device tracker (#9106)
* Added Geofency device tracker

Added Geofency device tracker

* fix pylint error

* review fixes

* merge coroutines
2017-09-01 23:56:59 +02:00
Matthew Breedlove
639eb81aef Adding ZWave CentralScene activation handler. (#9178)
* Adding ZWave CentralScene activation handler.

* Migrated CentralScene logic to node_entity.py

Removed extraneous logging

Modified scene_activated event to send the scene_id and scene_data separately

* Adding unit test for ZWave central scene activation

* Removed return to allow node statistics to update after central scene message is received
2017-09-01 21:41:35 +02:00
Fabian Affolter
8797932f80 Upgrade psutil to 5.3.0 (#9253) 2017-09-01 18:05:53 +02:00
Fabian Affolter
8d1f6d3995 Upgrade sendgrid to 5.2.0 (#9254) 2017-09-01 18:05:37 +02:00
Christian Brædstrup
4defd96cd6 Version bump of DLink switch to v0.6.0 (#9252) 2017-09-01 15:27:43 +02:00
snjoetw
185d838803 This is to fix #6386: Manual Alarm not re-arm after 2nd trigger (#9249) 2017-09-01 12:08:30 +02:00
Philipp Schmitt
713f7fa2a1 Fix nello.io login (#9251) 2017-09-01 12:02:22 +02:00
Daniel Høyer Iversen
4cd5173ac8 upgrade xiaomi lib (#9250) 2017-09-01 11:58:26 +02:00
Oliver
8d5f6723ce Added configurable timeout for receiver HTTP requests | Additional AV… (#9244)
* Added configurable timeout for receiver HTTP requests | Additional AVR-X detection based on CommApiVers | Treat Marantz SR6007 - SR6010 as AVR-X device

* timeout value not passed correctly
2017-09-01 09:15:47 +02:00
Marcelo Moreira de Mello
a55895b662 Make sure Ring binary_sensor state will update only if device_id matches (#9247) 2017-09-01 09:14:16 +02:00
Pascal Vizeli
0af4f8903d Add available to sonos (#9243)
* Readd sonos available flag / fix polling state

* cleanup
2017-09-01 00:23:11 +02:00
Pascal Vizeli
836b528bd3 WIP: Homematic improvments with new hass interfaces (#9058)
* Remove hass to init hack and use official interfaces

* fix lint

* Fix lint

* change style
2017-08-31 21:16:44 +02:00
Martin Hjelmare
274e4449ea Fix possible KeyError (#9242)
* Multiple devices per child per platform would lead to KeyError.
2017-08-31 21:00:09 +02:00
Daniel Høyer Iversen
acb6b7c68d title and message was swapped in pushbullet (#9241) 2017-08-31 20:41:22 +02:00
Adam Mills
7d281fd224 Skip automatic events older than latest data (#9230)
* Skip automatic events older than latest data

* Update test
2017-08-31 16:29:18 +02:00
Fabian Affolter
60342b4738 Upgrade discord.py to 0.16.11 (#9239) 2017-08-31 16:26:52 +02:00
happyleavesaoc
99c1c9472a mopar sensor (#9136)
* mopar sensor

* fix doc url

* mopar review comments

* remove unneeded hass.data handling

* fix lint
2017-08-31 16:26:33 +02:00
Daniel Høyer Iversen
d816ff26ad A bugfix for pushbullet (#9237)
* Bug fix for pushbullet
2017-08-31 14:19:33 +02:00
John K. Luebs
e22ec28bce Use ZCL mandatory attribute to determine ZHA light capabilities (#9232)
The manadatory ColorCapabilities attribute should indicate whether a
light is capable of XY color changes and/or color temperature.
2017-08-31 00:18:01 -05:00
Andrey
bb37294047 Allow panels with external URL (#9214)
* Allow panels with external URL

* Update comment
2017-08-30 23:21:24 -05:00
Maciej Sokołowski
de4a4fe71a [light.tradfri] Full range of white spectrum lightbulbs support (#9224)
* [light.tradfri] Support for pytradfri version supporting full white spectrum

* [light.tradfri] Checkout pytradfri master

* Developer docker image adjusted

* [light.tradfri] pytradfri 2.2 support for white spectrum bulbs

* Removed fix already included in dev

* Style adjusted

* pylint false positive overriden

* Review remarks applied (#1)

* make pylint happy

* Review remarks
2017-08-30 23:19:06 -05:00
Sergey Isachenko
5f445b4a13 Tesla platform (#9211)
* Tesla support implemetation

* requirements_all.txt fix

* .coveragerc fix

* logging-too-many-args fix

* logging-too-many-args attempt 2

* Post-review fixes.

* requirements version fix

* requirements

* Lint fix

* Hot fix

* requirements_all.txt fix

* Review preparation.

* 1. Linting fix.
2. Minimal value for SCAN_INTERVAL hardcoded to 300 sec (to prevent possible ban form Tesla)

* Removed redundant whitespace.

* Fixed components according to @MartinHjelmare proposals and remarks.

* .coveragerc as @MartinHjelmare suggested.

* Minor changes

* Fix docstrings

* Update ordering

* Update quotes

* Minor changes

* Update quotes
2017-08-30 23:13:02 -05:00
Fabian Affolter
10e8aea46b Upgrade shodan to 1.7.5 (#9228) 2017-08-30 22:23:28 +02:00
Kris Molendyke
76c7eef7d8 Add Tank Utility sensor (#9132)
* Add Tank Utility sensor

* Fix, disable Pylint errors

* Move coverage omission to single platform section

* Do not catch unknown exceptions

* Check for invalid credentials in setup

* Update tank_utility.py
2017-08-30 22:21:54 +02:00
Daniel Høyer Iversen
214c92d787 pushbullet, send a file from url (#9189)
* pushbullet, send a file from url

* pushbullet, send a file from url

* Simplify
2017-08-30 21:42:27 +02:00
Jeroen ter Heerdt
f2551c08af Egardia package to .19 and change in port number for egardiaserver (#9225) 2017-08-30 20:11:45 +02:00
Lukas Barth
3a0e38aa73 Add max_age to statistics sensor (#8790)
* Add max_age to statistics sensor

* Allow only non-zero sampling sizes

* Fix long line

* Fix style
2017-08-30 17:13:36 +02:00
Riccardo Canta
56f9ccb877 Allow sonos to select album as a source (#9221)
Importing the fix in the PR https://github.com/home-assistant/home-assistant/pull/8258 I noticed that the same error is present also for Spotify album so I have extended the code and tested it. It works fine on my setup
2017-08-30 15:10:02 +02:00
Marcelo Moreira de Mello
f76436f326 Fix fitbit error when trying to access token after upgrade. (#9183)
*   - Fixes Fitbit error when trying to refresh oauth token

  The 3rd python-fitbit module requires an extra kwarg on the FitBit
  constructor called refresh_cb. The value should be a function that
  accepts one argument token.

  This value will be a dictionary with the keys:

     'access_token', 'refresh_token', 'expires_at'

  This implements a lambda refresh_cb as required by the Fitbit module
  to work, however the new token will always be save manually on
  every update() call.

*  Simplified by calling  expires_at instead reading again from dict
2017-08-30 10:01:01 +02:00
Fabian Affolter
4aafcfa478 Upgrade sendgrid to 5.0.1 (#9215) 2017-08-29 21:06:31 -07:00
Fabian Affolter
8673e53940 Upgrade pyasn1 to 0.3.3 and pyasn1-modules to 0.1.1 (#9216) 2017-08-29 21:06:18 -07:00
Nicholas Sielicki
ebc7ade591 directv: extended discovery via REST api, bug fix (#8800)
* fix not providing device for discovered directvs

This fixes a bug introduced at 6884965c80

Discovered directv boxes would not be instantiated with a DEVICE
parameter.

Signed-off-by: Nicholas Sielicki <sielicki@yandex.com>

* directv: add discovery of RVU clients

If discovery is used with directv, also try to further discover and
configure RVU client set-top boxes by requesting information from a REST
service running on the main directv box/RVU-server.

This commit also disables discovery if any directv configuration is
supplied by the user.

Signed-off-by: Nicholas Sielicki <sielicki@yandex.com>

* components/media_player/directv.py: use hass.data

Use hass.data instead of a global to remember state.

Signed-off-by: Nicholas Sielicki <sielicki@yandex.com>

* unconditionally import requests in directv.py

Requests is a core requirement, so we're okay to import at the top of
the file rather than conditionally / in a function.

Signed-off-by: Nicholas Sielicki <sielicki@yandex.com>
2017-08-30 00:08:56 +02:00
Jeff McGehee
7de73e9ef7 Bayesian Binary Sensor (#8810)
* Bayesian Binary Sensor

Why:

* It would be beneficial to leverage various sensor outputs in a
Bayesian manner in order to sense more complex events.

This change addresses the need by:

* `BayesianBinarySensor` class in
`./homeassistant/components/binary_sensor/bayesian.py`
* Tests in `./tests/components/binary_sensor/test_bayesian.py`

Caveats:
This is my first time in this code-base. I did try to follow conventions
that I was able to find, but I'm sure there will be some issues to
straighten out.

* minor cleanup

* Address reviewer's comments

This change addresses the need by:

* Removing `CONF_SENSOR_CLASS` and its usage in `get_deprecated`.
* Make probability update function a static method, and use single `_`
to match project conventions.

* Address linter failures

* fix `device_class` declaration

* Address Comments

Why:
* Not validating config schema enough.
* Not following common practices for async initialization.
* Naive implementation of Bayes' rule.

This change addresses the need by:
* Improving config validation for observations.
* Moving initialization logic into `async_added_to_hass`.
* Re-configuring Bayesian updates to allow true P|Q usage.

* address linting issues

* Improve DRYness by adding `_update_current_obs` method

* update doc strings and ensure functions are set up properly for async

* Make only 1 state change handle

* fix style

* fix style part 2

* fix lint
2017-08-29 23:53:41 +02:00
Paulus Schoutsen
0b58d5405e Add cloud auth support (#9208)
* Add initial cloud auth

* Move hass.data to a dict

* Move mode into helper

* Fix bugs afte refactor

* Add tests

* Clean up scripts file after test config

* Lint

* Update __init__.py
2017-08-29 13:40:08 -07:00
Mister Wil
33c906c20a Abode push events and lock, cover, and switch components (#9095)
* Updated abodepy version to 0.7.1

* Refactored to use AbodeDevice. Added Abode Lock device.

* Added push updates to abode devices.

* Upgraded to 0.7.2 after finding issue with callbacks.

* Refactored to use AbodeDevice. Added Abode Lock device.

* Added push updates to abode devices.

* Upgraded to 0.7.2 after finding issue with callbacks.

* Bumped version to 0.8.2. Modified code to work with new constants and properties. Added cover and switch.

* Fixed hound violations.

* Updated to 0.8.3 to fix small bug with standby mode. Fixed comment in cover/abode.py.

* Fix lint issues

* Removed excessive logging. Moved device callback registration to async_added_to_hass. Moved abode controller from global into hass data.

* Removed explicit None from dict.get()

* Move device class into the constructor.

* Changed constant name to platforms.

* Changes as requested.

* Removing stray blank line.

* Added blank line of which I'm not sure how it was removed.

* Updated version to 0.9.0. Fixed motion sensor. Added power_switch_meter device type.

* Update abode.py

* fix lint
2017-08-29 17:34:19 +02:00
Paulus Schoutsen
81a00bf3f1 Lint Sonarr tests 2017-08-29 08:10:38 -07:00
Martin Hjelmare
b8d737c0cc Upgrade pymysensors to 0.11.1 (#9212) 2017-08-29 17:10:28 +02:00
Daniel Høyer Iversen
ee28b439b3 Refactor rfxtrx (#9117)
* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor

* rfxtrx refactor
2017-08-29 16:22:28 +02:00
Daniel Høyer Iversen
aa8dd8fbdd Issue #6893 in rfxtrx (#9130)
* Issue #6893 in rfxtrx

* Update rfxtrx.py

* rfxtrx issue
2017-08-29 16:20:26 +02:00
William Scanlon
3e0eb8763f Support for season sensor (#8958)
Add an optional extended description…
2017-08-29 16:18:36 +02:00
Fabian Affolter
0687a457b1 Add counter component (#9146) 2017-08-29 15:44:36 +02:00
Dale Higgs
38071501b4 Fix and optimize digitalloggers platform (#9203)
* Fix and optimize digitalloggers platform

* Fix line length

* Fix hanging indentation

* Add missing docstring

* Add period to end of docstring

* Add second blank line
2017-08-29 15:38:42 +02:00
mjj4791
5d800c1d51 Prevent error when no forecast data was available (#9176)
* Prevent error when no forecast data was available

Prevent an Error when buienradar data was available, but no forecasted data was retrieved for the requested day.

* Update buienradar.py

* Update buienradar.py
2017-08-29 15:33:47 +02:00
Trevor
75559cb81f Add "status" to Sonarr sensor (#9204)
* Use X-Api-Key header

* Increase timeout

* Add "status" to Sonarr sensor

* Update test_sonarr.py

* Update test_sonarr.py

* Update test_sonarr.py

* Update sonarr.py

* Update sonarr.py
2017-08-29 15:33:27 +02:00
aetolus
0de6a37822 fix worldtidesinfo #9184 (#9201) 2017-08-29 08:28:40 +02:00
bobnwk
6505019701 Update pushbullet.py (#9200) 2017-08-29 05:40:33 +02:00
Mario Wenzel
e76e9e0966 Fix dht22 when no data was read initially #8976 (#9198)
This fixes https://github.com/home-assistant/home-assistant/issues/8976
When no data was available the module crashes.
2017-08-28 22:46:31 +03:00
Paulus Schoutsen
bd71a33ba8 Merge pull request #9196 from home-assistant/release-0-52-1
0.52.1
2017-08-28 09:22:00 -07:00
Nolan Gilley
0ccff6c03e bump ecobee version to fix issue 9190 (#9191) 2017-08-28 09:15:34 -07:00
mjj4791
3509ecf07f Prevent iCloud exceptions in logfile (#9179)
* Prevent iCloud exceptions in logfile

With this change ValueError exceptions in the logfile caused by this component will disappear.
These errors are caused by the iCloud API returning an HTTP 450 error and the external lib throwing a ValueError because of it.

A PR has been raised against the external library, but that fix did not yet make it into a new version of the library. This will catch the exception in the mean time.... https://github.com/picklepete/pyicloud/pull/138

* Align log messages
2017-08-28 09:15:34 -07:00
Paulus Schoutsen
308b822832 Wrap state when iterating a domain in templates (#9157) 2017-08-28 09:15:34 -07:00
Adam Mills
d986b8f4c2 Bump aioautomatic to prevent leaking exceptions (#9148) 2017-08-28 09:15:33 -07:00
Sean Dague
e6892a4077 Fix import for foscam (#9140)
While waiting for a new pyfoscam release, we can fix this for users
just by changing the import. Foscam devices a pretty widely deployed,
so a regression here is definitely no fun.

Fixes Bug #8940
2017-08-28 09:15:33 -07:00
Daniel Høyer Iversen
422be25d22 bug fix pushbullet (#9139) 2017-08-28 09:15:33 -07:00
Daniel Høyer Iversen
0ae1f85f9f Fix issue #9116 in pushbullet (#9128)
* Fix issue #9116 in pushbullet
2017-08-28 09:15:32 -07:00
Andrey Kupreychik
8a89643338 Close stream request once we end up with proxy (#9110)
* Close stream request once we end up with proxy

* Update aiohttp_client.py

* Update aiohttp_client.py

* Removed trailing whitespace
2017-08-28 09:15:32 -07:00
Paulus Schoutsen
10e3c00f07 Version bump to 0.52.1 2017-08-28 09:11:11 -07:00
mjj4791
cc18b5af3d Prevent iCloud exceptions in logfile (#9179)
* Prevent iCloud exceptions in logfile

With this change ValueError exceptions in the logfile caused by this component will disappear.
These errors are caused by the iCloud API returning an HTTP 450 error and the external lib throwing a ValueError because of it.

A PR has been raised against the external library, but that fix did not yet make it into a new version of the library. This will catch the exception in the mean time.... https://github.com/picklepete/pyicloud/pull/138

* Align log messages
2017-08-28 09:09:36 -07:00
Paulus Schoutsen
924290adb0 Update frontend 2017-08-28 09:04:34 -07:00
Nolan Gilley
f9c22b0e61 bump ecobee version to fix issue 9190 (#9191) 2017-08-28 10:12:21 -05:00
Ryan Kraus
2533b49aef Merge pull request #9182 from home-assistant/pyisy-update
Bumped the version of PyISY
2017-08-28 00:02:28 -04:00
Ryan Kraus
f6a701e843 Bumped the version of PyISY
PyISY has been updated to better support newer ISY994 firmware. This
should resolve #7601.
2017-08-27 23:24:29 -04:00
Brian Hopkins
bd039b8c53 Mycroft notify/component (#9173)
* working mycroft notification platform

* Update mycroft.py

* Update mycroft.py

* Update mycroft.py

* Update mycroft.py

* updating to use new api

updating code to use new api.

* updating changes

updating files

* updating typos

fixing some typos

* Update mycroft.py

adding text

* fixing pep issues

fixing pep issues

* adding new mycroft component

adding mycroft component

* updating

updating code

* updating typo

fixing typo

* updating file

adding updates

* updating notify

updating notify component for new changes

* Update mycroft.py

* Update mycroft.py

* Update mycroft.py

* updating for tox

updating to pass tox tests

* updating for tox

fixing tox errors

* fixing tox issues

fixing tox issues

* fixing tox issues

fixing more tox issues

* updating requirement

adding requirement for component

* fixed typo

fixed typo

* updating requirements

updating requirements

* updating code

updating code

* updating files

updating

* updating

* adding logging

adding in logging

* fixing typo

fixing typo

* updating debugs

* updating files

updating files

* updating dependencies

updating dependencies

* updating to load notification

updating to load notification

* cleaning up whitespace

* updating requirements_all.txt

* adding requirement

adding requirement

* Update mycroft.py

* Update .coveragerc

updated .coveragerc
2017-08-27 13:53:20 -07:00
Sebastian Muszynski
de48d42f33 "TypeError: write_to_hub() takes 2 positional arguments but 4 were given" fixed. (#9174) 2017-08-27 21:41:47 +02:00
Sebastian Muszynski
bf315da8df Xiaomi gateway: Device support for the Aqara Water Leak Sensor (sensor_wleak.aq1) (#9172)
* Device support for the Aqara Water Leak Sensor (sensor_wleak.aq1) added.

* Required version of PyXiaomiGateway changed.
2017-08-27 21:06:11 +02:00
EmitKiwi
654f6892f9 Mysensors nodes can be renamed in config file (#9123)
* Mysensors nodes can be renamed in the config file

* Replace nodes array with dict. Replace whole name of the node.

* Improved iteration on node names
2017-08-27 20:40:38 +02:00
Adam Mills
cd3f0f8f96 Use node_modules gulp in script/build_frontend (#9170) 2017-08-27 13:46:37 -04:00
Daniel Høyer Iversen
499d54c8fc upgrade xiaomi lib to 0.3.1 to supprt water sensor (#9168) 2017-08-27 19:31:34 +02:00
Paulus Schoutsen
5629157740 Allow getting number of available states in template (#9158) 2017-08-27 18:33:25 +02:00
Andrey
c367021aa4 Allow specifying custom html urls to load. (#9150)
* Allow specifying custom html urls to load.

* Change add_extra_html_urls to accept a single URL
2017-08-27 09:07:58 -07:00
Fabian Affolter
8fdd9712e6 Upgrade uber_rides to 0.5.2 (#9149) 2017-08-27 11:31:06 +02:00
Fabian Affolter
f47de06f02 Upgrade sphinx-autodoc-typehints to 1.2.3 (#9151) 2017-08-27 11:30:42 +02:00
Fabian Affolter
7062c2b257 Remove links to gitter (#9155) 2017-08-27 11:30:26 +02:00
Fabian Affolter
ae5fca1ec9 Upgrade async_timeout to 1.3.0 (#9156) 2017-08-27 11:30:04 +02:00
Paulus Schoutsen
8605098ea0 Wrap state when iterating a domain in templates (#9157) 2017-08-26 17:00:59 -07:00
Adam Mills
21bf089b17 Bump aioautomatic to prevent leaking exceptions (#9148) 2017-08-26 17:09:57 -04:00
Andrey
c73338bf3e Backend changes for customize config panel. (#9134)
* Backend changes for customize config panel.

* Backend changes for customize config panel.

* Add customize.yaml to default config

* Precreate customize.yaml

* Add tests
2017-08-26 10:02:32 -07:00
Andrey Kupreychik
c537770786 Close stream request once we end up with proxy (#9110)
* Close stream request once we end up with proxy

* Update aiohttp_client.py

* Update aiohttp_client.py

* Removed trailing whitespace
2017-08-26 09:56:39 -07:00
Daniel Høyer Iversen
493353e4de bug fix pushbullet (#9139) 2017-08-26 09:12:51 -07:00
Sean Dague
f4d464c008 Fix import for foscam (#9140)
While waiting for a new pyfoscam release, we can fix this for users
just by changing the import. Foscam devices a pretty widely deployed,
so a regression here is definitely no fun.

Fixes Bug #8940
2017-08-26 09:08:37 -07:00
Daniel Høyer Iversen
0d3fa59d77 Fix issue #9116 in pushbullet (#9128)
* Fix issue #9116 in pushbullet
2017-08-26 09:36:54 +02:00
Paulus Schoutsen
50e5032f86 Merge pull request #9131 from home-assistant/release-0-52
0.52
2017-08-25 22:11:12 -07:00
Martin Hjelmare
1d615ea6c3 Refactor mysensors callback and add validation (#9069)
* Refactor mysensors callback and add validation

* Add mysensors entity class. The mysensors entity class inherits from
  a more general mysensors device class.
* Extract mysensors name function.
* Add setup_mysensors_platform for mysensors platforms.
* Add mysensors const schemas.
* Update mysensors callback and add child validation.
* Remove gateway wrapper class.
* Add better logging for mysensors callback.
* Add discover_persistent_devices function.
* Remove discovery in mysensors component setup.
* Clean up gateway storage in hass.data.
* Update all mysensors platforms.
  * Add repr for MySensorsNotificationDevice.
  * Fix bug in mysensors climate target temperatures.
  * Clean up platforms. Child validation simplifies assumptions in
    platforms.
  * Remove not needed try except statements. All messages are validated
    already in pymysensors.
* Clean up logging.
* Add timer debug logging if callback is slow.
* Upgrade pymysensors to 0.11.0.

* Make dispatch callback async

* Pass tuple device_args and optional add_devices

* Also return new_devices as list instead of dictionary.
2017-08-25 21:47:22 -07:00
Sebastian Muszynski
56083c0c64 Xiaomi Philips Lights integration (#9087)
* Adds support for the Xiaomi Philips LED Ball and Ceiling Lamp

* Documentation url updated.

* New component to .coveragerc added.

* Unused import removed.

* translate labeled as static method.

* Mixed parameters in log message fixed.

* Order of requirements_all.txt fixed.

* Plattform updated. It's async now.

* Simplifiable if-statement fixed.

* Some more clean-up of unneeded stuff.

* Platform schema updated.

* Component is called xiaomi_philipslight now.

* Requirements all updated.

* Initialization of some variables updated.

* Raise PlatformNotReady exception if light cannot be discovered.

* Import of math removed.
Missing space added.

* Remove unnecessary updates
2017-08-25 21:27:31 -07:00
Martin Hjelmare
8775c54d29 Refactor mysensors callback and add validation (#9069)
* Refactor mysensors callback and add validation

* Add mysensors entity class. The mysensors entity class inherits from
  a more general mysensors device class.
* Extract mysensors name function.
* Add setup_mysensors_platform for mysensors platforms.
* Add mysensors const schemas.
* Update mysensors callback and add child validation.
* Remove gateway wrapper class.
* Add better logging for mysensors callback.
* Add discover_persistent_devices function.
* Remove discovery in mysensors component setup.
* Clean up gateway storage in hass.data.
* Update all mysensors platforms.
  * Add repr for MySensorsNotificationDevice.
  * Fix bug in mysensors climate target temperatures.
  * Clean up platforms. Child validation simplifies assumptions in
    platforms.
  * Remove not needed try except statements. All messages are validated
    already in pymysensors.
* Clean up logging.
* Add timer debug logging if callback is slow.
* Upgrade pymysensors to 0.11.0.

* Make dispatch callback async

* Pass tuple device_args and optional add_devices

* Also return new_devices as list instead of dictionary.
2017-08-25 08:58:05 -07:00
Paulus Schoutsen
044b96e3cd Version bump to 0.53.0.dev0 2017-08-25 08:44:35 -07:00
Paulus Schoutsen
2e1b1635b1 Version bump to 0.52 2017-08-25 08:44:02 -07:00
Paulus Schoutsen
fe7dca5144 Merge remote-tracking branch 'origin/master' into dev 2017-08-25 08:43:26 -07:00
Fabian Affolter
fdeef2f707 Use const (#9127)
* Use const

* Align quotes
2017-08-25 13:30:00 +02:00
PhracturedBlue
2ec0d25a38 optimistic mode for template covers (w/o timed movement) (#8402)
* Emulate set_current_position in cover.template

* Add opportunistic mode

* Prevent another move when cover is already moving.  Add tests for opotunistic/timed-delay mode

* Remove timed-move capabilities

* Set init state to unknown

* cleanup template

* Update test_template.py
2017-08-25 12:33:53 +02:00
Daniel Høyer Iversen
fb5019e73f refactor pushbullet (#9125)
* refactor push bullet
2017-08-25 11:25:06 +02:00
Daniel Høyer Iversen
1e276a7b07 Xiaomi (#9126)
* small fixes xiaomi
2017-08-25 10:22:32 +02:00
Daniel Høyer Iversen
d72a181e30 Update flux_led.py (#9122) 2017-08-24 23:31:57 +02:00
lekobob
698d133455 Simplisafe unknown status fix (#9111)
* Simplisafe unknown status fix

Changed simplisafe-python requirement to 1.0.5 and changed state return
case statements to lower case

* Bump requirements_all.txt
2017-08-24 08:09:50 +02:00
Jeroen ter Heerdt
0dccef4063 pythonegardia package requirement to .18 (#9104)
* Bumping pythonegardia package requirement up to .18

* Updating requirements_all to reflect updated pythonegardia package .18
2017-08-23 17:11:13 +02:00
Daniel Høyer Iversen
feb85b90b4 upgrade Xiaomi Gateway lib to 0.3 (#9101) 2017-08-23 12:37:17 +02:00
Robin
48909539be Fix issue 8894 with uk_transport component if no next_buses or next_trains (#9046)
* Fix bug if no next_buses or trains

Fixes https://github.com/home-assistant/home-assistant/issues/8894

* Requested fixes
2017-08-23 00:05:06 -07:00
Fabian Affolter
2355216f61 Catch exceptions (#9085)
* Catch exceptions

* Fix pylint disable

* Move check for emtpy target list
2017-08-22 22:21:09 -07:00
Teemu R
55a44b0a1c Yeelight fix updates on hsv mode (#9093)
* cast strings to integers for hsv_to_rgb conversion, fixes #6473

* remove type_checking, flake8 does not like that.

* use hsv_to_rgb to convert to correct rgb value
2017-08-22 22:19:33 -07:00
happyleavesaoc
27b0d648a6 bump fedex version (#9099) 2017-08-22 22:14:06 -07:00
happyleavesaoc
90724847a3 bump snapcast version (#9100) 2017-08-22 22:13:52 -07:00
Teemu R
90fb33f610 Support changing the bulb color for tplink smartbulbs, fixes #8766 (#8780)
* Support changing the bulb color for tplink smartbulbs, related to #8766

* existence of ATTR_RGB_COLOR in kwargs, not just its existence...

* return modified supported features

* rgb-hsv conversion utils from hass return bogus values (at least for this device), so doing conversions directly with colorsys

* add typing & documentation for color model conversions

* make linters happy

* cast hsv to integer before passing it to the backend library

* make sure the bulb is on before adjusting the other settings

* allow floats as inputs for conversions, return always integers

* use typing hint in the parameter list instead of at assignment

* do not assign local color state inside turn_on, but let update handle doing it

* use forward declaration for typing, fixes travis requirements build hopefully

* rename hsv and rgb

* remove type-checking check, forward declarations should work just fine without it

* disable (broken) pylint warnings, these can be removed after astroid is updated from 1.4.9 to 1.5
2017-08-22 22:11:44 -07:00
aetolus
cb59b3fee1 Add worldtidesinfo sensor component (#8860)
* Style fixes for worldtidesinfo sensor component

* Fix D202 for worldtidesinfo sensor component

* Multiple fixes

* Multiple fixes

* Fixes

* more

* working with changes

* changes

* changes

* fix style errors

* fix style errors

* Complete rewrite

* worldtidesinfo

Fix D202 for worldtidesinfo sensor component

Multiple fixes

Multiple fixes

Fixes

more

working with changes

changes

changes

fix style errors

fix style errors

Complete rewrite

PR Changes

* Fix

* fix scan interval & lint
2017-08-23 00:40:16 +02:00
Michaël Arnauts
90689c38f7 Fix netdata system_load and add disk_free. (#9091) 2017-08-22 17:52:29 +02:00
Jan Losinski
fd6fd765b2 Pilight switch: restore last state after restart (#8580)
* Pilight switch: restore last state after restart

This uses the restore_state helper to set the last known state to
pilight switches when the devices are initialized after a HA
restart.

Without this HA forget the state on every restart and needs to be told
the sttae by retoggling the switches. This can cause unwanted effects
as a switch toggling may emit an RF signal.

* Make hound happy

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

* Remove entity_id generation as requested in review.

* Make hound happy again.

* fix comments

* fix lint
2017-08-22 16:40:14 +02:00
William Scanlon
06a20d0d15 Fix octoprint errors when printer is off/disconnected (#8988)
* Fix octoprint errors when printer is off/disconnected
2017-08-22 09:37:06 -04:00
Fabian Affolter
252aea37d2 Don't redefine consts (#9086) 2017-08-22 13:12:01 +02:00
Alex
5a3a43cd5b 9043 Fixed error while running dev docker (#9044) 2017-08-22 13:48:50 +03:00
Fabian Affolter
dd0ca0adc4 Upgrade credstash to 1.13.3 (#9088) 2017-08-22 13:32:46 +03:00
Fabian Affolter
c77d2ea341 Remove dash (#9089) 2017-08-22 13:31:53 +03:00
Fabian Affolter
4a3be6d514 Upgrade youtube_dl to 2017.8.18 (#9079) 2017-08-22 10:26:31 +03:00
Max
da2cb8e97e Fix device attribute in fritz_callmonitor.py (fixes #9055) (#9081) 2017-08-22 10:24:36 +03:00
Fabian Affolter
42fcaf9a75 Upgrade discord.py to 0.16.10 (#9082) 2017-08-22 10:24:02 +03:00
Fabian Affolter
af8aec001c Upgrade uber_rides to 0.5.1 (#9080) 2017-08-22 10:23:11 +03:00
Tom Harris
f6c5e5ff00 Added insteonplm device_override multiple capabilities (#9078) 2017-08-22 10:22:37 +03:00
Erik Eriksson
398735c9be async_query returns False if connection to server failed, handle this properly (#9070) 2017-08-22 07:09:11 +02:00
Alok Saboo
8ceeee032c Bump abodepy to 0.7.1 (#9077)
* Version bump to 0.7.1

* Update abodepy version
2017-08-22 07:08:27 +02:00
Nolan Gilley
54f01f3f11 bump python-ecobee-api version to 0.0.8 (#9074) 2017-08-21 23:16:17 +02:00
Alok Saboo
bc549e9525 Use builtin constants for Abode alarm_control_panel (#9059)
* Use builtin constants for alarm_control_panel

* Made it consistent with other alarm panels

* Replaced STATE_UNKNOWN with None
2017-08-21 22:20:38 +02:00
Erik Eriksson
4bb78097a7 eliqonline: channel id is an integer (#9072) 2017-08-21 21:57:12 +02:00
Ståle Semb Hauknes
7c380588a0 Workday sensor offset (#8824)
* Add support for offset for the workday sensor

* Update tests for workday sensor

* Changed from 'offset' to 'days_offset'

* Attributes bugfix (dictionary key variable repeated with different values)
2017-08-21 13:24:30 +02:00
Fabian Affolter
f7daefd7a5 Upgrade onkyo-eiscp to 1.2.4 (fixes #8995) (#9068) 2017-08-21 11:25:34 +02:00
Martin Berg
97e6a69adb Add support for Prowl notifications. (#9028)
* Add support for Prowl notifications.

* Use HA session handler.

* Simplify http request logic.

* flake

* fix double fetch data

* Remove periods from log messages
2017-08-21 10:46:07 +02:00
Fabian Affolter
fe7384a4ef Upgrade slacker to 0.9.60 (#9065)
* Upgrade slacker to 0.9.60

* Group imports
2017-08-21 10:23:29 +02:00
Fabian Affolter
3c9e09ce16 Upgrade sendgrid to 5.0.0 (#9062) 2017-08-21 07:15:37 +02:00
Matt Schmitt
c3d548a0dd Update fitbit.py (#9064)
Minor format update, set ‘type’ attribute to lowercase (Fitbit returns
all uppercase currently)
2017-08-21 07:15:15 +02:00
Alok Saboo
ebd64cded9 Bump dlib face_recognition to 0.2.2 (#9060) 2017-08-20 22:30:35 +02:00
Anders Melchiorsen
fee89d8d16 LIFX: avoid rare NoneType errors (#9054)
* Get full multizone state during registration

We used to rely on the periodic update to get the state of each zone, only
establishing the number of zones during registration. This resulted in errors
if the current state was needed for a partial color change before the first
async_update happened.

Now we do a full update before adding the light. Thus async_update can no
longer assume device.color_zones to be defined and must instead use the
response message to decide the total number of zones.

* Insist on getting the initial state

If a response to the initial state query is lost we used to just carry on.
This resulted in type errors when we next tried to access the undefined state.

After this commit the light is not added before we have the full state.

This scenario mostly happens when something is misbehaving and the type errors
were actually useful in figuring out what happend. So an error message is
logged in their place.

* Remove lint
2017-08-20 20:29:54 +02:00
Alok Saboo
b3d16e8f89 Add Abode home security component (#9030)
* Add Abode home security component

* Remove protected member

* Remove debug messages

* Remove unwanted debug messages

* Updated based on script/gen_requirements_all

* Commit to restart the build process

* Remove unwanted return

* Removed unused listener

* Address Pascal's comments

* Updated alarm control panel based on Pascal's comments

* Removed debug messages

* Removed unused hass object
2017-08-20 16:55:48 +02:00
Matt Schmitt
c059dfdb67 Update Fitbit sensor (icons, formatting, client update) (#9031)
* Update fitbit.py

Add variable icon for battery status, clean up formatting for resource
names and values

* Update fitbit.py and requirements_all.txt

Fix PR comments and update client

* Update fitbit.py

Add dict map for battery levels and use icon util
2017-08-19 22:47:31 +02:00
boojew
d153ee0b9f Add speeds to fan dropdown in ISY fan component (#9004)
* Add speeds to fan dropdown in ISY fan component

* Update isy994.py

* Update isy994.py

* Update isy994.py

* Update isy994.py

* Update isy994.py

* Update isy994.py

* Update isy994.py

* Update isy994.py
2017-08-19 21:17:47 +02:00
David
0f9ae8827c Upgrade python-pushover to 0.3 (#9045)
* Upgrade python-pushover

* Upgrade python-pushover
2017-08-19 17:00:07 +02:00
Steve
5d52993231 Support Windows in UPNP discovery (#8936)
* Support WIndows and Linix

* Correct indentation

* reduce line length

* Lint
2017-08-19 15:26:27 +02:00
Matt Schmitt
84025e46ff Update ios.py (#9041)
Use battery icon util for charging condition also
2017-08-19 15:24:13 +02:00
Sören Oldag
bf66019c66 Configurable timeout for webostv. (#9042)
* Configurable timeout for webostv.

* Make PEP257 validation to pass
2017-08-19 15:14:02 +02:00
Sören Oldag
a748b5ee5e Update pwmled to 1.2.1. (#9040) 2017-08-19 13:23:46 +02:00
Robin
98370560e1 Adds London_air component (#9020)
* Adds London_air component

* Fix lints

* Reduce fixture

* Fix config validate

* Fix naming

* fix tests
2017-08-19 11:05:16 +02:00
Matt Schmitt
597f53ae30 Update iOS sensor (battery icon fix and format updates) (#9032)
* Update ios.py

Clean up battery and charging icons (MDI was missing some versions),
fix minor bug when battery level = 95%

* Update ios.py

Migrated function to battery icon util
2017-08-19 10:59:54 +02:00
Steven Looman
7ac1e469b7 Set password after connecting. Fixes #8983 (#9039) 2017-08-19 10:58:42 +02:00
John Mihalic
ecc249aa27 Refactor USPS into component with Sensors+Camera (#8679)
* Inital USPS Camera expansion

* Cleanup debugging, add camera change interval

* Change to local nomail image

* Explicitly pass in date

* Move camera date info to model property

* Fix copy typo

* Fix hound line-length

* Fix lint whitespace

* Fix requirements

* Bump myusps version, clarify interval, alter update scheme

* Add units

* Code cleanup, address comments

* Use built-in scan interval, remove nomail image

* Remove logging line
2017-08-18 23:47:36 +02:00
celeroll
6215e27de4 Fix Geizhals index issue when not 4 prices available (#9035)
* Out of index issue, when not 4 prices are available

* Removed the parenthesis, to fix the lint error.
2017-08-18 19:59:20 +02:00
Paulus Schoutsen
b282167f26 Add state_with_unit property to state objects in templates (#9014)
* Wrap state objects in templates

* Fix tests

* Fix bugs

* Lint

* Remove invalid state warning
2017-08-17 23:19:35 -07:00
Pascal Vizeli
c278209c7b Update ffmpeg to 1.7 to fix severals problems (#9029)
* Update ffmpeg to 1.7 to fix severals problems

* Update ffmpeg.py

* Update requirements_test_all.txt
2017-08-18 00:51:52 +02:00
Tom Matheussen
427d7ee1fc Check if album image(s) exist in spotify (#9024)
* Check if album image(s) exist in spotify

* Actually set the image to None

* Simplified using ternary operator
2017-08-17 22:39:20 +02:00
Dan
55234a7fa3 Update onkyo-eiscp to 1.2.3 (#9019) 2017-08-16 21:51:03 -07:00
BioSehnsucht
3765f882c7 Add HipChat notify service. (#8918)
* Add HipChat notify service.

* Change HipChat notify service to use python-simple-hipchat-v2.

* Change HipChat notify service to use hipnotify

* Change HipChat notify service to remove redundant validation
2017-08-16 19:26:30 -04:00
Michael Hertig
b75ce4f1b2 Fix #9010 - Swiss Public Transportation shows departure time in the past (#9011) 2017-08-16 21:28:51 +02:00
Dan Cinnamon
95663f8126 Update to pyenvisalink 2.2, and remove range validation on zonedump i… (#8981)
* Update to pyenvisalink 2.2, and remove range validation on zonedump interval.

* Keep using default timer dump variable, only remove minimum check.

* Fix lint issue

* Indentation issue
2017-08-16 12:08:15 +02:00
karlkar
f114263845 Pushbullet, fix multiple messages sent when url param is set (#9006) 2017-08-16 09:29:42 +02:00
mjj4791
e7ce110dc6 Buienradar newconditions (#8897)
* new monitored conditions and support for new weathercard

* new monitored conditions and support for new weathercard

* minor changes
2017-08-15 23:07:04 -07:00
timstanley1985
3342db33e4 MQTT Switch - Add configurable availability payload (#8934)
* Add configurable availabilty payload

* Fix

* Fix

* Lint fixes

* Fix tests

* Fix tests

* Move from const.py to mqtt switch

* New test

* Fix flake*
2017-08-15 23:04:57 -07:00
Paulus Schoutsen
0fb281c5b3 Update frontend 2017-08-15 22:34:46 -07:00
Paulus Schoutsen
2dab239021 Add scripts editor backend (#8993)
* Add scripts editor backend

* Fix docstrings
2017-08-15 22:09:10 -07:00
Adam Mills
95c57412ff Automatic device tracker remove password (#9002)
* Remove now disabled password auth from automatic

* Fallback to configurator more permissively

* Fix test for changes

* Bump lib
2017-08-15 21:04:44 -04:00
Aaron Bach
eb42d59210 Adds port/SSL config options for RainMachine (#8986)
* Adding port/SSL config updates

* New requirements generated

* Made `port` and `ssl` parameters optional

* Add defaults for new parameters

* Re-adding guard clause

* pass > continue
2017-08-15 20:03:40 +02:00
Tim Lyakhovetskiy
6507cc1dc8 Fix #8960 - Decora Wi-Fi Switch unable to set brightness (#8989) 2017-08-15 16:12:16 +02:00
Daniel Høyer Iversen
1892eb654f Is_allowed_path raise for None path (#8953)
* is_allowed_path

* Fix #8948

* assert path is not None

* Update test_core.py

* Update test_core.py

* Update test_core.py
2017-08-15 15:41:37 +02:00
Jack
5309006494 Added continue-on-errors, added value template (#8971)
* Added continue-on-errors, added value template

* Refactored long lines

* Fixed whitespace issues
2017-08-14 16:31:06 +02:00
Philipp Schmitt
e2920ce5e5 Nello.io lock support (#8957)
* Initial Nello.io lock support

* Log an error when unlocking failed

* Make the lock's state always locked
2017-08-14 10:02:37 +02:00
Adam Mills
19d1d748d4 Add support for Automatic OAuth2 authentication (#8962)
* Add support for Automatic OAuth2 authentication

* Fix async conversion of configurator

* Rename method for async

* Use hass.components to get configurator component

* Fix typo

* Move session data to hidden directory

* Make configurator callback optional
2017-08-13 22:37:50 -07:00
Paulus Schoutsen
8d661f8dea Merge pull request #8980 from home-assistant/release-0-51-2
0.51.2
2017-08-13 22:18:05 -07:00
Paulus Schoutsen
9363b189ba Sabnzbd: do not assume discovery info is a dict (#8951) 2017-08-13 21:54:21 -07:00
Eugenio Panadero
4da876e5c2 fix DeviceException handling when updating xiaomi vacuum (#8954)
* Fix DeviceException handling when updating entity

* add DeviceException error handling to generic request
2017-08-13 21:54:21 -07:00
Martin Hjelmare
335008ae5c Fix call to ha_send_commands (#8956)
* Name keyword arguments correctly according to dependency lib.
* Only pass keyword arguments that are not None.
2017-08-13 21:54:20 -07:00
Paulus Schoutsen
a4da31b573 fix issue #8948 in pushbullet (#8965)
* fix issue #8948 in pushbullet

* pushbullet
2017-08-13 21:54:20 -07:00
Andrey
cd795489ca Turn foscam verbose mode off (#8967) 2017-08-13 21:54:20 -07:00
Andrey
a8a037db49 Fix zwave power_consumption attribute (#8968) 2017-08-13 21:54:19 -07:00
Paulus Schoutsen
fc8e8e5d8c Update frontend 2017-08-13 21:53:26 -07:00
Paulus Schoutsen
56597d290c Version bump to 0.51.2 2017-08-13 21:53:22 -07:00
Paulus Schoutsen
8fcec03adf Update frontend 2017-08-13 21:52:36 -07:00
Andrey
a0ddb24245 Turn foscam verbose mode off (#8967) 2017-08-13 18:16:38 -07:00
Andrey
23273d3e88 Fix zwave power_consumption attribute (#8968) 2017-08-13 18:15:59 -07:00
Paulus Schoutsen
74adebc2fd fix issue #8948 in pushbullet (#8965)
* fix issue #8948 in pushbullet

* pushbullet
2017-08-13 13:28:36 -07:00
Paulus Schoutsen
4b3a932d88 Sabnzbd: do not assume discovery info is a dict (#8951) 2017-08-13 11:29:48 -07:00
Martin Hjelmare
cbe5225e04 Fix call to ha_send_commands (#8956)
* Name keyword arguments correctly according to dependency lib.
* Only pass keyword arguments that are not None.
2017-08-13 11:28:33 -07:00
Matt Schmitt
811fdc5533 Add service to alarm control panel for night mode arming (#8614)
* Update const.py

* Update __init__.py

* Update services.yaml

* Update totalconnect.py

* Update manual.py

Add night arm service for manual alarm control panel

* Update test_manual.py

Add tests for night mode arming

* Update manual.py

Fix docstring
2017-08-13 19:57:48 +02:00
Eugenio Panadero
c92e5c147a fix DeviceException handling when updating xiaomi vacuum (#8954)
* Fix DeviceException handling when updating entity

* add DeviceException error handling to generic request
2017-08-13 15:02:48 +02:00
Sebastian Muszynski
73d6227021 Remove spaces from Xiami switch attributes (#8952)
* Attributes of the xiaomi zigbee plug changed.

* Reformat.
2017-08-13 09:54:43 +02:00
Alok Saboo
79f45b5176 Fixed cert_expiry sensor to delay firing on HA startup (#8920)
* Fixed cert_expiry sensor to delay firing on HA startup

* Addressed Travis complaints

* Added imports

* Fixed cert_expiry sensor to delay firing on HA startup

* Changed comment
2017-08-12 23:49:15 -07:00
Paulus Schoutsen
b18679ec0b Merge pull request #8942 from home-assistant/release-0-51-1
0.51.1
2017-08-12 14:59:38 -07:00
Paulus Schoutsen
7d566c2c3d Version bump to 0.51.1 2017-08-12 14:56:34 -07:00
Paulus Schoutsen
46d9d77d03 Update frontend 2017-08-12 14:56:24 -07:00
Paulus Schoutsen
4a98b32a03 Update frontend 2017-08-12 14:54:50 -07:00
Paulus Schoutsen
adbcbe3a67 Merge pull request #8919 from home-assistant/release-0-51
0.51
2017-08-12 11:31:30 -07:00
Martin Hjelmare
eef3dda1e9 Fix SET_TEMPERATURE_SCHEMA in climate component (#8879)
* Require either temperature or high/low target temperatures.
* Add tests.
2017-08-12 10:57:10 -07:00
William Scanlon
08899ade00 Update python-wink version to fix Dome water valve bug. (#8923) 2017-08-12 10:57:10 -07:00
Philipp Schmitt
5814fdadd0 Update roombapy to 1.3.1 to avoid installing all the mapping dependencies (#8925) 2017-08-12 10:57:09 -07:00
cribbstechnologies
daf7d9ea7f fixing emulated hue issue and testing it (#8928)
* fixing emulated hue issue and testing it

* fixing hound issues

* I should probably stop using vim

* Check against dict directly instead of items.
2017-08-12 10:57:09 -07:00
Martin Hjelmare
956543ae1e Remove not needed call to update (#8930)
* This will ensure no I/O in entity properties.
2017-08-12 10:57:09 -07:00
Martin Hjelmare
fbb6782081 Fix SET_TEMPERATURE_SCHEMA in climate component (#8879)
* Require either temperature or high/low target temperatures.
* Add tests.
2017-08-12 09:39:05 -07:00
cribbstechnologies
369caeedbd fixing emulated hue issue and testing it (#8928)
* fixing emulated hue issue and testing it

* fixing hound issues

* I should probably stop using vim

* Check against dict directly instead of items.
2017-08-12 08:50:02 -07:00
groth-its
489a02b2c2 Fix hue lights for Philips and non-philips lights (#8905) 2017-08-12 08:38:12 -07:00
Fabian Affolter
c4550d02c5 Add version sensor (#8912)
* Add version sensor

* Set version directly

* Rework tests and fix typo

* Remove additional blank line
2017-08-12 08:52:56 +02:00
Martin Hjelmare
49733b7fdf Remove not needed call to update (#8930)
* This will ensure no I/O in entity properties.
2017-08-11 19:55:57 -07:00
Philipp Schmitt
0999e2ddc4 Update roombapy to 1.3.1 to avoid installing all the mapping dependencies (#8925) 2017-08-11 11:22:22 +02:00
William Scanlon
d427063acd Update python-wink version to fix Dome water valve bug. (#8923) 2017-08-11 08:35:45 +02:00
Fabian Affolter
ff3a4637a4 Version bump to 0.52.0.dev0 2017-08-10 23:28:04 +02:00
Fabian Affolter
8523aaca64 Prepare for release 2017-08-10 23:26:19 +02:00
Fabian Affolter
e3236d1a3b Honor PEP8 naming convention (#8909)
* Honor PEP8 naming convention

* Update validator
2017-08-10 19:31:28 +02:00
Marcus Schmidt
d7e8616651 Added possibilities to use template in the command_line sensor (#8505)
* Added possibilities to use template in the command_line sensor

* Minor style guideline conforms

* Minor style guideline conforms

* Added new test for template rendering

* Minor style guideline conforms

* Minor style guideline conforms

* Fixed failing testcases

* Fix style violations

* fix code pretty
2017-08-10 18:52:52 +02:00
Fabian Affolter
c0663bf722 Add Shodan sensor (#8902) 2017-08-10 17:27:49 +02:00
Abílio Costa
d195fd47f7 Add new device tracker for Huawei Routers. (#8488)
* Add new device tracker for Huawei Routers.

	This was tested with the HG8247H model, used by Vodafone
	Portugal for the Fiber service.

* add to .coveragerc; remove import and space

* add comments and fix lint

* rename methods

* huawei_router: add constants to scanner class

* huawei_router: remove lock; use format() in string

* huawei_router: use tupple instead of member only class

* huawei_router: reduce min scan time

* huawei_router: lint

* huawei_router: lint

* huawei_router: add missing lines in imports

* huawei_router: correctly decode string after router firmware update

* Remove things that is done on core now
2017-08-10 17:01:52 +02:00
Erik Eriksson
e84ff61d4a Support media position and media duration (will display progressbar in ui) (#8904) 2017-08-10 16:56:34 +02:00
Anders Melchiorsen
317bc10ccb LIFX: improve performance of multi-light transitions (#8873)
* LIFX: improve performance of multi-light transitions

To avoid hub overload, the light.turn_on call will change each light
sequentially.

As LIFX has no hub we can safely increase performance by starting all
light transitions concurrently.

* Improve state updates after light changes

The light.turn_on call will set a new state and then immediately read it
back. However, reading the state of a LIFX light right after a state
change can still return the old value.

To handle this situation we have previously delayed the update request a
little while to allow a potential state change to settle. Because light
updates are now run in parallel, this delay might be too short when many
lights are set at once.

This commit introduces a per-light Lock to make it explicit when the
state cannot yet be trusted.

We must then do the state update ourselves. This was already done at the
end of a long transition and that code can be reused for also doing the
update at the start of a transition.
2017-08-10 10:29:04 +02:00
William Scanlon
1cb42087f9 Update simplisafe-python version (#8908) 2017-08-10 07:58:39 +02:00
karlkar
b035577cf5 Fix for Neato D3 Connected state obtaining (#8817) 2017-08-09 23:22:08 +02:00
Paulus Schoutsen
55c84eaee3 Update frontend 2017-08-09 00:47:29 -07:00
kfcook
eb6017e16c added support for setting/getting position of lutron caseta covers (#8898) 2017-08-09 06:57:32 +02:00
PhracturedBlue
19ee3c42b6 Add longer text strings to mailbox demo to test string truncation (#8893)
* Add longer text strings to mailbox demo to test string truncation in frontend

* Remove lorem ipsum txt file

* Use format instead of %
2017-08-08 23:37:16 +02:00
Aaron Bach
af70054692 Changed Pi-hole graphs from stacked bar to line (#8896) 2017-08-08 22:57:35 +02:00
Fabian Affolter
be94f6e939 Do not call update() in constructor (#8892) 2017-08-08 22:36:59 +02:00
Fabian Affolter
f513f6271e Do not call update() in constructor (#8878)
* Do not call update() in constructor

* Fix lint issues
2017-08-08 20:21:33 +02:00
Alexey
588b36dff2 Fix media_extractor for some sites (#8887) 2017-08-08 15:21:32 +02:00
Fabian Affolter
cc5893ed8b Upgrade youtube_dl to 2017.8.6 (#8880) 2017-08-08 11:53:19 +02:00
Fabian Affolter
124a6cc8c0 Change level (#8883) 2017-08-08 11:53:04 +02:00
Fabian Affolter
0fe4245620 Allow usage of colorlog 3.0.1 (#8885) 2017-08-08 10:16:04 +02:00
Aaron Bach
289c88ff71 Add RainMachine switch platform (#8827)
* Add RainMachine switch platform

* Updated requirements_all.txt

* Cleaning up CI and coverage results

* Small update to deal with older pylint

* Fixed small indentation-based error

* Added some more defensive try/except logic around calls

* I'm not a fan of importing a library multiple times :)

* Making PR-requested changes

* Fixed ref to positional parameter

* Attempting to fix broken linting

* Ignoring no-value-for-parameter pylint error
2017-08-08 09:49:25 +02:00
Fabian Affolter
57f3bed465 Do not call update() in constructor (#8881) 2017-08-08 06:52:27 +02:00
Oleksii Serdiuk
62e86270e6 RFLink: Add send_command service (#8876)
Add an optional extended description…
2017-08-07 17:37:30 +02:00
Philipp Schmitt
3aceca9d8a Add nuki lock'n'go and unlatch services and add attributes (#8687)
* Add lock'n'go service

* Add unlatch service

* Implement changes requested by @MartinHjelmare

* Fix service domain
2017-08-07 14:58:31 +02:00
Philipp Schmitt
e81b3f7bc0 Implement Roomba fan speed (#8863)
* Implement Roomba fanspeed

* Fix: fan_speed_list is always empty

* Log instead of raising an exception when incorrect fan speed has been provided

* Don't attempt to set any preference if fan speed is invalid
2017-08-06 23:43:33 +02:00
Andy Castille
cc6c2bf25e Fix spelling error and update link (#8869) 2017-08-06 21:18:44 +02:00
Paulus Schoutsen
9575cbde09 Consolidate config panels (#8857)
* Remove automation panel registration

* Move Z-Wave config API to config.zwave

* Remove no longer needed test

* Lint

* Update frontend
2017-08-06 12:05:34 -07:00
Paulus Schoutsen
4e79517971 Update mailbox panel icon 2017-08-06 11:51:58 -07:00
Tim Lyakhovetskiy
4ec4cfc44e Add Leviton Decora Smart WiFi Device Platform (#8529)
* Add Leviton Decora Smart WiFi Device Platform

* Decora WiFi Code Review Fixes
2017-08-06 11:30:28 -07:00
PhracturedBlue
d74f4eaf52 Add Initial Mailbox panel and sensor (#8233)
* Initial implementation of Asterisk Mailbox

* Rework asterisk_mbox handler to avoid using the hass.data hash.  Fix requirements.

* Handle potential asterisk server disconnect.  bump asterisk_mbox requirement to 0.4.0

* Use async method for mp3 fetch from server

* Add http as dependency

* Minor log fix. try to force Travis to rebuild

* Updates based on review

* Fix error handling as per review

* Fix error handling as per review

* Refactor voicemail into mailbox component

* Hide mailbox component from front page

* Add demo for mailbox

* Add tests for mailbox

* Remove asterisk_mbox sensor and replace with a generic mailbox sensor

* Fix linting errors

* Remove mailbox sensor.  Remove demo.mp3.  Split entity from platform object.

* Update mailbox test

* Update mailbox test

* Use events to indicate state change rather than entity last-updated

* Make mailbox platform calls async.  Fix other review concerns

* Rewrite mailbox tests to live at root level and be async.  Fixmailbox dependency on http

* Only store number of messages not content in mailbox entity
2017-08-06 11:19:47 -07:00
Paulus Schoutsen
5696e38dd6 Warn instead of raise on duplicate YAML key (#8834)
* Warn instead of raise on duplicate key

* Update test_yaml.py

* Lint

* Change to error
2017-08-06 10:47:19 -07:00
Eugenio Panadero
c6aaacbb08 Add new service clean_spot to vacuums (#8862)
* Add new service `clean_spot` to vacuums

    - Add as base component service, with associated support flag to make it optional
    - Implement on Demo vacuum
    - Implement on Xiaomi vacuum
    - Update tests for platforms Demo and Xiaomi
    - Change default icon for vacuums to `mdi:roomba`, but keep the one for the Xiaomi
    - (In a polymer PR: add new service to command toolbar in the 'more-info' card)

* Add `clean_spot` service description

* fix default properties for vacuum component
2017-08-06 10:23:22 -07:00
Fabian Affolter
d8ca04a4bc Do not call update() in constructor (#8859) 2017-08-06 10:21:55 -07:00
Paulus Schoutsen
ac9c1235bb Allow get local ip to work without internet (#8855) 2017-08-06 09:15:17 -07:00
Andrey Kupreychik
c49cce7243 Do not use pychromecast.Chromecast for Cast Groups (#8786)
* Do not use pychromecast.Chromecast for Cast Groups

pychromecast.Chromecast creates Chromecast instance with friendly_name and cast_type of the device and not of a group.
Which leads to collisions

* Update cast.py

* using hass.data

* Fixed and extended tests

* Line length in tests

* Lint in tests
2017-08-06 09:15:01 -07:00
John Arild Berentsen
99a20c845c Fix off_delay for zwave trigger sensors (#8864) 2017-08-06 18:31:32 +03:00
Kevin Fronczak
3723f67dc1 Added rounding to Google Wifi (#8866) 2017-08-06 18:29:52 +03:00
Fabian Affolter
b655fe6e04 Allow to set coordinates (#8858) 2017-08-06 15:20:51 +02:00
Fabian Affolter
24e9fa238a Upgrade pyasn1 to 0.3.2 and pyasn1-modules to 0.0.11 (#8856) 2017-08-06 15:20:13 +02:00
Charles Blonde
83afd12807 Add support to Dyson 360 Eye robot vacuum using new vacuum platform (#8852)
* Add support to Dyson 360 Eye robot vacuum using new vacuum platform

* Fix tests with Python 3.5

* Code review

* Code review - v2

* Code review - v3
2017-08-06 13:08:46 +02:00
Philipp Schmitt
82a7dffc03 Wi-Fi enabled Roomba support (#8825)
* Roomba vacuum component

* Update requirements and coveragerc

* Update error handling message

* Implement changes requested by @azogue

* Add missing import

* Don't wrap commands with functools.partial

* Refactoring

* Remove state attribute and use double quotes for log messages strings

* Remove unused constants

* Sorting

* Sorting + remove None arg from dict.get() calls

* Re-sort imports
2017-08-06 11:08:45 +02:00
Fabian Affolter
c11b6798dc Upgrade pylast to 1.9.0 (#8854) 2017-08-06 10:08:45 +02:00
Fabian Affolter
8e4c799ad1 Upgrade sqlalchemy to 1.1.13 (#8850) 2017-08-06 10:08:24 +02:00
Fabian Affolter
5059d4c54b Catch ConnectionRefusedError (#8844)
* Do not call update() in constructor

* Catch ConnectionRefusedError
2017-08-06 10:08:00 +02:00
Fabian Affolter
569d9764ab Do not call update() in constructor (#8847) 2017-08-06 10:07:45 +02:00
Fabian Affolter
058deb5be3 Make 'monitored_conditions' optional (#8848)
* Do not call update() in constructor

* Update tests
2017-08-06 10:07:22 +02:00
Fabian Affolter
cd36a71f64 Do not call update() in constructor (#8849)
* Do not call update() in constructor

* Fix pylint issues
2017-08-06 10:07:05 +02:00
Fabian Affolter
6832a2e642 Make 'monitored_conditions' optional (#8843)
* Do not call update() in constructor

* Make 'monitored_conditions' optional

* Update tests
2017-08-06 10:05:37 +02:00
Fabian Affolter
2c7b2fe19e Do not call update() in constructor (#8840) 2017-08-06 10:03:57 +02:00
Fabian Affolter
45ec7f6180 Upgrade sendgrid to 4.2.1 (#8839) 2017-08-06 10:03:32 +02:00
Fabian Affolter
cb8517834a Do not call update() in constructor. (#8837) 2017-08-06 10:03:09 +02:00
Jeroen ter Heerdt
f41ef5d727 Egardia (#8389)
* Added support for Egardia / Woonveilig alarm control panel

* Added support for Egardia / Woonveilig alarm control panel

* Added support for Egardia / Woonveilig alarms

* Updating egardia support with exception handling and other fixes

* Egardia platform, requirements file updated

* Fixing state checking

* Adding exception handling

* Removing unnecessary logging

* Removing unnecessary logging

* Updating to egardiadevice component 1.0.10

* Improving exception handling

* Adding implementation of egardiaserver for alarm triggered status

* Clean-up

* Fix my previous change
2017-08-05 22:04:00 +02:00
Eugenio Panadero
a221b10694 Update xiaomi vacuum tests and include in coverage (#8845)
* Fix tests for Demo vacuum platform (and increase coverage)

* increase coverage of xiaomi vacuum tests and include in coverage

Also little fixes

* remove print statement
2017-08-05 21:45:59 +02:00
Greg Laabs
6e1785173f History query and schema optimizations for huge performance boost (#8748)
* Add DEBUG-level log for db row to native object conversion

This is now the bottleneck (by a large margin) for big history queries, so I'm leaving this log feature in to help diagnose users with a slow history page

* Rewrite of the "first synthetic datapoint" query for multiple entities

The old method was written in a manner that prevented an index from being used in the inner-most GROUP BY statement, causing massive performance issues especially when querying for a large time period.

The new query does have one material change that will cause it to return different results than before: instead of using max(state_id) to get the latest entry, we now get the max(last_updated). This is more appropriate (primary key should not be assumed to be in order of event firing) and allows an index to be used on the inner-most query. I added another JOIN layer to account for cases where there are two entries on the exact same `last_created` for a given entity. In this case we do use `state_id` as a tiebreaker.

For performance reasons the domain filters were also moved to the outermost query, as it's way more efficient to do it there than on the innermost query as before (due to indexing with GROUP BY problems)

The result is a query that only needs to do a filesort on the final result set, which will only be as many rows as there are entities.

* Remove the ORDER BY entity_id when fetching states, and add logging

Having this ORDER BY in the query prevents it from using an index due to the range filter, so it has been removed.

We already do a `groupby` in the `states_to_json` method which accomplishes exactly what the ORDER BY in the query was trying to do anyway, so this change causes no functional difference.

Also added DEBUG-level logging to allow diagnosing a user's slow history page.

* Add DEBUG-level logging for the synthetic-first-datapoint query

For diagnosing a user's slow history page

* Missed a couple instances of `created` that should be `last_updated`

* Remove `entity_id` sorting from state_changes; match significant_update

This is the same change as 09b3498f410106881fc5e095c49a8d527fa89644 , but applied to the `state_changes_during_period` method which I missed before. This should give the same performance boost to the history sensor component!

* Bugfix in History query used for History Sensor

The date filter was using a different column for the upper and lower bounds. It would work, but it would be slow!

* Update Recorder purge script to use more appropriate columns

Two reasons: 1. the `created` column's meaning is fairly arbitrary and does not represent when an event or state change actually ocurred. It seems more correct to purge based on the event date than the time the database row was written.
2. The new columns are indexed, which will speed up this purge script by orders of magnitude

* Updating db model to match new query optimizations

A few things here: 1. New schema version with a new index and several removed indexes
2. A new method in the migration script to drop old indexes
3. Added an INFO-level log message when a new index will be added, as this can take quite some time on a Raspberry Pi
2017-08-04 23:16:53 -07:00
Fabian Affolter
52cff83267 Upgrade aiohttp to 2.2.5 (#8828) 2017-08-04 23:14:05 -07:00
Paulus Schoutsen
e49b970665 Block dependencies that depend on enum34 (#8698)
* Block dependencies that depend on enum34

* Remove uninstalling enum34

* Update validation script

* Add constraints to tox.ini

* Upgrade yeelight to version that uses enum-compat

* Disable sensor.skybeacon

* Lint
2017-08-04 23:06:10 -07:00
Paulus Schoutsen
a0530d8b9c Update frontend 2 2017-08-04 23:02:07 -07:00
Paulus Schoutsen
99d4021f47 Update frontend 2017-08-04 22:58:19 -07:00
Charles Blonde
7f0d0607f1 Fix Dyson sensors if devices are configured without standby monitoring. Fixes #8569 (#8826)
Upgrade libpurecoolink libraries without unused enum34 dependency
2017-08-04 14:27:23 -07:00
Hellowlol
cf298c2435 Make HA discover sabnzbd and add it to the Configurator (#8634)
* Init discover sab.

* Fix hound errors

Nobody likes being hound at :(

* sabnzbd discovery says if ssl is active.

* Fixups after codereview.
2017-08-04 23:24:55 +02:00
Boyi C
77cdc833f0 Update yweather.py (#8820)
Fix missing weather unit support.
Move some weather code to their correct classes.
2017-08-04 17:22:38 +02:00
Eugenio Panadero
96f8c37dcd Xiaomi vacuum as platform of new vacuum component derived from ToggleEntity, and services (#8623)
* Xiaomi vacuum as component with switch, sensors and services

- Conversion from switch platform to async component.
- Add services proposed in #8416 to the new component, with shorter names.
- Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating.
- Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!)

* path change in requirements_all (from switch platform to component)

* copy pasting is a bad habit

* services to the components services.yaml, modify .coveragerc

* review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services

* `icon_for_battery_level` util method

* Xiaomi vacuum as platform of new component vacuum

- Created new component `vacuum` from a ToggleEntity.
- Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`.
- Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch).
- Add `support flags` for the common services
- Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!)
- Move services descriptions to a yaml file for the new component.
- Update requirements_all.
- Update coveragerc.

* fix coveragerc

* fix battery icon helper to use more icons

* remove sensors, create properties and support flags for custom UI

* cleaning

* updated state_attrs for filtering in UI, renamed platform to simply `xiaomi`

* fix platform rename

* change fanspeed and expose `fanspeed_list` to use speed steps

* minor fixes

- Rename service `start_pause`
- Add 'Error' attribute only if `got_error`.
- Minor changes

* rename state attrs

* rename state attrs

* review changes: cut fan__speed, style changes, remove logging, and more

* add ATTR_COMMAND = 'command' to const

* pop entity_id from service data

* remove property accessor for vacuum object

* lint fix

* fix extra attrs names

* module level functions for calling the services

* params as optional keyword for `send_command`

* params as optional keyword for `send_command`, remove debug logs

* explicit parameters for `set_fan_speed` and `send_command`

* Demo platform for the vacuum component

* vacuum tests for the Demo platform

* some fixes

* don't omit vacuum

* vacuum tests for the Xiaomi platform

* fix test

* fix

* fix xiaomi test

* fix coveragerc

* test send command

* fix coveragerc

* fix string formatting

* The coverage is to low. It need 93% or more
2017-08-04 15:27:10 +02:00
Julian Kahnert
5b4e30cde3 geizhals sensor component (#8458)
* initial create of the geizhals component

* only .coveragerc, geizhals.py, and requirements_all.txt included
2017-08-04 12:11:33 +02:00
Paulus Schoutsen
d4dfb4d80c Polymer 2 (#8815)
* Update build for Polymer 2

* Update webcomponents polyfills/helpers

* Load ES5 class adapter when not in dev mode

* Update frontend
2017-08-03 23:46:57 -07:00
Andrey
c895f1f1db When Sonos gets a tts source - dont't show an image (#8777) 2017-08-03 17:39:11 +03:00
Haim Gelfenbeyn
944af9cd7d InfluxDB component improvements (#8633)
* Allow reporting some state attributes as tags to InfluxDB

Some state attributes should really be tags in InfluxDB. E.g.
it is helpful to be able to group by friendly_name, or add a custom
attribute like "location" and group by that. Graphs in Grafana are much
easier to read when friendly names are used, and not node ids.

This commit adds an optional setting to InfluxDB config:
'tags_attributes'. Any attribute on this list will be reported as tag
and not as field to InfluxDB.

* Allow overriding InfluxDB measurement for each reported item separately

Bundling all items with the same "unit of measurement" together does not
always makes sense. For example, both "relatively humidity" and "battery
level" are reported as "%", but I'd rather see them as separate
measurements in InfluxDB. This commit allows for 'influxdb_measurement'
attribute. When set on node, it will take precedence over the global
'override_measurement' and component-specific 'unit_of_measurement'.

* Minor updates to InfluxDB component improvements, as suggested by
@MartinHjelmare.

* Moved per-component config from 'customize' into 'influxdb'
configuration section. The following three sub-sections were added:
'component_config', 'component_config_domain' and
'component_config_glob'. The sole supported per-component attribute
at this point is 'override_measurement'.

* Lint

* Fixed mocked entity_ids in InfluxDB tests to be in domain.entity_id
format, to satisfy EntityValues requirements.

* Added tests for new InfluxDB configuration parameters

* Fixes to some docstrings
2017-08-03 16:26:01 +02:00
John Mihalic
f3e16ca304 Catch divide by zero errors when a sleep type is 0 (#8809)
Add an optional extended description…
2017-08-03 15:58:40 +02:00
Fabian Affolter
6de38cb941 Upgrade aiohttp to 2.2.4 (#8805) 2017-08-03 11:37:02 +02:00
Pascal Vizeli
8e51e66c9b Update numpy 1.13.1 (#8806)
* Update opencv.py

* Update requirements_all.txt
2017-08-03 11:36:50 +02:00
Matthew Treinish
57dfe378a1 Add mochad light component (#8476)
* Add mochad light component

This commit adds a new component to control x10 dimmers/lights with
mochad.

* Create comm_type and address constants

The comm_type and address conf constants are shared between all mochad
devices because they are required information used for configuring a
device. This commit moves the definition into const.py so they're
consistent between all component types.
2017-08-03 10:51:01 +02:00
Fabian Affolter
d8cded637c Revert "Upgrade aiohttp to 2.2.4"
This reverts commit 7c92f7e1ad.
2017-08-03 10:11:32 +02:00
Fabian Affolter
7c92f7e1ad Upgrade aiohttp to 2.2.4 2017-08-03 10:08:09 +02:00
Abílio Costa
ccf0559059 mqtt switch: add voluptuous for availability topic (#8797) 2017-08-03 07:18:18 +02:00
Paulus Schoutsen
2d38e70268 Merge branch 'polymer-build' into dev 2017-08-02 21:34:20 -07:00
Paulus Schoutsen
9dae1ca5c2 Update frontend 2017-08-02 21:34:04 -07:00
Luuk
6ac8caa857 Fix referencing unset variable in tado climate component (causes update to fail when tado zone is in manual mode) (#8723)
Add an optional extended description…
2017-08-02 15:07:03 +02:00
Fabian Affolter
39131d06ba Improvements (configuration and validation) (#8785) 2017-08-02 14:51:09 +02:00
Fabian Affolter
8a626e1572 Upgrade sphinx-autodoc-typehints to 1.2.1 (#8783)
Add an optional extended description…
2017-08-02 14:15:00 +02:00
Fabian Affolter
bc376f7045 Upgrade pyasn1 to 0.3.1 and pyasn1-modules to 0.0.10 (#8787) 2017-08-02 14:14:01 +02:00
Paulus Schoutsen
cad1de790e Build frontend with polymer-build 2017-08-02 01:46:08 -07:00
Sebastian Muszynski
32b7f4d16f Fixes UnboundLocalError: local variable 'setting' referenced before assignment (#8782) 2017-08-02 09:14:28 +02:00
Steve Rhoades
1adb5040e7 Feature alexa launch request (#8730)
* Add support for LaunchRequest alexa intent

* Support LaunchRequest for multiple skills

* formatting

* adding tests to cover launch request

* formatting
2017-08-01 22:53:36 -07:00
Lukas Barth
47dad547eb Add 'forecast' ability to yr weather sensor (#8650)
* Add forecast option to YR sensor

* Fix some style issues

* Fix linting
2017-08-01 22:42:51 -07:00
thrawnarn
86c06ad76e New component: bluesound (#7192)
* New component: bluesound

* New component: bluesound

* Removed response.release()
Fixed update_sync_status bug
Changed should_poll to True

* Fix lint error

* Changes to init

* Fixed blank line

* updated requirements

* bump to xmltodict 0.11.0
2017-08-01 22:41:51 -07:00
pezinek
7dbcf63543 flux_led: support for property "available" (#8764)
* flux_led: support for property "available"

* Implemented changes from code review

* Implemented changes from code review

* Implemented changes from code review
2017-08-01 21:26:27 -07:00
Thomas Friedel
6ff340492b use updated osram lightify 1.0.6 component, including bugfix allowing more than 27 devices (#8774) 2017-08-01 20:36:31 +02:00
Fabian Affolter
50cd6c9a9c Catch exception (fixes #8724) (#8731) 2017-08-01 19:30:26 +02:00
Dan Sarginson
365f21b209 Honeywell fixes and improvements (#8756)
* Honeywell fixes and improvements

Give the Honeywell device a state ('On', 'Off', etc) that
can be displayed to user and understood by other components.
Previously this was always 'Unknown'. Update also raises a
state_changed event when a new temperature is polled.

These two together fix an issue (#8688) where Honeywell
climate data couldn't be logged in InfluxDB.

* Roll back some changes

These were not necessary to achieve the result I wanted.

* Renamed RoundThermostat's 'device' member for greater clarity

Now called 'client'

* Improve and simplify discovering thermostat mode

Per code review, this is a rather neater way to discover the thermostat mode

* Update tests for compatibility with new component

The tests previously relied upon the update() method being
called in the constructor. This is no longer the case.

* Address formatting review comment

Parens not necessary

* This system mode is not certain to apply to domestic hot water

Moved the mode lookup to only happen on update of radiator devices,
since hot water devices seem to be treated differently and I can't test.
2017-08-01 16:18:14 +02:00
Tsvi Mostovicz
075422e7ad Add support for file attachments in pushbullet (#8763)
* Add support for file attachments in pishbullet

* Check filepath is allowed
2017-08-01 14:55:46 +02:00
Steven Looman
342ec8ec99 mpd improvements (#8655)
* Don't require the MPD device to online during HASS startup

* Hide private variables

* Keep tox/flake8 happy

* Fix typo

* Force direct update

* Implement MpdDevice.available

* Fix typo
2017-07-31 23:18:26 -07:00
Matt Colyer
2b59b917c4 Allow sonos to select playlists as a source (#8258)
* Allow sonos to select playlists as a source

Most of this was taken from
https://github.com/home-assistant/home-assistant/issues/5598#issuecomment-278229895
however I made a few small improvements so that it works for other
services than Spotify and it should properly switch to playing the queue
if you had another song playing previously.

/cc @PatBoud

* Attempt to fix style issues

* More indent changes

* Fix misplaced period

* Move playlist replacement to function

* Privatize replace_queue_with_playlist and explain

* Remove unneeded decorator

* Fix doc formatting
2017-07-31 23:16:05 -07:00
viswa-swami
e40388e7ad Enable/Disable Motion detection for Foscam Cameras (#8582)
* Added support to enable/disable motion detection for foscam cameras. This support was added in 0.48.1 as a generic service for cameras. Motion detection can be enabled/disabled for foscam cameras with this code-set.

* Fixed the violation identified by hound-bot

* Fixed the comment posted by HoundCI-Bot regarding using imperative mood statement for pydocstyle

* Fixed the error that travis-ci bot found.

* As per comment from @balloob, Instead of directly using the URL to talk to foscam, used a 3rd party foscam library to communicate with it. This library already has support to enable/disable motion detection and also APIs to change the motion detection schedule etc. Need to add more support in the pyfoscam 3rd party library for checking if motion was detected or even if sound was detected. Once that is done, we can add that into HASS as well.

* Lint

* Removed the requests library import which is not used anymore

* Updating requirements_all.txt based on the code-base of home assistant that i have. Generated using the gen_requirements_all.py script

* Updating requirements_all.txt and requirements_test_all.txt generated by gen_requirements_all.py after latest pull from origin/dev

* Updated requirements_all.txt with script

* Updated the foscam camera code to fix lint errors

* Fixed houndci violation
2017-07-31 23:14:34 -07:00
William Scanlon
cb292a0b18 Wink discovery (#8739)
* Support for Wink discovery

* Switched try/except for if/else
2017-07-31 20:54:07 -07:00
Martin Hjelmare
33663f9502 Clean up remote component (#8728)
* Clean up remote component

* Don't have device be required in send_command service and method.
* Don't have entity_id be required in the base service schema.
* Don't always add activity in the data dict for a service call.
* Update harmony remote platform according to new service schema.
* Remove not needed properties and attributes from the Kira remote
  platform.
* Add send_command method to demo platform.
* Add tests and remove duplicate tests.

* Break out required argument as positional argument
2017-07-31 20:52:39 -07:00
emlt
e57d6f679a Change units from KW to W (#8761)
* Change units from KW to W

Change power unit from KW to W to be consistent with other energy sensors.

* Change units from kW to W
2017-07-31 20:41:45 -07:00
mjj4791
775185896a buienradar dates tz-aware (#8767) 2017-07-31 20:37:33 -07:00
Paulus Schoutsen
e6331aafb2 Merge remote-tracking branch 'origin/master' into dev 2017-07-31 18:30:40 -07:00
Paulus Schoutsen
fbb4c43353 Merge pull request #8757 from home-assistant/release-0-50-2
0.50.2
2017-07-31 18:28:14 -07:00
gwhiteCL
455ac9724a added invert_state optional parameter (#8695)
* added invert_state optional parameter

* removed superfluous parens

* moved state inversion to the is_closed method

* added relay_invert feature

* fixed syntax to comply with houndci-bot rules

* changed state_invert to invert_state and relay_invert to invert_relay
2017-07-31 20:24:21 -04:00
Alan Fischer
e6be560e00 Add toggle to remotes (#8483)
* Add toggle to remotes

* Only include activity if specified, and add service description
2017-07-31 19:46:12 +02:00
Adam Mills
59891fa838 Fix Z-Wave barrier discovery for new API (#8706) 2017-07-31 09:15:10 -07:00
Adam Mills
475ab68853 Correctly discover GE Fan Controllers (#8682) 2017-07-31 09:15:10 -07:00
Paulus Schoutsen
d3f8ad15a4 Version bump to 0.50.2 2017-07-31 09:14:07 -07:00
Sean Gollschewsky
c45fc84859 Move I/O outside of properties for light/tplink platform (#8699)
* Add new component for TPLink light bulbs.

* Update with result of gen_requirements_all.

* Add new component light.tplink.

* Move I/O outside of properties as per https://goo.gl/Nvioub.
2017-07-31 09:10:04 -07:00
Adam Mills
2a09ac017f Fix Kodi reconnection after websocket disconnect (#8704) 2017-07-31 09:10:04 -07:00
Paulus Schoutsen
f9e8d4237d Fix alexa cards (#8708) 2017-07-31 09:10:04 -07:00
Kevin Fronczak
30e16c97fc Fixed sensor issue with Google Wifi routers in bridge mode (#8710)
* Fixed issue with routers in bridge mode

- Router in brdige mode apparently don't report all of the stats
- Re-wrote the data_format function so it's a bit easier to follow and able to log keys that aren't supported by a router in a given mode
- Changed config so that it properly ignores conditions when not explicitly listed
- Added tests to check for the above and also to verify we log that a key doesn't exist rather than throwing an exception

* Mistakenly was calling MONITORED_CONDITIONS in data_format

- Changed to be the actual config values to prevent log error
2017-07-31 09:10:04 -07:00
Eugenio Panadero
0e1f664102 Retry set_webhook up to three times, reduce timeout to 5s again (#8716) 2017-07-31 09:10:04 -07:00
Fabian Affolter
60ca79ce35 Supress exception if host is not available (fixes #8684) (#8732) 2017-07-31 09:10:04 -07:00
Martin Hjelmare
f576b37e9f Fix tradfri error spam (#8738)
* Catch tradfri timout exception

* Remove not needed return statement

* Remove test logging

* Log warning instead of error
2017-07-31 09:10:04 -07:00
Nathan Henrie
592f9901f9 Fix typo (#8754) 2017-07-31 09:10:04 -07:00
Sean Gollschewsky
7991e2df5f Fix brightness issue #8744. (#8755) 2017-07-31 09:10:04 -07:00
Paulus Schoutsen
91b062f9b7 Update frontend 2017-07-31 09:06:50 -07:00
Sean Gollschewsky
9919eec596 Fix brightness issue #8744. (#8755) 2017-07-31 09:02:04 -07:00
Nathan Henrie
e525d13a5d Fix typo (#8754) 2017-07-31 09:00:09 -07:00
Martin Hjelmare
ce67be2fff Fix tradfri error spam (#8738)
* Catch tradfri timout exception

* Remove not needed return statement

* Remove test logging

* Log warning instead of error
2017-07-31 08:58:47 -07:00
Fabian Affolter
53048f71a0 Supress exception if host is not available (fixes #8684) (#8732) 2017-07-31 08:58:13 -07:00
Martin Donlon
164e953e8c New media_player platform for Russound devices using the RIO protocol (#8448)
* New media_player platform for Russound devices using the RIO protocol
Auto discovers zones and sources
Handles media metadata from sources that support it
asyncio implementation
Push updates for any zone or source changes so no polling required.

* Fixed up linting issues

* Addressing PR feedback

Updated russound_rio dependency to 0.1.3
Use enumerate_zones and enumerate_sources methods instead of doing it in
the platform.
Register callbacks in async_added_to_hass coroutine
Corrected behavior of async methods
2017-07-31 14:42:55 +02:00
David McNett
7156e4782e python-insteonplm module version bump (#8736)
Requiring python-insteonplm v0.7.5 (up from 0.7.4) now
2017-07-31 14:33:51 +02:00
Eugenio Panadero
37fef4016e Add proxy support for telegram_bot (#8717)
* Add proxy support for telegram_bot

New optional config parameters `proxy_url` and `proxy_params` (a dict)
```yaml
telegram_bot:
  platform: polling
  api_key: !secret telegram_bot_api_key
  allowed_chat_ids:
    - !secret telegram_bot_chatid
  proxy_url: socks5://proxy_ip:proxy_port
  proxy_params:
    username: my-username
password: my-secret-password
```

* change `ATTR_` for `CONF_` for config params
2017-07-30 12:08:19 +02:00
Eugenio Panadero
cee49f313f Retry set_webhook up to three times, reduce timeout to 5s again (#8716) 2017-07-30 11:14:28 +02:00
Eugenio Panadero
05330ac763 bump python-telegram-bot to 7.0.1 for fully support Bot API 3.2 (#8715) 2017-07-30 11:13:51 +02:00
Nicholas Sielicki
6884965c80 directv: add configuration glue for Genie slaves (#8713)
DirectPy, the third party library used for controlling directv boxes,
has the ability to accept an ID in order to act as a remote for Genie
slaves instead of just the master directv box. This commit adds glue
such that one can configure home assistant to interface with these slave
genie boxes.

Signed-off-by: Nicholas Sielicki <sielicki@yandex.com>
2017-07-30 10:17:56 +02:00
Adam Mills
e992527c68 Fix Kodi reconnection after websocket disconnect (#8704) 2017-07-29 21:55:08 -07:00
Sean Gollschewsky
431a381c8d Move I/O outside of properties for light/tplink platform (#8699)
* Add new component for TPLink light bulbs.

* Update with result of gen_requirements_all.

* Add new component light.tplink.

* Move I/O outside of properties as per https://goo.gl/Nvioub.
2017-07-29 21:53:37 -07:00
Paulus Schoutsen
22088d192a Fix alexa cards (#8708) 2017-07-29 21:52:26 -07:00
Kevin Fronczak
418a8bab11 Fixed sensor issue with Google Wifi routers in bridge mode (#8710)
* Fixed issue with routers in bridge mode

- Router in brdige mode apparently don't report all of the stats
- Re-wrote the data_format function so it's a bit easier to follow and able to log keys that aren't supported by a router in a given mode
- Changed config so that it properly ignores conditions when not explicitly listed
- Added tests to check for the above and also to verify we log that a key doesn't exist rather than throwing an exception

* Mistakenly was calling MONITORED_CONDITIONS in data_format

- Changed to be the actual config values to prevent log error
2017-07-29 21:50:02 -07:00
Adam Mills
a94e7ec25d Fix Z-Wave barrier discovery for new API (#8706) 2017-07-30 00:40:56 -04:00
Adam Mills
8ac63fd70c Remove deprecated sensor_class config options (#8702) 2017-07-29 19:46:27 -04:00
Adam Mills
8ba9e8016b Remove deprecated substitute interfaces (#8701) 2017-07-29 19:18:06 -04:00
Adam Mills
750ea44b4b Remove deprecated host and ssl logic from Kodi (#8700) 2017-07-29 19:17:41 -04:00
Paulus Schoutsen
5876d6766d Version bump to 0.50.1 2017-07-29 15:11:45 -07:00
Paulus Schoutsen
78428b0acd Version bump to 0.51.0.dev0 2017-07-29 13:33:52 -07:00
Paulus Schoutsen
72db28abac Merge remote-tracking branch 'origin/master' into dev 2017-07-29 13:33:15 -07:00
Paulus Schoutsen
e13fd05e7d Merge pull request #8685 from home-assistant/release-0-50
0.50
2017-07-29 13:28:22 -07:00
Adam Mills
80ab02c3e8 Correctly discover GE Fan Controllers (#8682) 2017-07-29 16:24:15 -04:00
Paulus Schoutsen
a760673ad6 Persist shopping list + clear completed (#8697) 2017-07-29 12:22:52 -07:00
Paulus Schoutsen
0bde0a6f3a Persist shopping list + clear completed (#8697) 2017-07-29 12:22:38 -07:00
Paulus Schoutsen
12dec93565 Update frontend 2017-07-29 12:18:50 -07:00
William Scanlon
c376bc2e45 Support for Wink local control (#8607)
* Support for Wink local control
2017-07-29 10:50:56 -07:00
Paulus Schoutsen
f0e5f68865 Shopping List: edit name / complete status (#8666)
* Shopping List: edit name / complete status

* Change ID to be UUID based
2017-07-29 10:50:56 -07:00
Paulus Schoutsen
56f4486e0b Update frontend 2017-07-29 10:45:14 -07:00
kfcook
d1b73a96f4 Added Lutron Caseta Scene Support (#8690)
* added scene support

* Updated pylutron_caseta version number

* Updated pylutron_caseta version number

* Fixed lint errors

* fix style
2017-07-29 18:07:28 +02:00
Paulus Schoutsen
1749859cdf Shopping List: edit name / complete status (#8666)
* Shopping List: edit name / complete status

* Change ID to be UUID based
2017-07-29 17:48:09 +02:00
Fabian Affolter
931f4d8161 Upgrade mypy to 0.521 (#8692) 2017-07-28 23:22:56 -07:00
Fabian Affolter
61508deed3 Upgrade pushbullet.py to 0.11.0 (#8691)
* Upgrade pushbullet.py to 0.11.0

* Update sensor as well
2017-07-28 23:22:35 -07:00
Paulus Schoutsen
828c469ef7 Fix Lint 2017-07-28 20:53:15 -07:00
William Scanlon
1b57566e8e Support for Wink local control (#8607)
* Support for Wink local control
2017-07-28 12:02:16 -04:00
Paulus Schoutsen
0a6d519b9d Merge remote-tracking branch 'origin/master' into release-0-50 2017-07-28 02:42:58 -07:00
Paulus Schoutsen
0c97fe7eac Version bump to 0.50 2017-07-28 02:38:06 -07:00
Chris
e8ce41874c Fix COMMAND_CLASS_BARRIER_OPERATOR for dev branch of OpenZwave (#8574)
* Update zwave.py to work with updated OpenZwave library

Update zwave.py to work with updated OpenZwave library

* Update zwave.py

* Update zwave.py

* Update to fix garage door openers

Update to fix garage door support for latest version of openzwavelib

* Update to cover.zwave list of states

Update to cover.zwave to provide list of states based on dev version of
openzwave lib

* Some values not saved

* Formatting fix

* Formatting fix

* Variable typo

* Formatting fix

* Formatting

* Variable Update

Variable Update and properties added

* Formatting fixes

* Formatting Fix

* Update test case for door states

* Formatting / Testing process fix

* Formatting

* Formatting / Test Fixes

* Variable rename

* Added members to CoverDevice

* Removed un-needed else

* Formatting

* Formatting

* Variable name changes and const updates

* Changed variable names to cover_state
* Added constains into const.py
* Updated to change the main state on the cover device

* Fixes

* Formatting fixes

* Formatting/Variables

* Formatting

* Variable fixes

* Import update

* Formatting  / Variables

* Update test for new states

* Revert state changes

* Test fix

* Variable Fix

* Formatting

* Variable typo

* Missing constant

* Variable fix

* Requested changes

* Added is_opening
* Added is_closing
* Updated test based on changes

* Formatting

* Changed cover_state back to _state

* Formatting and variable fixes

* Test fixes

* Formatting and variable touchup

* Formatting

* Optimizations

* Add new cover features to demo

* Add tests for demo cover closing/opening

* Remove unused STATE_STOPPED

* Add tests for new zwave cover values
2017-07-27 18:57:30 -04:00
Brian Gehrich
0ab0e35d59 Updated pysnmp to 4.3.9 (#8675) 2017-07-27 22:33:17 +02:00
Pascal Vizeli
51108b8fe9 Hass.io: logo support / timeout handling (#8668)
* Disable auth on logo / no timeout for addons update/install

* fix tests
2017-07-27 12:23:22 -07:00
Daniel Perna
9e6817b6d0 Upgrade pyhomematic to 0.1.30 (#8673) 2017-07-27 17:45:59 +02:00
Fabian Affolter
74581b57f8 Upgrade sqlalchemy to 1.1.12 (#8669) 2017-07-27 17:23:51 +02:00
Fabian Affolter
4fcaea23a8 Upgrade libnacl to 1.5.2 (#8670) 2017-07-27 17:23:07 +02:00
Fabian Affolter
b59c29943b Upgrade fuzzywuzzy to 0.15.1 (#8671) 2017-07-27 17:22:40 +02:00
Richard Cox
1e8c00ac02 Adding support for mapping keys to value in statsd (#8665)
* Adding ability to map specific states to values

* Adding test for mapping
2017-07-27 08:58:34 +02:00
Abílio Costa
9d5c61b2f0 MQTT Switch: add availability_topic for online/offline status (#8593)
* mqtt switch: add availability_topic for online/offline status

* fix method doc strings

* MQTT Switch: add test
2017-07-27 08:24:15 +02:00
kfcook
f5eeb252a7 Added support for SerenaHoneycombShades to Lutron Caseta (#8662)
Add an optional extended description…
2017-07-27 08:14:12 +02:00
Robin
3b4ea864a1 Add uk_transport component. (#8600) 2017-07-26 20:49:52 +01:00
Jeff Wilson
3318f02664 Add transition support to light.zha (#8548)
* Add transition support to light.zha

* Address hound formatting

* Address hound comments

Look, nobody is perfect... alright?

* Update zha.py
2017-07-26 17:22:31 +02:00
Greg Laabs
438edc5ca1 History performance improvements for single-entity requests (#8632)
* Bugfix: remove superfluous domain filter

This filter is already applied later in the function by the `filters` object, where it is conditionally applied when appropriate. This fixes the problem where we get a domain filter even when searching for a single entity_id, which needlessly harms the query's performance.

* Performance: build different query when only getting single entity

When querying the history of a single entity, we can use an entirely different method for the "synthetic zero data point" by simply sorting by date and doing a LIMIT 1. This performs thousands of times better than the multi-entity query when the current recorder_run has been going for a while.

* Add entity_id filter to single-entity request

The entity_id filter was handled inside the `filters.apply` logic which is used in most cases, BUT didn't work when no `filters` was passed in to the method. Now it'll work even if no `filters` object is passed in.

* Fix linting errors in history.py

* Undo removal of domain filter

Putting back the domain filter that was removed in 76a6371705dcd57483e55dcc03435ae867c184d2 - there are use-cases where get_states is called without a filter object, so we need the domain filter to work in those cases as well.

* Fix truncated comment
2017-07-26 11:22:01 -04:00
Boyi C
abcfcdd887 Yahoo Weather update, supports forecast for more days (#8626)
* work on weather panel

* update yahooweather with more forecast details

* Update yweather to allow user input forecast date

* fix for houndci

* fix long line

* fix1

* Revert "work on weather panel"

This reverts commit 28b4972233de42617fb05df574de22743604edfd.

revert unintentional submodule change

* fix2

fix typo, add try catch to another int()

* fix pylint

* fix3

* fix4

* Update yweather.py

* Update yweather.py

* Remove global data construct

* Yahoo API support only 5 days forecast

* remove forecast

* fix lint

* fix lint p2

* Update yweather.py
2017-07-26 16:46:21 +02:00
Thomas Delaet
fff269e790 Velbus (#8076)
* add Velbus changes

* update library version

* fix python-velbus version

* bug fix and update python-velbus

* change config handling

* update velbus components/platforms

* add support for Velbus switches

* fix bugs

* typo

* add velbus fan

* update velbus library

* bug fix in logic of fan handling of speed settings

* add Velbus changes

change config handling

update velbus components/platforms

add support for Velbus switches

add velbus fan

* remove duplicate entry

* fix documentation links

* fix linting error

* regen requirements_all.txt

* add support for Velbus cover

* bugfix in cover component

* bugfix in cover component

* remove unused imports

* Travis fixes

* fix style

* fix style

* Update velbus.py

* Update velbus.py

* Update velbus.py

* Update requirements_all.txt

* Update velbus.py

* Update velbus.py

* Update velbus.py

* Update velbus.py

* fix style

* Update velbus.py

* Update velbus.py

* Update velbus.py

* Update velbus.py

* Update velbus.py

* Update velbus.py
2017-07-26 14:03:29 +02:00
Anders Melchiorsen
81a27e726c Upgrade aiolifx (#8648)
This release includes a fix for multizone lights with zone counts that are not
a multiple of eight.
2017-07-26 12:33:00 +02:00
Marcelo Moreira de Mello
7c120748ce Fixes Fitbit sensor to report battery level with the expected device (#8647)
Add an optional extended description…
2017-07-26 11:05:48 +02:00
Sean Gollschewsky
e83816c055 Add component Light TPLink (#8643)
* Add new component for TPLink light bulbs.

* Update with result of gen_requirements_all.

* Add new component light.tplink.
2017-07-26 09:04:40 +02:00
Paulus Schoutsen
cd2703e121 Update dependencies cast + discovery (#8646) 2017-07-25 23:35:05 -07:00
Paulus Schoutsen
c2828bac2c Tweak conversation/intent/shopping list (#8636) 2017-07-25 00:42:59 -07:00
Paulus Schoutsen
ad7370e1c2 Update frontend 2017-07-25 00:29:05 -07:00
Adam Mills
3b7f16f189 Catch and log Lyft API errors (#8635) 2017-07-25 00:05:47 -04:00
Colin O'Dell
cc03f7ee6a Manual alarm with MQTT control (#8257)
* Manual alarm with MQTT control

* Duplicate manual control panel code instead of extending it

* Duplicate manual alarm test as well; modify for manual_mqtt

* Add MQTT-specific tests for manual_mqtt alarm
2017-07-24 09:06:38 -07:00
Corey Pauley
ecc1429453 Added support for default value when environment variable is missing (#8484)
* Added support for a default value when an environment variable is missing

* Shouldn't have used docstring
2017-07-24 09:00:01 -07:00
Justin Dray
98568b5eb7 Add support for using credstash as a secret store (#8494) 2017-07-24 08:59:10 -07:00
Paulus Schoutsen
9d9ca64f26 Update README.rst 2017-07-24 07:53:14 -07:00
Paulus Schoutsen
1d31137616 Update README.rst 2017-07-24 07:47:57 -07:00
Pascal Vizeli
f86bd15580 Cleanup old device_tracker stuff (#8627)
* Cleanup old device_tracker stuff

* Fix lint
2017-07-24 07:45:02 -07:00
Paulus Schoutsen
cbf65220aa Merge pull request #8625 from home-assistant/release-0-49-1
0.49.1
2017-07-24 07:35:29 -07:00
Daniel Høyer Iversen
c100b8cb52 Add is_lighting4 to RfxtrxBinarySensor (#8563) 2017-07-24 07:33:37 -07:00
matt2005
654ad41464 added onvif camera fix for non-virtual env installations (#8592)
* added fix for non-virtual env installations

* fixed new line at end of file

* moved import os to top of file
2017-07-24 15:22:12 +02:00
Paulus Schoutsen
a2abb4ae0a Update frontend 2017-07-24 00:11:58 -07:00
Jeff Wilson
36e266442f Properly slugify switch.flux update service name (#8545) 2017-07-23 23:53:21 -07:00
Jeff Wilson
f3d9086ff4 Properly slugify switch.flux update service name (#8545) 2017-07-23 23:53:03 -07:00
Anton Lundin
0c09cfc6c4 ubus: Make multiple instances work again (#8571)
Back in "ubus: Refresh session on Access denied (#8111)" I added the
decorator _refresh_on_acccess_denied. Somehow that stopped multiple ubus
trackers from working in parallel, and only the one first init'ed
worked.

Changing the order of the decorators fixes the issue but, I'm sorry to
say I can't figure out why. There's some magic somewhere which I'm
missing.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
2017-07-23 23:51:25 -07:00
Anton Lundin
b0b6026c68 ubus: Make multiple instances work again (#8571)
Back in "ubus: Refresh session on Access denied (#8111)" I added the
decorator _refresh_on_acccess_denied. Somehow that stopped multiple ubus
trackers from working in parallel, and only the one first init'ed
worked.

Changing the order of the decorators fixes the issue but, I'm sorry to
say I can't figure out why. There's some magic somewhere which I'm
missing.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
2017-07-23 23:51:07 -07:00
Russell Cloran
8f47a9109c prometheus: Fix zwave battery level (#8615) 2017-07-23 23:49:22 -07:00
Russell Cloran
f0293eeac2 prometheus: Fix zwave battery level (#8615) 2017-07-23 23:49:03 -07:00
Phil Cole
e4317a6741 Tado Fix #8606 (#8621)
Handle case where 'mode' and 'fanSpeed' are missing JSON. Based on
changes in commit
adfb608f86
2017-07-23 23:48:33 -07:00
Phil Cole
4b449f5f93 Tado Fix #8606 (#8621)
Handle case where 'mode' and 'fanSpeed' are missing JSON. Based on
changes in commit
adfb608f86
2017-07-23 23:48:20 -07:00
Daniel Schaal
8760dc9b29 Check if /dev/input/by-id exists (#8601) 2017-07-23 23:47:52 -07:00
Daniel Schaal
1831a7da68 Check if /dev/input/by-id exists (#8601) 2017-07-23 23:47:38 -07:00
Marcelo Moreira de Mello
3e34f34f6b Bumped Amcrest version (#8624) 2017-07-23 23:46:35 -07:00
Marcelo Moreira de Mello
3fec2955a5 Bumped Amcrest version (#8624) 2017-07-23 23:46:23 -07:00
Chia-liang Kao
2cf9254a08 Fix STATION_SCHEMA validation on longitude (#8610) 2017-07-23 23:35:49 -07:00
Russell Cloran
333da0dc6d zha: Update to bellows 0.3.4 (#8594) 2017-07-23 23:35:49 -07:00
Yannick POLLART
7b10f0a14f Fix broken status update for lighting4 devices (#8543)
* Fix broken status update for lighting4 devices

* Fixed indentation
2017-07-23 23:35:49 -07:00
Anders Melchiorsen
fb6bdfaba9 LIFX: assume default features for unknown products (#8553)
This makes the detection work for prototypes as well.
2017-07-23 23:35:48 -07:00
Pierre Ståhl
d7da90ae54 Fix support for multiple Apple TVs (#8539) 2017-07-23 23:35:48 -07:00
Eugenio Panadero
a5bfcceacd Attach the chat_id for a callback query from a chat group (fixes #8461) (#8523) 2017-07-23 23:35:48 -07:00
Pascal Vizeli
4961ece931 Realfix for dlib (#8517)
* Update dlib_face_detect.py

* fix lint

* Update dlib_face_detect.py
2017-07-23 23:35:48 -07:00
Pascal Vizeli
7d99d6aad9 Update dlib_face_detect.py (#8516) 2017-07-23 23:35:48 -07:00
Russell Cloran
6dc93c2751 prometheus: Convert fahrenheit to celsius (#8511) 2017-07-23 23:35:48 -07:00
Maikel Wever
5c39eebea8 Fix TP-Link device tracker regression since 0.49 (#8497)
* Fix TP-Link device tracker regression since 0.49

This regression was introduced by #8322.

Fix is to utf encode the password like the other TP-Link backends do.

* Fix linting issue introduced in previous commit

Commit in question: 677f3fbb7f821ee925364c8260d235dce4f0ddbe
2017-07-23 23:35:48 -07:00
Paulus Schoutsen
ffd295b38b Update to 0.49.1 2017-07-23 23:34:08 -07:00
Phil Hawthorne
5d810dae86 REST binary sensor value_template optional (#8596)
According to the documentation, the `value_template` for the REST
binary_sensor is not required. However, if you don't provide this when
setting up a binary sensor, the component fails. Looks like a variable
was not being set, which I've now included.

This should make the REST binary sensor act the same way as the REST
sensor now.
2017-07-23 22:42:41 +02:00
Fabian Affolter
486bcc4cae Upgrade mypy to 0.520 (#8616) 2017-07-23 20:20:38 +02:00
Fabian Affolter
cc2de5e1dc Upgrade youtube_dl to 2017.7.23 (#8617) 2017-07-23 20:20:23 +02:00
cribbstechnologies
77d8e393a1 better but still not great (#8618) 2017-07-23 20:19:58 +02:00
Koen Ekelschot
c6bf529d38 Allow set_cover_position in scenes (#8613) 2017-07-23 15:59:27 +02:00
Chia-liang Kao
dac9716cf4 Fix STATION_SCHEMA validation on longitude (#8610) 2017-07-23 10:22:49 +02:00
Thomas Klingbeil
9043895407 make attributes in the fritzdect module easier to process (#8436)
* make attributes in the fritzdect module easier to process
* remove spaces in attribute names
* move units to separate attributes

* make attributes in the fritzdect module easier to process
* remove spaces in attribute names
* move units to separate attributes

* Use new python formating syntax and attribute constant

* Shorten too long line

* Fix indent
2017-07-22 20:34:58 +02:00
Open Home Automation
2f08a91fdd Simplified percent conversion, better logging (#8568)
* Simplified percent conversion, better logging

*  Unnecessary pass statement (unnecessary-pass)
2017-07-22 20:00:13 +02:00
Sean Gollschewsky
1807b45222 Binary sensor ping fixed for hassio (#8573)
* Add support for multiple ping utilities.

* Added support for differing flavours of ping included with
different distributions (specifically alpine linux for hassio's
homeassistant).

* Updated as per comments in PR
2017-07-22 19:50:31 +02:00
Teemu R
b4f392b181 bump python-mirobo version for more robust protocol handling, make the platform to update on startup (#8602) 2017-07-22 19:36:14 +02:00
William Scanlon
8e8ec7a7c3 Remove code in wink.py overwriting hass.data configurator (#8595) 2017-07-22 09:21:38 -04:00
Paulus Schoutsen
7edf14e55f Add Intent component (#8434)
* Add intent component

* Add intent script component

* Add shopping list component

* Convert Snips to use intent component

* Convert Alexa to use intent component

* Lint

* Fix Alexa tests

* Update snips test

* Add intent support to conversation

* Add API to view shopping list contents

* Lint

* Fix demo test

* Lint

* lint

* Remove type from slot schema

* Add dependency to conversation

* Move intent to be a helper

* Fix conversation

* Clean up intent helper

* Fix Alexa

* Snips to use new hass.components

* Allow registering intents with conversation at any point in time

* Shopping list to register sentences

* Add HTTP endpoint to Conversation

* Add async action option to intent_script

* Update API.ai to use intents

* Cleanup Alexa

* Shopping list component to register built-in panel

* Rename shopping list intent to inlude Hass name
2017-07-21 21:38:53 -07:00
Paulus Schoutsen
7bea69ce83 update frontend 2017-07-21 21:29:58 -07:00
Russell Cloran
8d31c5fbf6 zha: Update to bellows 0.3.4 (#8594) 2017-07-21 21:22:43 -07:00
William Scanlon
dc42b6358a Support for Wink oauth application authorization (#8208) 2017-07-21 20:18:57 -04:00
Per Osbäck
06ceadfd54 upgrade pywebpush and PyJWT (#8588) 2017-07-21 22:35:19 +02:00
Daniel Høyer Iversen
4359e0babf xiaomi binary sensor bug fix (#8586)
* xiaomi binary sensor bug fig

* Is not need on binary_sensor
2017-07-21 12:39:25 +02:00
Marcelo Moreira de Mello
ee153062ab Extends Fitbit sensors to track the device battery level (#8583)
* Extends Fitbit sensors to track the device battery level

* cleanup old stuff

* remove update from init
2017-07-21 10:19:26 +02:00
Sebastian Muszynski
fada6d3f49 Device support for different new sensors of the xiaomi aqara gateway (#8577)
* The gateway configuration accepts a MAC address or a SID value in uppercase already.
The ringtone services accepts the same values now. I hope it will avoid confusion.

* Device support for the new wall switches with neutral lead (ctrl_ln1, ctrl_ln2) added.

* Measurement unit from pressure of weather.v1 fixed.

* Device support for sensor_magnet.aq2 added.

* Device support for sensor_motion.aq2 (motion and lux) added.

* Code reformatted.

* The ringtone service (start/stop) uses the parameter gw_mac instead of gw_sid now.

* Version of the required library updated.
2017-07-21 10:13:42 +02:00
Daniel Høyer Iversen
f6a5e0887d upgade xiaomi lib to 0.2 (#8584) 2017-07-21 10:10:03 +02:00
William Scanlon
4f8d2ec317 Added Time Remaining and Time Elapsed sensors for octoprint (#8581)
Add an optional extended description…
2017-07-21 09:40:07 +02:00
Aaron Bach
e63a96cf53 Bumped python-simplisafe version (#8578)
* Bumped python-simplisafe version
2017-07-20 18:59:41 -04:00
Daniel Høyer Iversen
a5c0831dc1 xiaomi bug fix (#8576) 2017-07-20 23:04:21 +02:00
namadori
718949481f fix #8263 corrected Adafruit DHT library version from 1.3.0 to 1.3.2 (#8562)
Add an optional extended description…
2017-07-20 15:53:06 +02:00
Daniel Høyer Iversen
90639d33ab Xiaomi gw support (#8555)
* xiaomi support

* xiaomi support

* style

* style

* style

* style

* style

* coveragerc

* Update xiaomi.py

* Update xiaomi.py

* Update xiaomi.py

* refactorization

* refactorization

* config validation

* style

* package

* refactorization

* refactorization

* refactorization

* HA integration
2017-07-20 15:20:00 +02:00
Greg Dowling
966809c1a1 Merge pull request #8564 from home-assistant/bump_pyvera
Bump pyvera to fix colour exception
2017-07-20 11:53:53 +01:00
pavoni
bc27d173d0 Bump pyver to fix exception in fetching colours. 2017-07-20 10:21:08 +01:00
Daniel Høyer Iversen
fde291f866 Add is_lighting4 to RfxtrxBinarySensor (#8563) 2017-07-20 11:12:42 +02:00
Paulus Schoutsen
49c399c358 Update persistent deps dir version in config.py (#8479)
* Update persistent deps dir version in config.py

* Update last version to remove deps dir in tests
2017-07-19 22:59:21 -07:00
Sean Dague
8d1999dc12 Enhance python_script to support "_getitem_" (#8541)
* Enhance python_script to support "_getitem_"

In order to use dict / list structures in python scripts we need
_getitem_ allowed in the RestrictedPython environment. There is a
default_guarded_getitem included with RestrictedPython, which is a
pass through used in the Eval code paths.

* Add tests for dict/list support in python_scripts

* Lint
2017-07-19 22:56:24 -07:00
Yannick POLLART
ee05a4ab89 Fix broken status update for lighting4 devices (#8543)
* Fix broken status update for lighting4 devices

* Fixed indentation
2017-07-19 22:56:11 -07:00
Anders Melchiorsen
8a42e1551a LIFX: assume default features for unknown products (#8553)
This makes the detection work for prototypes as well.
2017-07-19 22:54:46 -07:00
Jeff Wilson
9cc3e7e47b Handle manual edits to emulated_hue_ids.json (#8560) 2017-07-19 22:51:50 -07:00
Open Home Automation
54755df9ea Added a service to write to KNX group addressed including documentation (#8491)
* Added a service to write to KNX group addressed including documentation

* Define parameters as required

* Reformating

* Moved service documentation to service.yaml

* Moved service documentation to services.yaml

* Update knx.py
2017-07-20 07:01:05 +02:00
William Scanlon
84ebcd8a59 Support for Wink Switch and Light groups also fix fan speed selection (#8501)
* Support for Switch and Light groups, fix fan speed

* Fixed hound violations
2017-07-20 00:27:39 +02:00
Marcelo Moreira de Mello
f1280d3edb Extends Pi-hole sensor to support the new sensors: (#8549)
- domains_being_blocked
  - queries_cached
  - queries_forwarded
  - unique_clients
  - unique_domains
2017-07-19 19:45:53 +02:00
Eugenio Panadero
c27074e6f7 turn_on_action and turn_off_action with script syntax (#8558) 2017-07-19 13:33:16 -04:00
viswa-swami
c8bfcd2ed4 Upgrade the alarmdecoder dependency library from 0.12.1 to 0.12.3. (#8542)
* Upgrade the alarmdecoder dependency library from 0.12.1 to 0.12.3. Nutech software who owns this library have upgraded this library with some fixes regarding arming it to home/away and then disarming the alarm. Without this upgraded library, HASS is having a problem when we try to disarm an armed alarm after around 8 hours or so.

* Updated the requirements_all.txt by running the script gen_requirements_all.py
2017-07-19 12:21:39 +02:00
Jeff Wilson
42699b7a60 Report Harmony remote off if state is unknown (#8547)
Add an optional extended description…
2017-07-19 12:20:45 +02:00
Aliaksandr
6bc07298d3 [media_extractor] Add support for custom stream queries for media_extractor (#8538)
* Add support for different stream formats

* Encapsulate logic inside MediaExtractor class

* Add CONFIG_SCHEMA

* Fix for cases when youtube-dl returns content of playlist as list
2017-07-19 08:14:48 +01:00
Marcelo Moreira de Mello
4ece4bf241 Fix exception dlib_face_identify when image is not recognized by face_recognition module (#8552)
*   Some images are not supported by face_recognition, so this patch treats the error
  messages instead throwing a traceback. Fixes #7867

* Makes lint happy
2017-07-19 09:04:01 +02:00
Kevin Fronczak
1a86fa5a02 Initial support for Google Wifi/OnHub (#8485)
* Initial support for Google Wifi/OnHub

* Moved state logic to update function of API class

- Throttle added to update
- State logic implementation is cleaner
- Modified tests to work with the new throttle on update
2017-07-19 00:16:32 +02:00
Paulus Schoutsen
d54a634f11 Update demo.py 2017-07-18 15:11:17 -07:00
Thibault Cohen
5e1ff20b09 Decora: Fix set brightness and improve reconnection (#8522) 2017-07-19 00:02:42 +02:00
Pierre Ståhl
29266213a0 Fix support for multiple Apple TVs (#8539) 2017-07-18 19:19:36 +01:00
Fabian Affolter
2aa89cfe07 Upgrade TwitterAPI to 2.4.6 (#8535) 2017-07-18 16:24:32 +02:00
Fabian Affolter
879c816f5c Update docstrings (#8536) 2017-07-18 16:23:57 +02:00
Thibault Cohen
4ae11c009d Fix #6469 and #6828 (#8537) 2017-07-18 16:23:39 +02:00
Phil Lavin
dcd6f7a29e Return a 0 temperature value when none is found (#8518)
* Return a 0 temperature value when none is found

It's well documented that these TRVs will only return the current temperature
for a short time after the actuator has moved. This means that, usually, they will
not return the current temperature. Setting a non-value here causes errors in the logs
and for the TRV to not show on the dashboard at all

* Fix lint issue
2017-07-17 23:34:38 +02:00
Jan Losinski
fde4a7d029 Citybikes: Allow None as result for empty slots (#8528)
The citibykes API returns "null" as value for empty_slots on some
stations (see #8527). This causes the component to not process the data.

This is fixed by accepting None as valid data. The row in the frontend
is left empty if "null" was returned by the service.

fixes #8527
2017-07-17 22:50:55 +02:00
Anders Melchiorsen
b83ff739bc Remove deprecated automation keywords (#8510)
* Remove deprecated automation keywords

* Remove retired test case

* Remove retired keyword
2017-07-17 22:24:05 +02:00
Eugenio Panadero
8c9b3898fc handle timeout errors without logging.exception when updating hue lights; double quotes in log msgs (#8524) 2017-07-17 22:16:18 +02:00
Fabian Affolter
95e0027924 Fix KeyError 2017-07-17 20:21:16 +02:00
Mike Christianson
c67c20f752 fix for a bug introduced with media support in #8282 (#8513)
data may be None if twitter data property unconfigured:
  File "/opt/homeassistant/homeassistant_venv/lib/python3.4/site-packages/homeassistant/components/notify/twitter.py", line 63, in send_message
    media = data.get(ATTR_MEDIA)
2017-07-17 19:45:42 +02:00
Kevin Fronczak
1a1571cd52 Added sensor state rounding (#8499) 2017-07-17 19:21:41 +02:00
Eugenio Panadero
cca0d3ed44 Attach the chat_id for a callback query from a chat group (fixes #8461) (#8523) 2017-07-17 13:47:28 +02:00
Pascal Vizeli
f0479855bd Realfix for dlib (#8517)
* Update dlib_face_detect.py

* fix lint

* Update dlib_face_detect.py
2017-07-17 12:09:42 +02:00
Russell Cloran
40aafcdf5d prometheus: Convert fahrenheit to celsius (#8511) 2017-07-17 11:25:20 +02:00
Pascal Vizeli
8c9557401f Update dlib_face_detect.py (#8516) 2017-07-17 11:03:16 +02:00
Alexander Rust
ffd3081743 Added additional attributes to OwnTracks device_tracker (#8503)
* Added additional attributes to OwnTracks device_tracker

* Added missing space after :
2017-07-16 21:43:47 +02:00
Paulus Schoutsen
d0275c8075 Persistent notification import (#8507)
* Rewrite persistent notification creation

* Update components.is_on to use auto loading

* Fix two hass parameters
2017-07-16 21:39:38 +02:00
Maikel Wever
f6c3832e90 Fix TP-Link device tracker regression since 0.49 (#8497)
* Fix TP-Link device tracker regression since 0.49

This regression was introduced by #8322.

Fix is to utf encode the password like the other TP-Link backends do.

* Fix linting issue introduced in previous commit

Commit in question: 677f3fbb7f821ee925364c8260d235dce4f0ddbe
2017-07-16 10:27:48 -07:00
Paulus Schoutsen
d29bdddaa7 Add bind_hass to components (#8502)
* Add bind_hass to components

* Add bind_hass to group
2017-07-16 10:14:46 -07:00
Paulus Schoutsen
d3be056d15 Expose all components on hass [Concept] (#8490)
* Add components concept

* Lint

* Raise ImportError if component not found
2017-07-16 09:23:06 -07:00
Open Home Automation
bffa0d2b04 Bump to KNXIP 0.5 (#8492)
* Bump to KNXIP 0.5

* Updated requirements file
2017-07-16 13:34:13 +02:00
Paulus Schoutsen
23b65bfb30 Version bump to 0.50.0dev0 2017-07-15 21:07:15 -07:00
Martin Hjelmare
543e8bb62e Fix check for running inside venv (#8481)
* Import and use the function from pip instead of defining it
  ourselves.
* Fix tests.
2017-07-15 07:25:02 -07:00
Andrey
6ca828fd14 Make themes API work even when themes are not defined. (#8473) 2017-07-14 11:26:26 -07:00
Anders Melchiorsen
87b83f3602 Accept transition for light.toggle (#8466) 2017-07-13 20:04:23 -07:00
Bryce Edwards
5829cdfdf1 Radarr sensor fix for issue #8250 (#8456)
* Radarr sensor fix for issue #8250

* Radarr sensor fix for issue #8250
2017-07-13 20:02:07 -07:00
Sean Gollschewsky
d473f3407b Remove km from visibility, add visibility_distance (#8454)
* Remove km from visibility, add visibility_distance

* Fix line length

* Fix trailing space and line break indentation

* Indentation

* More whitespace
2017-07-13 20:01:25 -07:00
dersger
9373d5e901 Fix media_position for cast component (#8452)
* Make it available during state paused.

* Don't adjust for media_position_updated_at. I.e. do as vlc, sonos etc
  so that returned position is the position at the time of
  media_position_updated_at, not now.
2017-07-13 20:00:23 -07:00
Dougal Matthews
d8abef9210 Add RGB support to switch.flux (#8417) 2017-07-13 19:53:19 -07:00
Anders Melchiorsen
4fde0ffe9c LIFX: support for multizone (#8399)
* Make aiolifx modules easily available

* Use aiolifx features_map for deciding bulb features

Also move the feature detection out of Light so it is available even
during the initial detection.

* Move each LIFX light type to a separate class

* Simplify AwaitAioLIFX

This has become possible with recent aiolifx that calls the callback even
when a message is lost.

Now the wrapper can be used also before a Light is added though the register
callback then has to become a coroutine.

* Refactor send_color

* Add support for multizone

This lets lifx_set_state work on individual zones.

Also update to aiolifx_effects 0.1.1 that restores the state for individual
zones.
2017-07-13 19:38:36 -07:00
Martin Hjelmare
ba019c799a Make deps directory persistent over upgrades (#7801)
* Use pip install --user if venv not active

* Set PYTHONUSERBASE to deps directory, when installing with --user
  option.
* Reset --prefix option to workaround incompatability when installing
  with --user option. This requires pip version 8.0.0 or greater.
* Require pip version 8.0.3.
* Do not delete deps directory on home assistant upgrade.
* Fix local lib mount and check package exist.

* Update and add tests

* Fix upgrade from before version 0.46

* Extract function to get user site

* Add function(s) to package util to get user site.
* Use async subprocess for one of the functions to get user site.
* Add function to package util to check if virtual environment is
  active.
* Add and update tests.

* Update version for last removal of deps dir

* Address comments

* Rewrite package util tests with pytest

* Rewrite all existing unittest class based tests for package util as
  test functions, and capitalize pytest fixtures.
* Add test for installing with target inside venv.
2017-07-13 19:26:21 -07:00
Paulus Schoutsen
5581c6295e Fix iFrame panel test 2017-07-13 10:19:59 -07:00
785 changed files with 41922 additions and 8659 deletions

View File

@@ -8,6 +8,9 @@ omit =
homeassistant/helpers/signal.py
# omit pieces of code that rely on external devices being present
homeassistant/components/abode.py
homeassistant/components/*/abode.py
homeassistant/components/alarmdecoder.py
homeassistant/components/*/alarmdecoder.py
@@ -29,6 +32,9 @@ omit =
homeassistant/components/arlo.py
homeassistant/components/*/arlo.py
homeassistant/components/asterisk_mbox.py
homeassistant/components/*/asterisk_mbox.py
homeassistant/components/axis.py
homeassistant/components/*/axis.py
@@ -47,6 +53,9 @@ omit =
homeassistant/components/digital_ocean.py
homeassistant/components/*/digital_ocean.py
homeassistant/components/doorbird.py
homeassistant/components/*/doorbird.py
homeassistant/components/dweet.py
homeassistant/components/*/dweet.py
@@ -140,6 +149,9 @@ omit =
homeassistant/components/rachio.py
homeassistant/components/*/rachio.py
homeassistant/components/raincloud.py
homeassistant/components/*/raincloud.py
homeassistant/components/raspihats.py
homeassistant/components/*/raspihats.py
@@ -152,6 +164,9 @@ omit =
homeassistant/components/rpi_pfio.py
homeassistant/components/*/rpi_pfio.py
homeassistant/components/satel_integra.py
homeassistant/components/*/satel_integra.py
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
@@ -164,6 +179,12 @@ omit =
homeassistant/components/tellstick.py
homeassistant/components/*/tellstick.py
homeassistant/components/tesla.py
homeassistant/components/*/tesla.py
homeassistant/components/thethingsnetwork.py
homeassistant/components/*/thethingsnetwork.py
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/tradfri.py
@@ -173,6 +194,12 @@ omit =
homeassistant/components/notify/twilio_sms.py
homeassistant/components/notify/twilio_call.py
homeassistant/components/usps.py
homeassistant/components/*/usps.py
homeassistant/components/velbus.py
homeassistant/components/*/velbus.py
homeassistant/components/velux.py
homeassistant/components/*/velux.py
@@ -193,6 +220,13 @@ omit =
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/xiaomi_aqara.py
homeassistant/components/binary_sensor/xiaomi_aqara.py
homeassistant/components/cover/xiaomi_aqara.py
homeassistant/components/light/xiaomi_aqara.py
homeassistant/components/sensor/xiaomi_aqara.py
homeassistant/components/switch/xiaomi_aqara.py
homeassistant/components/zabbix.py
homeassistant/components/*/zabbix.py
@@ -208,6 +242,8 @@ omit =
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/concord232.py
homeassistant/components/alarm_control_panel/egardia.py
homeassistant/components/alarm_control_panel/manual_mqtt.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/alarm_control_panel/simplisafe.py
homeassistant/components/alarm_control_panel/totalconnect.py
@@ -223,6 +259,7 @@ omit =
homeassistant/components/binary_sensor/rest.py
homeassistant/components/binary_sensor/tapsaff.py
homeassistant/components/browser.py
homeassistant/components/calendar/todoist.py
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/ffmpeg.py
homeassistant/components/camera/foscam.py
@@ -257,7 +294,9 @@ omit =
homeassistant/components/device_tracker/cisco_ios.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/gpslogger.py
homeassistant/components/device_tracker/huawei_router.py
homeassistant/components/device_tracker/icloud.py
homeassistant/components/device_tracker/keenetic_ndms2.py
homeassistant/components/device_tracker/linksys_ap.py
homeassistant/components/device_tracker/linksys_smart.py
homeassistant/components/device_tracker/luci.py
@@ -274,7 +313,6 @@ omit =
homeassistant/components/device_tracker/tplink.py
homeassistant/components/device_tracker/trackr.py
homeassistant/components/device_tracker/ubus.py
homeassistant/components/device_tracker/xiaomi.py
homeassistant/components/downloader.py
homeassistant/components/emoncms_history.py
homeassistant/components/emulated_hue/upnp.py
@@ -291,6 +329,7 @@ omit =
homeassistant/components/light/blinkt.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/decora.py
homeassistant/components/light/decora_wifi.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
@@ -303,18 +342,22 @@ omit =
homeassistant/components/light/piglow.py
homeassistant/components/light/sensehat.py
homeassistant/components/light/tikteck.py
homeassistant/components/light/tplink.py
homeassistant/components/light/tradfri.py
homeassistant/components/light/x10.py
homeassistant/components/light/xiaomi_miio.py
homeassistant/components/light/yeelight.py
homeassistant/components/light/yeelightsunflower.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/nello.py
homeassistant/components/lock/nuki.py
homeassistant/components/lock/lockitron.py
homeassistant/components/lock/sesame.py
homeassistant/components/media_extractor.py
homeassistant/components/media_player/anthemav.py
homeassistant/components/media_player/aquostv.py
homeassistant/components/media_player/bluesound.py
homeassistant/components/media_player/braviatv.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/clementine.py
@@ -344,6 +387,7 @@ omit =
homeassistant/components/media_player/pioneer.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/roku.py
homeassistant/components/media_player/russound_rio.py
homeassistant/components/media_player/russound_rnet.py
homeassistant/components/media_player/samsungtv.py
homeassistant/components/media_player/snapcast.py
@@ -354,24 +398,30 @@ omit =
homeassistant/components/media_player/vlc.py
homeassistant/components/media_player/volumio.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/media_player/yamaha_musiccast.py
homeassistant/components/mycroft.py
homeassistant/components/notify/aws_lambda.py
homeassistant/components/notify/aws_sns.py
homeassistant/components/notify/aws_sqs.py
homeassistant/components/notify/ciscospark.py
homeassistant/components/notify/clicksend.py
homeassistant/components/notify/clicksendaudio.py
homeassistant/components/notify/discord.py
homeassistant/components/notify/facebook.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/gntp.py
homeassistant/components/notify/group.py
homeassistant/components/notify/hipchat.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/kodi.py
homeassistant/components/notify/lannouncer.py
homeassistant/components/notify/llamalab_automate.py
homeassistant/components/notify/matrix.py
homeassistant/components/notify/message_bird.py
homeassistant/components/notify/mycroft.py
homeassistant/components/notify/nfandroidtv.py
homeassistant/components/notify/nma.py
homeassistant/components/notify/prowl.py
homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushetta.py
homeassistant/components/notify/pushover.py
@@ -392,6 +442,7 @@ omit =
homeassistant/components/remote/itach.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/scene/lifx_cloud.py
homeassistant/components/sensor/airvisual.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
homeassistant/components/sensor/bbox.py
@@ -417,6 +468,7 @@ omit =
homeassistant/components/sensor/dovado.py
homeassistant/components/sensor/dte_energy_bridge.py
homeassistant/components/sensor/dublin_bus_transport.py
homeassistant/components/sensor/dwd_weather_warnings.py
homeassistant/components/sensor/ebox.py
homeassistant/components/sensor/eddystone_temperature.py
homeassistant/components/sensor/eliqonline.py
@@ -430,6 +482,7 @@ omit =
homeassistant/components/sensor/fixer.py
homeassistant/components/sensor/fritzbox_callmonitor.py
homeassistant/components/sensor/fritzbox_netmonitor.py
homeassistant/components/sensor/geizhals.py
homeassistant/components/sensor/gitter.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/google_travel_time.py
@@ -451,6 +504,7 @@ omit =
homeassistant/components/sensor/metoffice.py
homeassistant/components/sensor/miflora.py
homeassistant/components/sensor/modem_callerid.py
homeassistant/components/sensor/mopar.py
homeassistant/components/sensor/mqtt_room.py
homeassistant/components/sensor/mvglive.py
homeassistant/components/sensor/netdata.py
@@ -476,6 +530,7 @@ omit =
homeassistant/components/sensor/scrape.py
homeassistant/components/sensor/sensehat.py
homeassistant/components/sensor/serial_pm.py
homeassistant/components/sensor/shodan.py
homeassistant/components/sensor/skybeacon.py
homeassistant/components/sensor/sma.py
homeassistant/components/sensor/snmp.py
@@ -487,8 +542,10 @@ omit =
homeassistant/components/sensor/swiss_public_transport.py
homeassistant/components/sensor/synologydsm.py
homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/temper.py
homeassistant/components/sensor/tibber.py
homeassistant/components/sensor/time_date.py
homeassistant/components/sensor/torque.py
homeassistant/components/sensor/transmission.py
@@ -496,9 +553,10 @@ omit =
homeassistant/components/sensor/uber.py
homeassistant/components/sensor/upnp.py
homeassistant/components/sensor/ups.py
homeassistant/components/sensor/usps.py
homeassistant/components/sensor/vasttrafik.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/worldtidesinfo.py
homeassistant/components/sensor/worxlandroid.py
homeassistant/components/sensor/xbox_live.py
homeassistant/components/sensor/yweather.py
homeassistant/components/sensor/zamg.py
@@ -520,17 +578,18 @@ omit =
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pilight.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rainmachine.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/rpi_rf.py
homeassistant/components/switch/tplink.py
homeassistant/components/switch/telnet.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/switch/xiaomi_vacuum.py
homeassistant/components/telegram_bot/*
homeassistant/components/thingspeak.py
homeassistant/components/tts/amazon_polly.py
homeassistant/components/tts/picotts.py
homeassistant/components/upnp.py
homeassistant/components/vacuum/roomba.py
homeassistant/components/weather/bom.py
homeassistant/components/weather/buienradar.py
homeassistant/components/weather/metoffice.py
@@ -539,6 +598,7 @@ omit =
homeassistant/components/weather/zamg.py
homeassistant/components/zeroconf.py
homeassistant/components/zwave/util.py
homeassistant/components/vacuum/mqtt.py
[report]

4
.gitignore vendored
View File

@@ -73,6 +73,7 @@ pyvenv.cfg
pip-selfcheck.json
venv
.venv
Pipfile*
# vimmy stuff
*.swp
@@ -93,3 +94,6 @@ docs/build
# Windows Explorer
desktop.ini
/home-assistant.pyproj
/home-assistant.sln
/.vs/home-assistant/v14

View File

@@ -39,3 +39,7 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave
homeassistant/components/cover/template.py @PhracturedBlue
homeassistant/components/device_tracker/automatic.py @armills
homeassistant/components/media_player/kodi.py @armills
homeassistant/components/light/tplink.py @rytilahti
homeassistant/components/switch/tplink.py @rytilahti
homeassistant/components/climate/eq3btsmart.py @rytilahti
homeassistant/components/*/xiaomi_miio.py @rytilahti

View File

@@ -4,11 +4,11 @@ Everybody is invited and welcome to contribute to Home Assistant. There is a lot
The process is straight-forward.
- Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) by Kubernetes (but skip step 0)
- Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/devel/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0)
- Fork the Home Assistant [git repository](https://github.com/home-assistant/home-assistant).
- Write the code for your device, notification service, sensor, or IoT thing.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should take a peak at the [developer documentation](https://home-assistant.io/developers/) to get more details.
Still interested? Then you should take a peek at the [developer documentation](https://home-assistant.io/developers/) to get more details.

View File

@@ -11,9 +11,10 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
#ENV INSTALL_FFMPEG no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
#ENV INSTALL_COAP_CLIENT no
#ENV INSTALL_COAP no
#ENV INSTALL_SSOCR no
VOLUME /config
RUN mkdir -p /usr/src/app
@@ -25,12 +26,10 @@ RUN virtualization/Docker/setup_docker_prereqs
# Install hass component dependencies
COPY requirements_all.txt requirements_all.txt
# Uninstall enum34 because some depenndecies install it but breaks Python 3.4+.
# See PR #8103 for more info.
RUN pip3 install --no-cache-dir -r requirements_all.txt && \
pip3 install --no-cache-dir mysqlclient psycopg2 uvloop cchardet && \
pip3 uninstall -y enum34
pip3 install --no-cache-dir mysqlclient psycopg2 uvloop cchardet
# Copy source
COPY . .

View File

@@ -1,5 +1,5 @@
Home Assistant |Build Status| |Coverage Status| |Join the chat at https://gitter.im/home-assistant/home-assistant| |Join the dev chat at https://gitter.im/home-assistant/home-assistant/devs|
==============================================================================================================================================================================================
Home Assistant |Build Status| |Coverage Status| |Chat Status|
=============================================================
Home Assistant is a home automation platform running on Python 3. It is able to track and control all devices at home and offer a platform for automating control.
@@ -31,10 +31,8 @@ of a component, check the `Home Assistant help section <https://home-assistant.i
:target: https://travis-ci.org/home-assistant/home-assistant
.. |Coverage Status| image:: https://img.shields.io/coveralls/home-assistant/home-assistant.svg
:target: https://coveralls.io/r/home-assistant/home-assistant?branch=master
.. |Join the chat at https://gitter.im/home-assistant/home-assistant| image:: https://img.shields.io/badge/gitter-general-blue.svg
:target: https://gitter.im/home-assistant/home-assistant?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |Join the dev chat at https://gitter.im/home-assistant/home-assistant/devs| image:: https://img.shields.io/badge/gitter-development-yellowgreen.svg
:target: https://gitter.im/home-assistant/home-assistant/devs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |Chat Status| image:: https://img.shields.io/discord/330944238910963714.svg
:target: https://discord.gg/c5DvZ4e
.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png
:target: https://home-assistant.io/demo/
.. |screenshot-components| image:: https://raw.github.com/home-assistant/home-assistant/dev/docs/screenshot-components.png

View File

@@ -117,7 +117,11 @@ def linkcode_resolve(domain, info):
linespec = "#L%d" % (lineno + 1)
else:
linespec = ""
fn = relpath(fn, start='../')
index = fn.find("/homeassistant/")
if index == -1:
index = 0
fn = fn[index:]
return '{}/blob/{}/{}{}'.format(GITHUB_URL, code_branch, fn, linespec)

View File

@@ -126,6 +126,12 @@ def get_arguments() -> argparse.Namespace:
type=int,
default=None,
help='Enables daily log rotation and keeps up to the specified days')
parser.add_argument(
'--log-file',
type=str,
default=None,
help='Log file to write to. If not set, CONFIG/home-assistant.log '
'is used')
parser.add_argument(
'--runner',
action='store_true',
@@ -256,13 +262,14 @@ def setup_and_run_hass(config_dir: str,
}
hass = bootstrap.from_config_dict(
config, config_dir=config_dir, verbose=args.verbose,
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days)
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,
log_file=args.log_file)
else:
config_file = ensure_config_file(config_dir)
print('Config directory:', config_dir)
hass = bootstrap.from_config_file(
config_file, verbose=args.verbose, skip_pip=args.skip_pip,
log_rotate_days=args.log_rotate_days)
log_rotate_days=args.log_rotate_days, log_file=args.log_file)
if hass is None:
return None

View File

@@ -19,6 +19,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE
from homeassistant.setup import async_setup_component
import homeassistant.loader as loader
from homeassistant.util.logging import AsyncHandler
from homeassistant.util.package import async_get_user_site, get_user_site
from homeassistant.util.yaml import clear_secret_cache
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.signal import async_register_signal_handling
@@ -26,6 +27,10 @@ from homeassistant.helpers.signal import async_register_signal_handling
_LOGGER = logging.getLogger(__name__)
ERROR_LOG_FILENAME = 'home-assistant.log'
# hass.data key for logging information.
DATA_LOGGING = 'logging'
FIRST_INIT_COMPONENT = set((
'recorder', 'mqtt', 'mqtt_eventstream', 'logger', 'introduction',
'frontend', 'history'))
@@ -37,9 +42,10 @@ def from_config_dict(config: Dict[str, Any],
enable_log: bool=True,
verbose: bool=False,
skip_pip: bool=False,
log_rotate_days: Any=None) \
log_rotate_days: Any=None,
log_file: Any=None) \
-> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a config dict.
"""Try to configure Home Assistant from a configuration dictionary.
Dynamically loads required components and its dependencies.
"""
@@ -48,13 +54,14 @@ def from_config_dict(config: Dict[str, Any],
if config_dir is not None:
config_dir = os.path.abspath(config_dir)
hass.config.config_dir = config_dir
mount_local_lib_path(config_dir)
hass.loop.run_until_complete(
async_mount_local_lib_path(config_dir, hass.loop))
# run task
hass = hass.loop.run_until_complete(
async_from_config_dict(
config, hass, config_dir, enable_log, verbose, skip_pip,
log_rotate_days)
log_rotate_days, log_file)
)
return hass
@@ -67,14 +74,27 @@ def async_from_config_dict(config: Dict[str, Any],
enable_log: bool=True,
verbose: bool=False,
skip_pip: bool=False,
log_rotate_days: Any=None) \
log_rotate_days: Any=None,
log_file: Any=None) \
-> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a config dict.
"""Try to configure Home Assistant from a configuration dictionary.
Dynamically loads required components and its dependencies.
This method is a coroutine.
"""
start = time()
if enable_log:
async_enable_logging(hass, verbose, log_rotate_days, log_file)
if sys.version_info[:2] < (3, 5):
_LOGGER.warning(
'Python 3.4 support has been deprecated and will be removed in '
'the begining of 2018. Please upgrade Python or your operating '
'system. More info: https://home-assistant.io/blog/2017/10/06/'
'deprecating-python-3.4-support/'
)
core_config = config.get(core.DOMAIN, {})
try:
@@ -85,13 +105,10 @@ def async_from_config_dict(config: Dict[str, Any],
yield from hass.async_add_job(conf_util.process_ha_config_upgrade, hass)
if enable_log:
async_enable_logging(hass, verbose, log_rotate_days)
hass.config.skip_pip = skip_pip
if skip_pip:
_LOGGER.warning('Skipping pip installation of required modules. '
'This may cause issues.')
_LOGGER.warning("Skipping pip installation of required modules. "
"This may cause issues")
if not loader.PREPARED:
yield from hass.async_add_job(loader.prepare, hass)
@@ -116,13 +133,13 @@ def async_from_config_dict(config: Dict[str, Any],
# pylint: disable=not-an-iterable
res = yield from core_components.async_setup(hass, config)
if not res:
_LOGGER.error('Home Assistant core failed to initialize. '
'Further initialization aborted.')
_LOGGER.error("Home Assistant core failed to initialize. "
"further initialization aborted")
return hass
yield from persistent_notification.async_setup(hass, config)
_LOGGER.info('Home Assistant core initialized')
_LOGGER.info("Home Assistant core initialized")
# stage 1
for component in components:
@@ -141,7 +158,7 @@ def async_from_config_dict(config: Dict[str, Any],
yield from hass.async_block_till_done()
stop = time()
_LOGGER.info('Home Assistant initialized in %.2fs', stop-start)
_LOGGER.info("Home Assistant initialized in %.2fs", stop-start)
async_register_signal_handling(hass)
return hass
@@ -151,7 +168,8 @@ def from_config_file(config_path: str,
hass: Optional[core.HomeAssistant]=None,
verbose: bool=False,
skip_pip: bool=True,
log_rotate_days: Any=None):
log_rotate_days: Any=None,
log_file: Any=None):
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter if given,
@@ -163,7 +181,7 @@ def from_config_file(config_path: str,
# run task
hass = hass.loop.run_until_complete(
async_from_config_file(
config_path, hass, verbose, skip_pip, log_rotate_days)
config_path, hass, verbose, skip_pip, log_rotate_days, log_file)
)
return hass
@@ -174,7 +192,8 @@ def async_from_config_file(config_path: str,
hass: core.HomeAssistant,
verbose: bool=False,
skip_pip: bool=True,
log_rotate_days: Any=None):
log_rotate_days: Any=None,
log_file: Any=None):
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter.
@@ -183,15 +202,15 @@ 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.async_add_job(mount_local_lib_path, config_dir)
yield from async_mount_local_lib_path(config_dir, hass.loop)
async_enable_logging(hass, verbose, log_rotate_days)
async_enable_logging(hass, verbose, log_rotate_days, log_file)
try:
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)
_LOGGER.error("Error loading %s: %s", config_path, err)
return None
finally:
clear_secret_cache()
@@ -203,7 +222,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:
log_rotate_days=None, log_file=None) -> None:
"""Set up the logging.
This method must be run in the event loop.
@@ -237,13 +256,18 @@ def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
pass
# Log errors to a file if we have write access to file or config dir
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
if log_file is None:
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
else:
err_log_path = os.path.abspath(log_file)
err_path_exists = os.path.isfile(err_log_path)
err_dir = os.path.dirname(err_log_path)
# Check if we can write to the error log if it exists or that
# we can create files in the containing directory if not.
if (err_path_exists and os.access(err_log_path, os.W_OK)) or \
(not err_path_exists and os.access(hass.config.config_dir, os.W_OK)):
(not err_path_exists and os.access(err_dir, os.W_OK)):
if log_rotate_days:
err_handler = logging.handlers.TimedRotatingFileHandler(
@@ -270,17 +294,31 @@ def async_enable_logging(hass: core.HomeAssistant, verbose: bool=False,
logger.addHandler(async_handler)
logger.setLevel(logging.INFO)
# Save the log file location for access by other components.
hass.data[DATA_LOGGING] = err_log_path
else:
_LOGGER.error(
"Unable to setup error log %s (access denied)", err_log_path)
def mount_local_lib_path(config_dir: str) -> str:
"""Add local library to Python Path."""
deps_dir = os.path.join(config_dir, 'deps')
lib_dir = get_user_site(deps_dir)
if lib_dir not in sys.path:
sys.path.insert(0, lib_dir)
return deps_dir
@asyncio.coroutine
def async_mount_local_lib_path(config_dir: str,
loop: asyncio.AbstractEventLoop) -> str:
"""Add local library to Python Path.
Async friendly.
This function is a coroutine.
"""
deps_dir = os.path.join(config_dir, 'deps')
if deps_dir not in sys.path:
sys.path.insert(0, os.path.join(config_dir, 'deps'))
lib_dir = yield from async_get_user_site(deps_dir, loop=loop)
if lib_dir not in sys.path:
sys.path.insert(0, lib_dir)
return deps_dir

View File

@@ -15,7 +15,6 @@ import homeassistant.core as ha
import homeassistant.config as conf_util
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.loader import get_component
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
@@ -33,25 +32,27 @@ def is_on(hass, entity_id=None):
If there is no entity id given we will check all.
"""
if entity_id:
group = get_component('group')
entity_ids = group.expand_entity_ids(hass, [entity_id])
entity_ids = hass.components.group.expand_entity_ids([entity_id])
else:
entity_ids = hass.states.entity_ids()
for ent_id in entity_ids:
domain = ha.split_entity_id(ent_id)[0]
module = get_component(domain)
try:
if module.is_on(hass, ent_id):
return True
component = getattr(hass.components, domain)
except AttributeError:
# module is None or method is_on does not exist
_LOGGER.exception("Failed to call %s.is_on for %s",
module, ent_id)
except ImportError:
_LOGGER.error('Failed to call %s.is_on: component not found',
domain)
continue
if not hasattr(component, 'is_on'):
_LOGGER.warning("Component %s has no is_on method.", domain)
continue
if component.is_on(ent_id):
return True
return False
@@ -100,6 +101,12 @@ def reload_core_config(hass):
hass.services.call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG)
@asyncio.coroutine
def async_reload_core_config(hass):
"""Reload the core config."""
yield from hass.services.async_call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG)
@asyncio.coroutine
def async_setup(hass, config):
"""Set up general services related to Home Assistant."""
@@ -161,10 +168,9 @@ def async_setup(hass, config):
return
if errors:
notif = get_component('persistent_notification')
_LOGGER.error(errors)
notif.async_create(
hass, "Config error. See dev-info panel for details.",
hass.components.persistent_notification.async_create(
"Config error. See dev-info panel for details.",
"Config validating", "{0}.check_config".format(ha.DOMAIN))
return

View File

@@ -0,0 +1,354 @@
"""
This component provides basic support for Abode Home Security system.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/abode/
"""
import asyncio
import logging
from functools import partial
from os import path
import voluptuous as vol
from requests.exceptions import HTTPError, ConnectTimeout
from homeassistant.helpers import discovery
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (ATTR_ATTRIBUTION, ATTR_DATE, ATTR_TIME,
ATTR_ENTITY_ID, CONF_USERNAME, CONF_PASSWORD,
CONF_EXCLUDE, CONF_NAME,
EVENT_HOMEASSISTANT_STOP,
EVENT_HOMEASSISTANT_START)
REQUIREMENTS = ['abodepy==0.11.9']
_LOGGER = logging.getLogger(__name__)
CONF_ATTRIBUTION = "Data provided by goabode.com"
CONF_LIGHTS = "lights"
CONF_POLLING = "polling"
DOMAIN = 'abode'
NOTIFICATION_ID = 'abode_notification'
NOTIFICATION_TITLE = 'Abode Security Setup'
EVENT_ABODE_ALARM = 'abode_alarm'
EVENT_ABODE_ALARM_END = 'abode_alarm_end'
EVENT_ABODE_AUTOMATION = 'abode_automation'
EVENT_ABODE_FAULT = 'abode_panel_fault'
EVENT_ABODE_RESTORE = 'abode_panel_restore'
SERVICE_SETTINGS = 'change_setting'
SERVICE_CAPTURE_IMAGE = 'capture_image'
SERVICE_TRIGGER = 'trigger_quick_action'
ATTR_DEVICE_ID = 'device_id'
ATTR_DEVICE_NAME = 'device_name'
ATTR_DEVICE_TYPE = 'device_type'
ATTR_EVENT_CODE = 'event_code'
ATTR_EVENT_NAME = 'event_name'
ATTR_EVENT_TYPE = 'event_type'
ATTR_EVENT_UTC = 'event_utc'
ATTR_SETTING = 'setting'
ATTR_USER_NAME = 'user_name'
ATTR_VALUE = 'value'
ABODE_DEVICE_ID_LIST_SCHEMA = vol.Schema([str])
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_POLLING, default=False): cv.boolean,
vol.Optional(CONF_EXCLUDE, default=[]): ABODE_DEVICE_ID_LIST_SCHEMA,
vol.Optional(CONF_LIGHTS, default=[]): ABODE_DEVICE_ID_LIST_SCHEMA
}),
}, extra=vol.ALLOW_EXTRA)
CHANGE_SETTING_SCHEMA = vol.Schema({
vol.Required(ATTR_SETTING): cv.string,
vol.Required(ATTR_VALUE): cv.string
})
CAPTURE_IMAGE_SCHEMA = vol.Schema({
ATTR_ENTITY_ID: cv.entity_ids,
})
TRIGGER_SCHEMA = vol.Schema({
ATTR_ENTITY_ID: cv.entity_ids,
})
ABODE_PLATFORMS = [
'alarm_control_panel', 'binary_sensor', 'lock', 'switch', 'cover',
'camera', 'light'
]
class AbodeSystem(object):
"""Abode System class."""
def __init__(self, username, password, name, polling, exclude, lights):
"""Initialize the system."""
import abodepy
self.abode = abodepy.Abode(username, password,
auto_login=True,
get_devices=True,
get_automations=True)
self.name = name
self.polling = polling
self.exclude = exclude
self.lights = lights
self.devices = []
def is_excluded(self, device):
"""Check if a device is configured to be excluded."""
return device.device_id in self.exclude
def is_automation_excluded(self, automation):
"""Check if an automation is configured to be excluded."""
return automation.automation_id in self.exclude
def is_light(self, device):
"""Check if a switch device is configured as a light."""
import abodepy.helpers.constants as CONST
return (device.generic_type == CONST.TYPE_LIGHT or
(device.generic_type == CONST.TYPE_SWITCH and
device.device_id in self.lights))
def setup(hass, config):
"""Set up Abode component."""
from abodepy.exceptions import AbodeException
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
name = conf.get(CONF_NAME)
polling = conf.get(CONF_POLLING)
exclude = conf.get(CONF_EXCLUDE)
lights = conf.get(CONF_LIGHTS)
try:
hass.data[DOMAIN] = AbodeSystem(
username, password, name, polling, exclude, lights)
except (AbodeException, ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Abode: %s", str(ex))
hass.components.persistent_notification.create(
'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(ex),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
setup_hass_services(hass)
setup_hass_events(hass)
setup_abode_events(hass)
for platform in ABODE_PLATFORMS:
discovery.load_platform(hass, platform, DOMAIN, {}, config)
return True
def setup_hass_services(hass):
"""Home assistant services."""
from abodepy.exceptions import AbodeException
def change_setting(call):
"""Change an Abode system setting."""
setting = call.data.get(ATTR_SETTING)
value = call.data.get(ATTR_VALUE)
try:
hass.data[DOMAIN].abode.set_setting(setting, value)
except AbodeException as ex:
_LOGGER.warning(ex)
def capture_image(call):
"""Capture a new image."""
entity_ids = call.data.get(ATTR_ENTITY_ID)
target_devices = [device for device in hass.data[DOMAIN].devices
if device.entity_id in entity_ids]
for device in target_devices:
device.capture()
def trigger_quick_action(call):
"""Trigger a quick action."""
entity_ids = call.data.get(ATTR_ENTITY_ID, None)
target_devices = [device for device in hass.data[DOMAIN].devices
if device.entity_id in entity_ids]
for device in target_devices:
device.trigger()
descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))[DOMAIN]
hass.services.register(
DOMAIN, SERVICE_SETTINGS, change_setting,
descriptions.get(SERVICE_SETTINGS),
schema=CHANGE_SETTING_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_CAPTURE_IMAGE, capture_image,
descriptions.get(SERVICE_CAPTURE_IMAGE),
schema=CAPTURE_IMAGE_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_TRIGGER, trigger_quick_action,
descriptions.get(SERVICE_TRIGGER),
schema=TRIGGER_SCHEMA)
def setup_hass_events(hass):
"""Home assistant start and stop callbacks."""
def startup(event):
"""Listen for push events."""
hass.data[DOMAIN].abode.events.start()
def logout(event):
"""Logout of Abode."""
if not hass.data[DOMAIN].polling:
hass.data[DOMAIN].abode.events.stop()
hass.data[DOMAIN].abode.logout()
_LOGGER.info("Logged out of Abode")
if not hass.data[DOMAIN].polling:
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, startup)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, logout)
def setup_abode_events(hass):
"""Event callbacks."""
import abodepy.helpers.timeline as TIMELINE
def event_callback(event, event_json):
"""Handle an event callback from Abode."""
data = {
ATTR_DEVICE_ID: event_json.get(ATTR_DEVICE_ID, ''),
ATTR_DEVICE_NAME: event_json.get(ATTR_DEVICE_NAME, ''),
ATTR_DEVICE_TYPE: event_json.get(ATTR_DEVICE_TYPE, ''),
ATTR_EVENT_CODE: event_json.get(ATTR_EVENT_CODE, ''),
ATTR_EVENT_NAME: event_json.get(ATTR_EVENT_NAME, ''),
ATTR_EVENT_TYPE: event_json.get(ATTR_EVENT_TYPE, ''),
ATTR_EVENT_UTC: event_json.get(ATTR_EVENT_UTC, ''),
ATTR_USER_NAME: event_json.get(ATTR_USER_NAME, ''),
ATTR_DATE: event_json.get(ATTR_DATE, ''),
ATTR_TIME: event_json.get(ATTR_TIME, ''),
}
hass.bus.fire(event, data)
events = [TIMELINE.ALARM_GROUP, TIMELINE.ALARM_END_GROUP,
TIMELINE.PANEL_FAULT_GROUP, TIMELINE.PANEL_RESTORE_GROUP,
TIMELINE.AUTOMATION_GROUP]
for event in events:
hass.data[DOMAIN].abode.events.add_event_callback(
event,
partial(event_callback, event))
class AbodeDevice(Entity):
"""Representation of an Abode device."""
def __init__(self, data, device):
"""Initialize a sensor for Abode device."""
self._data = data
self._device = device
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe Abode events."""
self.hass.async_add_job(
self._data.abode.events.add_device_callback,
self._device.device_id, self._update_callback
)
@property
def should_poll(self):
"""Return the polling state."""
return self._data.polling
def update(self):
"""Update automation state."""
self._device.refresh()
@property
def name(self):
"""Return the name of the sensor."""
return self._device.name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
'device_id': self._device.device_id,
'battery_low': self._device.battery_low,
'no_response': self._device.no_response,
'device_type': self._device.type
}
def _update_callback(self, device):
"""Update the device state."""
self.schedule_update_ha_state()
class AbodeAutomation(Entity):
"""Representation of an Abode automation."""
def __init__(self, data, automation, event=None):
"""Initialize for Abode automation."""
self._data = data
self._automation = automation
self._event = event
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe Abode events."""
if self._event:
self.hass.async_add_job(
self._data.abode.events.add_event_callback,
self._event, self._update_callback
)
@property
def should_poll(self):
"""Return the polling state."""
return self._data.polling
def update(self):
"""Update automation state."""
self._automation.refresh()
@property
def name(self):
"""Return the name of the sensor."""
return self._automation.name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
'automation_id': self._automation.automation_id,
'type': self._automation.type,
'sub_type': self._automation.sub_type
}
def _update_callback(self, device):
"""Update the device state."""
self._automation.refresh()
self.schedule_update_ha_state()

View File

@@ -13,8 +13,10 @@ import voluptuous as vol
from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY,
SERVICE_ALARM_ARM_NIGHT)
from homeassistant.config import load_yaml_config_file
from homeassistant.loader import bind_hass
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
@@ -30,6 +32,7 @@ SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
SERVICE_ALARM_ARM_NIGHT: 'alarm_arm_night',
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
}
@@ -44,6 +47,7 @@ ALARM_SERVICE_SCHEMA = vol.Schema({
})
@bind_hass
def alarm_disarm(hass, code=None, entity_id=None):
"""Send the alarm the command for disarm."""
data = {}
@@ -55,6 +59,7 @@ def alarm_disarm(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
@bind_hass
def alarm_arm_home(hass, code=None, entity_id=None):
"""Send the alarm the command for arm home."""
data = {}
@@ -66,6 +71,7 @@ def alarm_arm_home(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
@bind_hass
def alarm_arm_away(hass, code=None, entity_id=None):
"""Send the alarm the command for arm away."""
data = {}
@@ -77,6 +83,19 @@ def alarm_arm_away(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
@bind_hass
def alarm_arm_night(hass, code=None, entity_id=None):
"""Send the alarm the command for arm night."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_NIGHT, data)
@bind_hass
def alarm_trigger(hass, code=None, entity_id=None):
"""Send the alarm the command for trigger."""
data = {}
@@ -182,6 +201,17 @@ class AlarmControlPanel(Entity):
"""
return self.hass.async_add_job(self.alarm_arm_away, code)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
raise NotImplementedError()
def async_alarm_arm_night(self, code=None):
"""Send arm night command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_arm_night, code)
def alarm_trigger(self, code=None):
"""Send alarm trigger command."""
raise NotImplementedError()

View File

@@ -0,0 +1,85 @@
"""
This component provides HA alarm_control_panel support for Abode System.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.abode/
"""
import logging
from homeassistant.components.abode import (
AbodeDevice, DOMAIN as ABODE_DOMAIN, CONF_ATTRIBUTION)
from homeassistant.components.alarm_control_panel import (AlarmControlPanel)
from homeassistant.const import (ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
DEPENDENCIES = ['abode']
_LOGGER = logging.getLogger(__name__)
ICON = 'mdi:security'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for an Abode device."""
data = hass.data[ABODE_DOMAIN]
alarm_devices = [AbodeAlarm(data, data.abode.get_alarm(), data.name)]
data.devices.extend(alarm_devices)
add_devices(alarm_devices)
class AbodeAlarm(AbodeDevice, AlarmControlPanel):
"""An alarm_control_panel implementation for Abode."""
def __init__(self, data, device, name):
"""Initialize the alarm control panel."""
super().__init__(data, device)
self._name = name
@property
def icon(self):
"""Return icon."""
return ICON
@property
def state(self):
"""Return the state of the device."""
if self._device.is_standby:
state = STATE_ALARM_DISARMED
elif self._device.is_away:
state = STATE_ALARM_ARMED_AWAY
elif self._device.is_home:
state = STATE_ALARM_ARMED_HOME
else:
state = None
return state
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._device.set_standby()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._device.set_home()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._device.set_away()
@property
def name(self):
"""Return the name of the alarm."""
return self._name or super().name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
'device_id': self._device.device_id,
'battery_backup': self._device.battery,
'cellular_backup': self._device.is_cellular
}

View File

@@ -57,19 +57,19 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
if message.alarm_sounding or message.fire_alarm:
if self._state != STATE_ALARM_TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
elif message.armed_away:
if self._state != STATE_ALARM_ARMED_AWAY:
self._state = STATE_ALARM_ARMED_AWAY
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
elif message.armed_home:
if self._state != STATE_ALARM_ARMED_HOME:
self._state = STATE_ALARM_ARMED_HOME
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
else:
if self._state != STATE_ALARM_DISARMED:
self._state = STATE_ALARM_DISARMED
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
@property
def name(self):

View File

@@ -107,7 +107,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
newstate = STATE_ALARM_ARMED_AWAY
if not newstate == self._state:
_LOGGER.info("State Chnage from %s to %s", self._state, newstate)
_LOGGER.info("State Change from %s to %s", self._state, newstate)
self._state = newstate
return self._state

View File

@@ -5,10 +5,26 @@ For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import homeassistant.components.alarm_control_panel.manual as manual
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_TRIGGERED, CONF_PENDING_TIME)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Demo alarm control panel platform."""
add_devices([
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10, False),
manual.ManualAlarm(hass, 'Alarm', '1234', 5, 10, False, {
STATE_ALARM_ARMED_AWAY: {
CONF_PENDING_TIME: 5
},
STATE_ALARM_ARMED_HOME: {
CONF_PENDING_TIME: 5
},
STATE_ALARM_ARMED_NIGHT: {
CONF_PENDING_TIME: 5
},
STATE_ALARM_TRIGGERED: {
CONF_PENDING_TIME: 5
},
}),
])

View File

@@ -0,0 +1,190 @@
"""
Interfaces with Egardia/Woonveilig alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.egardia/
"""
import logging
import requests
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.exceptions as exc
import homeassistant.helpers.config_validation as cv
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PORT, CONF_HOST, CONF_PASSWORD, CONF_USERNAME, STATE_UNKNOWN,
CONF_NAME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED)
REQUIREMENTS = ['pythonegardia==1.0.21']
_LOGGER = logging.getLogger(__name__)
CONF_REPORT_SERVER_CODES = 'report_server_codes'
CONF_REPORT_SERVER_ENABLED = 'report_server_enabled'
CONF_REPORT_SERVER_PORT = 'report_server_port'
CONF_REPORT_SERVER_CODES_IGNORE = 'ignore'
DEFAULT_NAME = 'Egardia'
DEFAULT_PORT = 80
DEFAULT_REPORT_SERVER_ENABLED = False
DEFAULT_REPORT_SERVER_PORT = 52010
DOMAIN = 'egardia'
NOTIFICATION_ID = 'egardia_notification'
NOTIFICATION_TITLE = 'Egardia'
STATES = {
'ARM': STATE_ALARM_ARMED_AWAY,
'DAY HOME': STATE_ALARM_ARMED_HOME,
'DISARM': STATE_ALARM_DISARMED,
'HOME': STATE_ALARM_ARMED_HOME,
'TRIGGERED': STATE_ALARM_TRIGGERED,
'UNKNOWN': STATE_UNKNOWN,
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_REPORT_SERVER_CODES): vol.All(cv.ensure_list),
vol.Optional(CONF_REPORT_SERVER_ENABLED,
default=DEFAULT_REPORT_SERVER_ENABLED): cv.boolean,
vol.Optional(CONF_REPORT_SERVER_PORT, default=DEFAULT_REPORT_SERVER_PORT):
cv.port,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Egardia platform."""
from pythonegardia import egardiadevice
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
rs_enabled = config.get(CONF_REPORT_SERVER_ENABLED)
rs_port = config.get(CONF_REPORT_SERVER_PORT)
rs_codes = config.get(CONF_REPORT_SERVER_CODES)
try:
egardiasystem = egardiadevice.EgardiaDevice(
host, port, username, password, '')
except requests.exceptions.RequestException:
raise exc.PlatformNotReady()
except egardiadevice.UnauthorizedError:
_LOGGER.error("Unable to authorize. Wrong password or username")
return False
add_devices([EgardiaAlarm(
name, egardiasystem, hass, rs_enabled, rs_port, rs_codes)], True)
class EgardiaAlarm(alarm.AlarmControlPanel):
"""Representation of a Egardia alarm."""
def __init__(self, name, egardiasystem, hass, rs_enabled=False,
rs_port=None, rs_codes=None):
"""Initialize object."""
self._name = name
self._egardiasystem = egardiasystem
self._status = STATE_UNKNOWN
self._rs_enabled = rs_enabled
self._rs_port = rs_port
self._hass = hass
if rs_codes is not None:
self._rs_codes = rs_codes[0]
else:
self._rs_codes = rs_codes
if self._rs_enabled:
self.listen_to_system_status()
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._status
def handle_system_status_event(self, event):
"""Handle egardia_system_status_event."""
if event.data.get('status') is not None:
statuscode = event.data.get('status')
status = self.lookupstatusfromcode(statuscode)
self.parsestatus(status)
def listen_to_system_status(self):
"""Subscribe to egardia_system_status event."""
self._hass.bus.listen(
'egardia_system_status', self.handle_system_status_event)
def lookupstatusfromcode(self, statuscode):
"""Look at the rs_codes and returns the status from the code."""
status = 'UNKNOWN'
if self._rs_codes is not None:
statuscode = str(statuscode).strip()
for i in self._rs_codes:
val = str(self._rs_codes[i]).strip()
if ',' in val:
splitted = val.split(',')
for code in splitted:
code = str(code).strip()
if statuscode == code:
status = i.upper()
break
elif statuscode == val:
status = i.upper()
break
return status
def parsestatus(self, status):
"""Parse the status."""
_LOGGER.debug("Parsing status %s", status)
# Ignore the statuscode if it is IGNORE
if status.lower().strip() != CONF_REPORT_SERVER_CODES_IGNORE:
_LOGGER.debug("Not ignoring status")
newstatus = ([v for k, v in STATES.items()
if status.upper() == k][0])
self._status = newstatus
else:
_LOGGER.error("Ignoring status")
def update(self):
"""Update the alarm status."""
if not self._rs_enabled:
status = self._egardiasystem.getstate()
self.parsestatus(status)
def alarm_disarm(self, code=None):
"""Send disarm command."""
try:
self._egardiasystem.alarm_disarm()
except requests.exceptions.RequestException as err:
_LOGGER.error("Egardia device exception occurred when "
"sending disarm command: %s", err)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
try:
self._egardiasystem.alarm_arm_home()
except requests.exceptions.RequestException as err:
_LOGGER.error("Egardia device exception occurred when "
"sending arm home command: %s", err)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
try:
self._egardiasystem.alarm_arm_away()
except requests.exceptions.RequestException as err:
_LOGGER.error("Egardia device exception occurred when "
"sending arm away command: %s", err)

View File

@@ -106,7 +106,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
def _update_callback(self, partition):
"""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())
self.async_schedule_update_ha_state()
@property
def code_format(self):

View File

@@ -4,6 +4,7 @@ Support for manual alarms.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.manual/
"""
import copy
import datetime
import logging
@@ -12,9 +13,10 @@ import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, CONF_PLATFORM, CONF_NAME,
CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME, CONF_DISARM_AFTER_TRIGGER)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED,
CONF_PLATFORM, CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
CONF_DISARM_AFTER_TRIGGER)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_point_in_time
@@ -23,7 +25,28 @@ DEFAULT_PENDING_TIME = 60
DEFAULT_TRIGGER_TIME = 120
DEFAULT_DISARM_AFTER_TRIGGER = False
PLATFORM_SCHEMA = vol.Schema({
SUPPORTED_PENDING_STATES = [STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_TRIGGERED]
ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
config = copy.deepcopy(config)
for state in SUPPORTED_PENDING_STATES:
if CONF_PENDING_TIME not in config[state]:
config[state][CONF_PENDING_TIME] = config[CONF_PENDING_TIME]
return config
STATE_SETTING_SCHEMA = vol.Schema({
vol.Optional(CONF_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0))
})
PLATFORM_SCHEMA = vol.Schema(vol.All({
vol.Required(CONF_PLATFORM): 'manual',
vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string,
vol.Optional(CONF_CODE): cv.string,
@@ -33,7 +56,11 @@ PLATFORM_SCHEMA = vol.Schema({
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_DISARM_AFTER_TRIGGER,
default=DEFAULT_DISARM_AFTER_TRIGGER): cv.boolean,
})
vol.Optional(STATE_ALARM_ARMED_AWAY, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_HOME, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_TRIGGERED, default={}): STATE_SETTING_SCHEMA,
}, _state_validator))
_LOGGER = logging.getLogger(__name__)
@@ -46,7 +73,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
config.get(CONF_CODE),
config.get(CONF_PENDING_TIME, DEFAULT_PENDING_TIME),
config.get(CONF_TRIGGER_TIME, DEFAULT_TRIGGER_TIME),
config.get(CONF_DISARM_AFTER_TRIGGER, DEFAULT_DISARM_AFTER_TRIGGER)
config.get(CONF_DISARM_AFTER_TRIGGER, DEFAULT_DISARM_AFTER_TRIGGER),
config
)])
@@ -60,19 +88,23 @@ class ManualAlarm(alarm.AlarmControlPanel):
or disarm if `disarm_after_trigger` is true.
"""
def __init__(self, hass, name, code, pending_time,
trigger_time, disarm_after_trigger):
def __init__(self, hass, name, code, pending_time, trigger_time,
disarm_after_trigger, config):
"""Init the manual alarm panel."""
self._state = STATE_ALARM_DISARMED
self._hass = hass
self._name = name
self._code = str(code) if code else None
self._pending_time = datetime.timedelta(seconds=pending_time)
self._trigger_time = datetime.timedelta(seconds=trigger_time)
self._disarm_after_trigger = disarm_after_trigger
self._pre_trigger_state = self._state
self._state_ts = None
self._pending_time_by_state = {}
for state in SUPPORTED_PENDING_STATES:
self._pending_time_by_state[state] = datetime.timedelta(
seconds=config[state][CONF_PENDING_TIME])
@property
def should_poll(self):
"""Return the plling state."""
@@ -86,23 +118,27 @@ class ManualAlarm(alarm.AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
if self._state in (STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY) and \
self._pending_time and self._state_ts + self._pending_time > \
dt_util.utcnow():
return STATE_ALARM_PENDING
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._state_ts + self._pending_time > dt_util.utcnow():
if self._within_pending_time(self._state):
return STATE_ALARM_PENDING
elif (self._state_ts + self._pending_time +
elif (self._state_ts + self._pending_time_by_state[self._state] +
self._trigger_time) < dt_util.utcnow():
if self._disarm_after_trigger:
return STATE_ALARM_DISARMED
return self._pre_trigger_state
else:
self._state = self._pre_trigger_state
return self._state
if self._state in SUPPORTED_PENDING_STATES and \
self._within_pending_time(self._state):
return STATE_ALARM_PENDING
return self._state
def _within_pending_time(self, state):
pending_time = self._pending_time_by_state[state]
return self._state_ts + pending_time > dt_util.utcnow()
@property
def code_format(self):
"""One or more characters."""
@@ -122,44 +158,47 @@ class ManualAlarm(alarm.AlarmControlPanel):
if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
return
self._state = STATE_ALARM_ARMED_HOME
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
if self._pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
self._update_state(STATE_ALARM_ARMED_HOME)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
return
self._state = STATE_ALARM_ARMED_AWAY
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
self._update_state(STATE_ALARM_ARMED_AWAY)
if self._pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
if not self._validate_code(code, STATE_ALARM_ARMED_NIGHT):
return
self._update_state(STATE_ALARM_ARMED_NIGHT)
def alarm_trigger(self, code=None):
"""Send alarm trigger command. No code needed."""
self._pre_trigger_state = self._state
self._state = STATE_ALARM_TRIGGERED
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
self._state = state
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
if self._trigger_time:
pending_time = self._pending_time_by_state[state]
if state == STATE_ALARM_TRIGGERED and self._trigger_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
self._state_ts + pending_time)
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time + self._trigger_time)
self._state_ts + self._trigger_time + pending_time)
elif state in SUPPORTED_PENDING_STATES and pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
def _validate_code(self, code, state):
"""Validate given code."""
@@ -167,3 +206,13 @@ class ManualAlarm(alarm.AlarmControlPanel):
if not check:
_LOGGER.warning("Invalid code given for %s", state)
return check
@property
def device_state_attributes(self):
"""Return the state attributes."""
state_attr = {}
if self.state == STATE_ALARM_PENDING:
state_attr[ATTR_POST_PENDING_STATE] = self._state
return state_attr

View File

@@ -0,0 +1,291 @@
"""
Support for manual alarms controllable via MQTT.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.manual_mqtt/
"""
import asyncio
import copy
import datetime
import logging
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED,
CONF_PLATFORM, CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
CONF_DISARM_AFTER_TRIGGER)
import homeassistant.components.mqtt as mqtt
from homeassistant.helpers.event import async_track_state_change
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_point_in_time
CONF_PAYLOAD_DISARM = 'payload_disarm'
CONF_PAYLOAD_ARM_HOME = 'payload_arm_home'
CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away'
CONF_PAYLOAD_ARM_NIGHT = 'payload_arm_night'
DEFAULT_ALARM_NAME = 'HA Alarm'
DEFAULT_PENDING_TIME = 60
DEFAULT_TRIGGER_TIME = 120
DEFAULT_DISARM_AFTER_TRIGGER = False
DEFAULT_ARM_AWAY = 'ARM_AWAY'
DEFAULT_ARM_HOME = 'ARM_HOME'
DEFAULT_ARM_NIGHT = 'ARM_NIGHT'
DEFAULT_DISARM = 'DISARM'
SUPPORTED_PENDING_STATES = [STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_TRIGGERED]
ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
config = copy.deepcopy(config)
for state in SUPPORTED_PENDING_STATES:
if CONF_PENDING_TIME not in config[state]:
config[state][CONF_PENDING_TIME] = config[CONF_PENDING_TIME]
return config
STATE_SETTING_SCHEMA = vol.Schema({
vol.Optional(CONF_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0))
})
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PLATFORM): 'manual_mqtt',
vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string,
vol.Optional(CONF_CODE): cv.string,
vol.Optional(CONF_PENDING_TIME, default=DEFAULT_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.Optional(CONF_TRIGGER_TIME, default=DEFAULT_TRIGGER_TIME):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_DISARM_AFTER_TRIGGER,
default=DEFAULT_DISARM_AFTER_TRIGGER): cv.boolean,
vol.Optional(STATE_ALARM_ARMED_AWAY, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_HOME, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_TRIGGERED, default={}): STATE_SETTING_SCHEMA,
vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Required(mqtt.CONF_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string,
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
}), _state_validator))
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the manual MQTT alarm platform."""
add_devices([ManualMQTTAlarm(
hass,
config[CONF_NAME],
config.get(CONF_CODE),
config.get(CONF_PENDING_TIME, DEFAULT_PENDING_TIME),
config.get(CONF_TRIGGER_TIME, DEFAULT_TRIGGER_TIME),
config.get(CONF_DISARM_AFTER_TRIGGER, DEFAULT_DISARM_AFTER_TRIGGER),
config.get(mqtt.CONF_STATE_TOPIC),
config.get(mqtt.CONF_COMMAND_TOPIC),
config.get(mqtt.CONF_QOS),
config.get(CONF_PAYLOAD_DISARM),
config.get(CONF_PAYLOAD_ARM_HOME),
config.get(CONF_PAYLOAD_ARM_AWAY),
config.get(CONF_PAYLOAD_ARM_NIGHT),
config)])
class ManualMQTTAlarm(alarm.AlarmControlPanel):
"""
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
triggered for 'trigger_time', after that we return to the previous state
or disarm if `disarm_after_trigger` is true.
"""
def __init__(self, hass, name, code, pending_time,
trigger_time, disarm_after_trigger,
state_topic, command_topic, qos,
payload_disarm, payload_arm_home, payload_arm_away,
payload_arm_night, config):
"""Init the manual MQTT alarm panel."""
self._state = STATE_ALARM_DISARMED
self._hass = hass
self._name = name
self._code = str(code) if code else None
self._pending_time = datetime.timedelta(seconds=pending_time)
self._trigger_time = datetime.timedelta(seconds=trigger_time)
self._disarm_after_trigger = disarm_after_trigger
self._pre_trigger_state = self._state
self._state_ts = None
self._pending_time_by_state = {}
for state in SUPPORTED_PENDING_STATES:
self._pending_time_by_state[state] = datetime.timedelta(
seconds=config[state][CONF_PENDING_TIME])
self._state_topic = state_topic
self._command_topic = command_topic
self._qos = qos
self._payload_disarm = payload_disarm
self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away
self._payload_arm_night = payload_arm_night
@property
def should_poll(self):
"""Return the polling state."""
return False
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._within_pending_time(self._state):
return STATE_ALARM_PENDING
elif (self._state_ts + self._pending_time_by_state[self._state] +
self._trigger_time) < dt_util.utcnow():
if self._disarm_after_trigger:
return STATE_ALARM_DISARMED
else:
self._state = self._pre_trigger_state
return self._state
if self._state in SUPPORTED_PENDING_STATES and \
self._within_pending_time(self._state):
return STATE_ALARM_PENDING
return self._state
def _within_pending_time(self, state):
pending_time = self._pending_time_by_state[state]
return self._state_ts + pending_time > dt_util.utcnow()
@property
def code_format(self):
"""One or more characters."""
return None if self._code is None else '.+'
def alarm_disarm(self, code=None):
"""Send disarm command."""
if not self._validate_code(code, STATE_ALARM_DISARMED):
return
self._state = STATE_ALARM_DISARMED
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
return
self._update_state(STATE_ALARM_ARMED_HOME)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
return
self._update_state(STATE_ALARM_ARMED_AWAY)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
if not self._validate_code(code, STATE_ALARM_ARMED_NIGHT):
return
self._update_state(STATE_ALARM_ARMED_NIGHT)
def alarm_trigger(self, code=None):
"""Send alarm trigger command. No code needed."""
self._pre_trigger_state = self._state
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
self._state = state
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
pending_time = self._pending_time_by_state[state]
if state == STATE_ALARM_TRIGGERED and self._trigger_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._trigger_time + pending_time)
elif state in SUPPORTED_PENDING_STATES and pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
def _validate_code(self, code, state):
"""Validate given code."""
check = self._code is None or code == self._code
if not check:
_LOGGER.warning("Invalid code given for %s", state)
return check
@property
def device_state_attributes(self):
"""Return the state attributes."""
state_attr = {}
if self.state == STATE_ALARM_PENDING:
state_attr[ATTR_POST_PENDING_STATE] = self._state
return state_attr
def async_added_to_hass(self):
"""Subscribe mqtt events.
This method must be run in the event loop and returns a coroutine.
"""
async_track_state_change(
self.hass, self.entity_id, self._async_state_changed_listener
)
@callback
def message_received(topic, payload, qos):
"""Run when new MQTT message has been received."""
if payload == self._payload_disarm:
self.async_alarm_disarm(self._code)
elif payload == self._payload_arm_home:
self.async_alarm_arm_home(self._code)
elif payload == self._payload_arm_away:
self.async_alarm_arm_away(self._code)
elif payload == self._payload_arm_night:
self.async_alarm_arm_night(self._code)
else:
_LOGGER.warning("Received unexpected payload: %s", payload)
return
return mqtt.async_subscribe(
self.hass, self._command_topic, message_received, self._qos)
@asyncio.coroutine
def _async_state_changed_listener(self, entity_id, old_state, new_state):
"""Publish state change to MQTT."""
mqtt.async_publish(self.hass, self._state_topic, new_state.state,
self._qos, True)

View File

@@ -87,7 +87,7 @@ class MqttAlarm(alarm.AlarmControlPanel):
_LOGGER.warning("Received unexpected payload: %s", payload)
return
self._state = payload
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
return mqtt.async_subscribe(
self.hass, self._state_topic, message_received, self._qos)

View File

@@ -0,0 +1,94 @@
"""
Support for Satel Integra alarm, using ETHM module: https://www.satel.pl/en/ .
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.satel_integra/
"""
import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.satel_integra import (CONF_ARM_HOME_MODE,
DATA_SATEL,
SIGNAL_PANEL_MESSAGE)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['satel_integra']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up for AlarmDecoder alarm panels."""
if not discovery_info:
return
device = SatelIntegraAlarmPanel("Alarm Panel",
discovery_info.get(CONF_ARM_HOME_MODE))
async_add_devices([device])
class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
"""Representation of an AlarmDecoder-based alarm panel."""
def __init__(self, name, arm_home_mode):
"""Initialize the alarm panel."""
self._name = name
self._state = None
self._arm_home_mode = arm_home_mode
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback)
@callback
def _message_callback(self, message):
if message != self._state:
self._state = message
self.async_schedule_update_ha_state()
else:
_LOGGER.warning("Ignoring alarm status message, same state")
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def should_poll(self):
"""Return the polling state."""
return False
@property
def code_format(self):
"""Return the regex for code format or None if no code is required."""
return '^\\d{4,6}$'
@property
def state(self):
"""Return the state of the device."""
return self._state
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if code:
yield from self.hass.data[DATA_SATEL].disarm(code)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if code:
yield from self.hass.data[DATA_SATEL].arm(code)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if code:
yield from self.hass.data[DATA_SATEL].arm(code,
self._arm_home_mode)

View File

@@ -31,6 +31,17 @@ alarm_arm_away:
description: An optional code to arm away the alarm control panel with
example: 1234
alarm_arm_night:
description: Send the alarm the command for arm night
fields:
entity_id:
description: Name of alarm control panel to arm night
example: 'alarm_control_panel.downstairs'
code:
description: An optional code to arm night the alarm control panel with
example: 1234
alarm_trigger:
description: Send the alarm the command for trigger

View File

@@ -15,9 +15,8 @@ from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
EVENT_HOMEASSISTANT_STOP)
import homeassistant.helpers.config_validation as cv
import homeassistant.loader as loader
REQUIREMENTS = ['simplisafe-python==1.0.2']
REQUIREMENTS = ['simplisafe-python==1.0.5']
_LOGGER = logging.getLogger(__name__)
@@ -42,7 +41,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
persistent_notification = loader.get_component('persistent_notification')
simplisafe = SimpliSafeApiInterface()
status = simplisafe.set_credentials(username, password)
if status:
@@ -53,8 +51,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
else:
message = 'Failed to log into SimpliSafe. Check credentials.'
_LOGGER.error(message)
persistent_notification.create(
hass, message,
hass.components.persistent_notification.create(
message,
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
@@ -91,11 +89,11 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
def state(self):
"""Return the state of the device."""
status = self.simplisafe.state()
if status == 'Off':
if status == 'off':
state = STATE_ALARM_DISARMED
elif status == 'Home':
elif status == 'home':
state = STATE_ALARM_ARMED_HOME
elif status == 'Away':
elif status == 'away':
state = STATE_ALARM_ARMED_AWAY
else:
state = STATE_UNKNOWN

View File

@@ -27,20 +27,20 @@ def _get_alarm_state(spc_mode):
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_entities,
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the SPC alarm control panel platform."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_AREAS] is None):
return
entities = [SpcAlarm(hass=hass,
area_id=area['id'],
name=area['name'],
state=_get_alarm_state(area['mode']))
for area in discovery_info[ATTR_DISCOVER_AREAS]]
devices = [SpcAlarm(hass=hass,
area_id=area['id'],
name=area['name'],
state=_get_alarm_state(area['mode']))
for area in discovery_info[ATTR_DISCOVER_AREAS]]
async_add_entities(entities)
async_add_devices(devices)
class SpcAlarm(alarm.AlarmControlPanel):

View File

@@ -13,8 +13,8 @@ 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)
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED,
STATE_ALARM_ARMING, STATE_ALARM_DISARMING, STATE_UNKNOWN, CONF_NAME)
REQUIREMENTS = ['total_connect_client==0.11']
@@ -74,6 +74,12 @@ class TotalConnect(alarm.AlarmControlPanel):
state = STATE_ALARM_ARMED_HOME
elif status == self._client.ARMED_AWAY:
state = STATE_ALARM_ARMED_AWAY
elif status == self._client.ARMED_STAY_NIGHT:
state = STATE_ALARM_ARMED_NIGHT
elif status == self._client.ARMING:
state = STATE_ALARM_ARMING
elif status == self._client.DISARMING:
state = STATE_ALARM_DISARMING
else:
state = STATE_UNKNOWN
@@ -90,3 +96,7 @@ class TotalConnect(alarm.AlarmControlPanel):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._client.arm_away()
def alarm_arm_night(self, code=None):
"""Send arm night command."""
self._client.arm_stay_night()

View File

@@ -15,7 +15,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
REQUIREMENTS = ['alarmdecoder==0.12.1.0']
REQUIREMENTS = ['alarmdecoder==0.12.3']
_LOGGER = logging.getLogger(__name__)

View File

@@ -1,335 +0,0 @@
"""
Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import copy
import enum
import logging
import uuid
from datetime import datetime
import voluptuous as vol
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
_LOGGER = logging.getLogger(__name__)
INTENTS_API_ENDPOINT = '/api/alexa'
FLASH_BRIEFINGS_API_ENDPOINT = '/api/alexa/flash_briefings/{briefing_id}'
CONF_ACTION = 'action'
CONF_CARD = 'card'
CONF_INTENTS = 'intents'
CONF_SPEECH = 'speech'
CONF_TYPE = 'type'
CONF_TITLE = 'title'
CONF_CONTENT = 'content'
CONF_TEXT = 'text'
CONF_FLASH_BRIEFINGS = 'flash_briefings'
CONF_UID = 'uid'
CONF_TITLE = 'title'
CONF_AUDIO = 'audio'
CONF_TEXT = 'text'
CONF_DISPLAY_URL = 'display_url'
ATTR_UID = 'uid'
ATTR_UPDATE_DATE = 'updateDate'
ATTR_TITLE_TEXT = 'titleText'
ATTR_STREAM_URL = 'streamUrl'
ATTR_MAIN_TEXT = 'mainText'
ATTR_REDIRECTION_URL = 'redirectionURL'
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.0Z'
DOMAIN = 'alexa'
DEPENDENCIES = ['http']
class SpeechType(enum.Enum):
"""The Alexa speech types."""
plaintext = "PlainText"
ssml = "SSML"
class CardType(enum.Enum):
"""The Alexa card types."""
simple = "Simple"
link_account = "LinkAccount"
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
CONF_INTENTS: {
cv.string: {
vol.Optional(CONF_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_CARD): {
vol.Required(CONF_TYPE): cv.enum(CardType),
vol.Required(CONF_TITLE): cv.template,
vol.Required(CONF_CONTENT): cv.template,
},
vol.Optional(CONF_SPEECH): {
vol.Required(CONF_TYPE): cv.enum(SpeechType),
vol.Required(CONF_TEXT): cv.template,
}
}
},
CONF_FLASH_BRIEFINGS: {
cv.string: vol.All(cv.ensure_list, [{
vol.Required(CONF_UID, default=str(uuid.uuid4())): cv.string,
vol.Required(CONF_TITLE): cv.template,
vol.Optional(CONF_AUDIO): cv.template,
vol.Required(CONF_TEXT, default=""): cv.template,
vol.Optional(CONF_DISPLAY_URL): cv.template,
}]),
}
}
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Activate Alexa component."""
intents = config[DOMAIN].get(CONF_INTENTS, {})
flash_briefings = config[DOMAIN].get(CONF_FLASH_BRIEFINGS, {})
hass.http.register_view(AlexaIntentsView(hass, intents))
hass.http.register_view(AlexaFlashBriefingView(hass, flash_briefings))
return True
class AlexaIntentsView(HomeAssistantView):
"""Handle Alexa requests."""
url = INTENTS_API_ENDPOINT
name = 'api:alexa'
def __init__(self, hass, intents):
"""Initialize Alexa view."""
super().__init__()
intents = copy.deepcopy(intents)
template.attach(hass, intents)
for name, intent in intents.items():
if CONF_ACTION in intent:
intent[CONF_ACTION] = script.Script(
hass, intent[CONF_ACTION], "Alexa intent {}".format(name))
self.intents = intents
@asyncio.coroutine
def post(self, request):
"""Handle Alexa."""
data = yield from request.json()
_LOGGER.debug('Received Alexa request: %s', data)
req = data.get('request')
if req is None:
_LOGGER.error('Received invalid data from Alexa: %s', data)
return self.json_message('Expected request value not received',
HTTP_BAD_REQUEST)
req_type = req['type']
if req_type == 'SessionEndedRequest':
return None
intent = req.get('intent')
response = AlexaResponse(request.app['hass'], intent)
if req_type == 'LaunchRequest':
response.add_speech(
SpeechType.plaintext,
"Hello, and welcome to the future. How may I help?")
return self.json(response)
if req_type != 'IntentRequest':
_LOGGER.warning('Received unsupported request: %s', req_type)
return self.json_message(
'Received unsupported request: {}'.format(req_type),
HTTP_BAD_REQUEST)
intent_name = intent['name']
config = self.intents.get(intent_name)
if config is None:
_LOGGER.warning('Received unknown intent %s', intent_name)
response.add_speech(
SpeechType.plaintext,
"This intent is not yet configured within Home Assistant.")
return self.json(response)
speech = config.get(CONF_SPEECH)
card = config.get(CONF_CARD)
action = config.get(CONF_ACTION)
if action is not None:
yield from action.async_run(response.variables)
# pylint: disable=unsubscriptable-object
if speech is not None:
response.add_speech(speech[CONF_TYPE], speech[CONF_TEXT])
if card is not None:
response.add_card(card[CONF_TYPE], card[CONF_TITLE],
card[CONF_CONTENT])
return self.json(response)
class AlexaResponse(object):
"""Help generating the response for Alexa."""
def __init__(self, hass, intent=None):
"""Initialize the response."""
self.hass = hass
self.speech = None
self.card = None
self.reprompt = None
self.session_attributes = {}
self.should_end_session = True
self.variables = {}
if intent is not None and 'slots' in intent:
for key, value in intent['slots'].items():
if 'value' in value:
underscored_key = key.replace('.', '_')
self.variables[underscored_key] = value['value']
def add_card(self, card_type, title, content):
"""Add a card to the response."""
assert self.card is None
card = {
"type": card_type.value
}
if card_type == CardType.link_account:
self.card = card
return
card["title"] = title.async_render(self.variables)
card["content"] = content.async_render(self.variables)
self.card = card
def add_speech(self, speech_type, text):
"""Add speech to the response."""
assert self.speech is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
if isinstance(text, template.Template):
text = text.async_render(self.variables)
self.speech = {
'type': speech_type.value,
key: text
}
def add_reprompt(self, speech_type, text):
"""Add reprompt if user does not answer."""
assert self.reprompt is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
self.reprompt = {
'type': speech_type.value,
key: text.async_render(self.variables)
}
def as_dict(self):
"""Return response in an Alexa valid dict."""
response = {
'shouldEndSession': self.should_end_session
}
if self.card is not None:
response['card'] = self.card
if self.speech is not None:
response['outputSpeech'] = self.speech
if self.reprompt is not None:
response['reprompt'] = {
'outputSpeech': self.reprompt
}
return {
'version': '1.0',
'sessionAttributes': self.session_attributes,
'response': response,
}
class AlexaFlashBriefingView(HomeAssistantView):
"""Handle Alexa Flash Briefing skill requests."""
url = FLASH_BRIEFINGS_API_ENDPOINT
name = 'api:alexa:flash_briefings'
def __init__(self, hass, flash_briefings):
"""Initialize Alexa view."""
super().__init__()
self.flash_briefings = copy.deepcopy(flash_briefings)
template.attach(hass, self.flash_briefings)
@callback
def get(self, request, briefing_id):
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug('Received Alexa flash briefing request for: %s',
briefing_id)
if self.flash_briefings.get(briefing_id) is None:
err = 'No configured Alexa flash briefing was found for: %s'
_LOGGER.error(err, briefing_id)
return b'', 404
briefing = []
for item in self.flash_briefings.get(briefing_id, []):
output = {}
if item.get(CONF_TITLE) is not None:
if isinstance(item.get(CONF_TITLE), template.Template):
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render()
else:
output[ATTR_TITLE_TEXT] = item.get(CONF_TITLE)
if item.get(CONF_TEXT) is not None:
if isinstance(item.get(CONF_TEXT), template.Template):
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render()
else:
output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT)
if item.get(CONF_UID) is not None:
output[ATTR_UID] = item.get(CONF_UID)
if item.get(CONF_AUDIO) is not None:
if isinstance(item.get(CONF_AUDIO), template.Template):
output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render()
else:
output[ATTR_STREAM_URL] = item.get(CONF_AUDIO)
if item.get(CONF_DISPLAY_URL) is not None:
if isinstance(item.get(CONF_DISPLAY_URL),
template.Template):
output[ATTR_REDIRECTION_URL] = \
item[CONF_DISPLAY_URL].async_render()
else:
output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL)
output[ATTR_UPDATE_DATE] = datetime.now().strftime(DATE_FORMAT)
briefing.append(output)
return self.json(briefing)

View File

@@ -0,0 +1,52 @@
"""
Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from .const import (
DOMAIN, CONF_UID, CONF_TITLE, CONF_AUDIO, CONF_TEXT, CONF_DISPLAY_URL)
from . import flash_briefings, intent
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
CONF_FLASH_BRIEFINGS = 'flash_briefings'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
CONF_FLASH_BRIEFINGS: {
cv.string: vol.All(cv.ensure_list, [{
vol.Optional(CONF_UID): cv.string,
vol.Required(CONF_TITLE): cv.template,
vol.Optional(CONF_AUDIO): cv.template,
vol.Required(CONF_TEXT, default=""): cv.template,
vol.Optional(CONF_DISPLAY_URL): cv.template,
}]),
}
}
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Activate Alexa component."""
config = config.get(DOMAIN, {})
flash_briefings_config = config.get(CONF_FLASH_BRIEFINGS)
intent.async_setup(hass)
if flash_briefings_config:
flash_briefings.async_setup(hass, flash_briefings_config)
return True

View File

@@ -0,0 +1,18 @@
"""Constants for the Alexa integration."""
DOMAIN = 'alexa'
# Flash briefing constants
CONF_UID = 'uid'
CONF_TITLE = 'title'
CONF_AUDIO = 'audio'
CONF_TEXT = 'text'
CONF_DISPLAY_URL = 'display_url'
ATTR_UID = 'uid'
ATTR_UPDATE_DATE = 'updateDate'
ATTR_TITLE_TEXT = 'titleText'
ATTR_STREAM_URL = 'streamUrl'
ATTR_MAIN_TEXT = 'mainText'
ATTR_REDIRECTION_URL = 'redirectionURL'
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.0Z'

View File

@@ -0,0 +1,96 @@
"""
Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import copy
import logging
from datetime import datetime
import uuid
from homeassistant.core import callback
from homeassistant.helpers import template
from homeassistant.components import http
from .const import (
CONF_UID, CONF_TITLE, CONF_AUDIO, CONF_TEXT, CONF_DISPLAY_URL, ATTR_UID,
ATTR_UPDATE_DATE, ATTR_TITLE_TEXT, ATTR_STREAM_URL, ATTR_MAIN_TEXT,
ATTR_REDIRECTION_URL, DATE_FORMAT)
_LOGGER = logging.getLogger(__name__)
FLASH_BRIEFINGS_API_ENDPOINT = '/api/alexa/flash_briefings/{briefing_id}'
@callback
def async_setup(hass, flash_briefing_config):
"""Activate Alexa component."""
hass.http.register_view(
AlexaFlashBriefingView(hass, flash_briefing_config))
class AlexaFlashBriefingView(http.HomeAssistantView):
"""Handle Alexa Flash Briefing skill requests."""
url = FLASH_BRIEFINGS_API_ENDPOINT
name = 'api:alexa:flash_briefings'
def __init__(self, hass, flash_briefings):
"""Initialize Alexa view."""
super().__init__()
self.flash_briefings = copy.deepcopy(flash_briefings)
template.attach(hass, self.flash_briefings)
@callback
def get(self, request, briefing_id):
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug('Received Alexa flash briefing request for: %s',
briefing_id)
if self.flash_briefings.get(briefing_id) is None:
err = 'No configured Alexa flash briefing was found for: %s'
_LOGGER.error(err, briefing_id)
return b'', 404
briefing = []
for item in self.flash_briefings.get(briefing_id, []):
output = {}
if item.get(CONF_TITLE) is not None:
if isinstance(item.get(CONF_TITLE), template.Template):
output[ATTR_TITLE_TEXT] = item[CONF_TITLE].async_render()
else:
output[ATTR_TITLE_TEXT] = item.get(CONF_TITLE)
if item.get(CONF_TEXT) is not None:
if isinstance(item.get(CONF_TEXT), template.Template):
output[ATTR_MAIN_TEXT] = item[CONF_TEXT].async_render()
else:
output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT)
uid = item.get(CONF_UID)
if uid is None:
uid = str(uuid.uuid4())
output[ATTR_UID] = uid
if item.get(CONF_AUDIO) is not None:
if isinstance(item.get(CONF_AUDIO), template.Template):
output[ATTR_STREAM_URL] = item[CONF_AUDIO].async_render()
else:
output[ATTR_STREAM_URL] = item.get(CONF_AUDIO)
if item.get(CONF_DISPLAY_URL) is not None:
if isinstance(item.get(CONF_DISPLAY_URL),
template.Template):
output[ATTR_REDIRECTION_URL] = \
item[CONF_DISPLAY_URL].async_render()
else:
output[ATTR_REDIRECTION_URL] = item.get(CONF_DISPLAY_URL)
output[ATTR_UPDATE_DATE] = datetime.now().strftime(DATE_FORMAT)
briefing.append(output)
return self.json(briefing)

View File

@@ -0,0 +1,204 @@
"""
Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import enum
import logging
from homeassistant.core import callback
from homeassistant.const import HTTP_BAD_REQUEST
from homeassistant.helpers import intent
from homeassistant.components import http
from .const import DOMAIN
INTENTS_API_ENDPOINT = '/api/alexa'
_LOGGER = logging.getLogger(__name__)
class SpeechType(enum.Enum):
"""The Alexa speech types."""
plaintext = "PlainText"
ssml = "SSML"
SPEECH_MAPPINGS = {
'plain': SpeechType.plaintext,
'ssml': SpeechType.ssml,
}
class CardType(enum.Enum):
"""The Alexa card types."""
simple = "Simple"
link_account = "LinkAccount"
@callback
def async_setup(hass):
"""Activate Alexa component."""
hass.http.register_view(AlexaIntentsView)
class AlexaIntentsView(http.HomeAssistantView):
"""Handle Alexa requests."""
url = INTENTS_API_ENDPOINT
name = 'api:alexa'
@asyncio.coroutine
def post(self, request):
"""Handle Alexa."""
hass = request.app['hass']
data = yield from request.json()
_LOGGER.debug('Received Alexa request: %s', data)
req = data.get('request')
if req is None:
_LOGGER.error('Received invalid data from Alexa: %s', data)
return self.json_message('Expected request value not received',
HTTP_BAD_REQUEST)
req_type = req['type']
if req_type == 'SessionEndedRequest':
return None
alexa_intent_info = req.get('intent')
alexa_response = AlexaResponse(hass, alexa_intent_info)
if req_type != 'IntentRequest' and req_type != 'LaunchRequest':
_LOGGER.warning('Received unsupported request: %s', req_type)
return self.json_message(
'Received unsupported request: {}'.format(req_type),
HTTP_BAD_REQUEST)
if req_type == 'LaunchRequest':
intent_name = data.get('session', {}) \
.get('application', {}) \
.get('applicationId')
else:
intent_name = alexa_intent_info['name']
try:
intent_response = yield from intent.async_handle(
hass, DOMAIN, intent_name,
{key: {'value': value} for key, value
in alexa_response.variables.items()})
except intent.UnknownIntent as err:
_LOGGER.warning('Received unknown intent %s', intent_name)
alexa_response.add_speech(
SpeechType.plaintext,
"This intent is not yet configured within Home Assistant.")
return self.json(alexa_response)
except intent.InvalidSlotInfo as err:
_LOGGER.error('Received invalid slot data from Alexa: %s', err)
return self.json_message('Invalid slot data received',
HTTP_BAD_REQUEST)
except intent.IntentError:
_LOGGER.exception('Error handling request for %s', intent_name)
return self.json_message('Error handling intent', HTTP_BAD_REQUEST)
for intent_speech, alexa_speech in SPEECH_MAPPINGS.items():
if intent_speech in intent_response.speech:
alexa_response.add_speech(
alexa_speech,
intent_response.speech[intent_speech]['speech'])
break
if 'simple' in intent_response.card:
alexa_response.add_card(
CardType.simple, intent_response.card['simple']['title'],
intent_response.card['simple']['content'])
return self.json(alexa_response)
class AlexaResponse(object):
"""Help generating the response for Alexa."""
def __init__(self, hass, intent_info):
"""Initialize the response."""
self.hass = hass
self.speech = None
self.card = None
self.reprompt = None
self.session_attributes = {}
self.should_end_session = True
self.variables = {}
# Intent is None if request was a LaunchRequest or SessionEndedRequest
if intent_info is not None:
for key, value in intent_info.get('slots', {}).items():
if 'value' in value:
underscored_key = key.replace('.', '_')
self.variables[underscored_key] = value['value']
def add_card(self, card_type, title, content):
"""Add a card to the response."""
assert self.card is None
card = {
"type": card_type.value
}
if card_type == CardType.link_account:
self.card = card
return
card["title"] = title
card["content"] = content
self.card = card
def add_speech(self, speech_type, text):
"""Add speech to the response."""
assert self.speech is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
self.speech = {
'type': speech_type.value,
key: text
}
def add_reprompt(self, speech_type, text):
"""Add reprompt if user does not answer."""
assert self.reprompt is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
self.reprompt = {
'type': speech_type.value,
key: text.async_render(self.variables)
}
def as_dict(self):
"""Return response in an Alexa valid dict."""
response = {
'shouldEndSession': self.should_end_session
}
if self.card is not None:
response['card'] = self.card
if self.speech is not None:
response['outputSpeech'] = self.speech
if self.reprompt is not None:
response['reprompt'] = {
'outputSpeech': self.reprompt
}
return {
'version': '1.0',
'sessionAttributes': self.session_attributes,
'response': response,
}

View File

@@ -0,0 +1,177 @@
"""Support for alexa Smart Home Skill API."""
import asyncio
import logging
from uuid import uuid4
from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
from homeassistant.components import switch, light
from homeassistant.util.decorator import Registry
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
ATTR_HEADER = 'header'
ATTR_NAME = 'name'
ATTR_NAMESPACE = 'namespace'
ATTR_MESSAGE_ID = 'messageId'
ATTR_PAYLOAD = 'payload'
ATTR_PAYLOAD_VERSION = 'payloadVersion'
MAPPING_COMPONENT = {
switch.DOMAIN: ['SWITCH', ('turnOff', 'turnOn'), None],
light.DOMAIN: [
'LIGHT', ('turnOff', 'turnOn'), {
light.SUPPORT_BRIGHTNESS: 'setPercentage'
}
],
}
@asyncio.coroutine
def async_handle_message(hass, message):
"""Handle incoming API messages."""
assert int(message[ATTR_HEADER][ATTR_PAYLOAD_VERSION]) == 2
# Do we support this API request?
funct_ref = HANDLERS.get(message[ATTR_HEADER][ATTR_NAME])
if not funct_ref:
_LOGGER.warning(
"Unsupported API request %s", message[ATTR_HEADER][ATTR_NAME])
return api_error(message)
return (yield from funct_ref(hass, message))
def api_message(name, namespace, payload=None):
"""Create a API formatted response message.
Async friendly.
"""
payload = payload or {}
return {
ATTR_HEADER: {
ATTR_MESSAGE_ID: str(uuid4()),
ATTR_NAME: name,
ATTR_NAMESPACE: namespace,
ATTR_PAYLOAD_VERSION: '2',
},
ATTR_PAYLOAD: payload,
}
def api_error(request, exc='DriverInternalError'):
"""Create a API formatted error response.
Async friendly.
"""
return api_message(exc, request[ATTR_HEADER][ATTR_NAMESPACE])
@HANDLERS.register('DiscoverAppliancesRequest')
@asyncio.coroutine
def async_api_discovery(hass, request):
"""Create a API formatted discovery response.
Async friendly.
"""
discovered_appliances = []
for entity in hass.states.async_all():
class_data = MAPPING_COMPONENT.get(entity.domain)
if not class_data:
continue
appliance = {
'actions': [],
'applianceTypes': [class_data[0]],
'additionalApplianceDetails': {},
'applianceId': entity.entity_id.replace('.', '#'),
'friendlyDescription': '',
'friendlyName': entity.name,
'isReachable': True,
'manufacturerName': 'Unknown',
'modelName': 'Unknown',
'version': 'Unknown',
}
# static actions
if class_data[1]:
appliance['actions'].extend(list(class_data[1]))
# dynamic actions
if class_data[2]:
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
for feature, action_name in class_data[2].items():
if feature & supported > 0:
appliance['actions'].append(action_name)
discovered_appliances.append(appliance)
return api_message(
'DiscoverAppliancesResponse', 'Alexa.ConnectedHome.Discovery',
payload={'discoveredAppliances': discovered_appliances})
def extract_entity(funct):
"""Decorator for extract entity object from request."""
@asyncio.coroutine
def async_api_entity_wrapper(hass, request):
"""Process a turn on request."""
entity_id = \
request[ATTR_PAYLOAD]['appliance']['applianceId'].replace('#', '.')
# extract state object
entity = hass.states.get(entity_id)
if not entity:
_LOGGER.error("Can't process %s for %s",
request[ATTR_HEADER][ATTR_NAME], entity_id)
return api_error(request)
return (yield from funct(hass, request, entity))
return async_api_entity_wrapper
@HANDLERS.register('TurnOnRequest')
@extract_entity
@asyncio.coroutine
def async_api_turn_on(hass, request, entity):
"""Process a turn on request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=True)
return api_message('TurnOnConfirmation', 'Alexa.ConnectedHome.Control')
@HANDLERS.register('TurnOffRequest')
@extract_entity
@asyncio.coroutine
def async_api_turn_off(hass, request, entity):
"""Process a turn off request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_OFF, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=True)
return api_message('TurnOffConfirmation', 'Alexa.ConnectedHome.Control')
@HANDLERS.register('SetPercentageRequest')
@extract_entity
@asyncio.coroutine
def async_api_set_percentage(hass, request, entity):
"""Process a set percentage request."""
if entity.domain == light.DOMAIN:
brightness = request[ATTR_PAYLOAD]['percentageState']['value']
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_BRIGHTNESS: brightness,
}, blocking=True)
else:
return api_error(request)
return api_message(
'SetPercentageConfirmation', 'Alexa.ConnectedHome.Control')

View File

@@ -11,14 +11,13 @@ import aiohttp
import voluptuous as vol
from requests.exceptions import HTTPError, ConnectTimeout
import homeassistant.loader as loader
from homeassistant.const import (
CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
CONF_SENSORS, CONF_SCAN_INTERVAL, HTTP_BASIC_AUTHENTICATION)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['amcrest==1.2.0']
REQUIREMENTS = ['amcrest==1.2.1']
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
@@ -92,7 +91,6 @@ def setup(hass, config):
amcrest_cams = config[DOMAIN]
persistent_notification = loader.get_component('persistent_notification')
for device in amcrest_cams:
camera = AmcrestCamera(device.get(CONF_HOST),
device.get(CONF_PORT),
@@ -103,8 +101,8 @@ def setup(hass, config):
except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
persistent_notification.create(
hass, 'Error: {}<br />'
hass.components.persistent_notification.create(
'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(ex),
title=NOTIFICATION_TITLE,

View File

@@ -263,7 +263,7 @@ class AndroidIPCamEntity(Entity):
"""Update callback."""
if self._host != host:
return
self.hass.async_add_job(self.async_update_ha_state(True))
self.async_schedule_update_ha_state(True)
async_dispatcher_connect(
self.hass, SIGNAL_UPDATE_DATA, async_ipcam_update)

View File

@@ -13,7 +13,7 @@ import async_timeout
import homeassistant.core as ha
import homeassistant.remote as rem
from homeassistant.bootstrap import ERROR_LOG_FILENAME
from homeassistant.bootstrap import DATA_LOGGING
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND,
@@ -51,8 +51,9 @@ def setup(hass, config):
hass.http.register_view(APIComponentsView)
hass.http.register_view(APITemplateView)
hass.http.register_static_path(
URL_API_ERROR_LOG, hass.config.path(ERROR_LOG_FILENAME), False)
log_path = hass.data.get(DATA_LOGGING, None)
if log_path:
hass.http.register_static_path(URL_API_ERROR_LOG, log_path, False)
return True

View File

@@ -5,13 +5,12 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/apiai/
"""
import asyncio
import copy
import logging
import voluptuous as vol
from homeassistant.const import PROJECT_NAME, HTTP_BAD_REQUEST
from homeassistant.helpers import template, script, config_validation as cv
from homeassistant.helpers import intent, template
from homeassistant.components.http import HomeAssistantView
_LOGGER = logging.getLogger(__name__)
@@ -29,24 +28,14 @@ DOMAIN = 'apiai'
DEPENDENCIES = ['http']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
CONF_INTENTS: {
cv.string: {
vol.Optional(CONF_SPEECH): cv.template,
vol.Optional(CONF_ACTION): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_ASYNC_ACTION,
default=DEFAULT_CONF_ASYNC_ACTION): cv.boolean
}
}
}
DOMAIN: {}
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Activate API.AI component."""
intents = config[DOMAIN].get(CONF_INTENTS, {})
hass.http.register_view(ApiaiIntentsView(hass, intents))
hass.http.register_view(ApiaiIntentsView)
return True
@@ -57,24 +46,10 @@ class ApiaiIntentsView(HomeAssistantView):
url = INTENTS_API_ENDPOINT
name = 'api:apiai'
def __init__(self, hass, intents):
"""Initialize API.AI view."""
super().__init__()
self.hass = hass
intents = copy.deepcopy(intents)
template.attach(hass, intents)
for name, intent in intents.items():
if CONF_ACTION in intent:
intent[CONF_ACTION] = script.Script(
hass, intent[CONF_ACTION], "Apiai intent {}".format(name))
self.intents = intents
@asyncio.coroutine
def post(self, request):
"""Handle API.AI."""
hass = request.app['hass']
data = yield from request.json()
_LOGGER.debug("Received api.ai request: %s", data)
@@ -91,55 +66,41 @@ class ApiaiIntentsView(HomeAssistantView):
if action_incomplete:
return None
# use intent to no mix HASS actions with this parameter
intent = req.get('action')
action = req.get('action')
parameters = req.get('parameters')
# contexts = req.get('contexts')
response = ApiaiResponse(parameters)
apiai_response = ApiaiResponse(parameters)
# Default Welcome Intent
# Maybe is better to handle this in api.ai directly?
#
# if intent == 'input.welcome':
# response.add_speech(
# "Hello, and welcome to the future. How may I help?")
# return self.json(response)
if intent == "":
if action == "":
_LOGGER.warning("Received intent with empty action")
response.add_speech(
apiai_response.add_speech(
"You have not defined an action in your api.ai intent.")
return self.json(response)
return self.json(apiai_response)
config = self.intents.get(intent)
try:
intent_response = yield from intent.async_handle(
hass, DOMAIN, action,
{key: {'value': value} for key, value
in parameters.items()})
if config is None:
_LOGGER.warning("Received unknown intent %s", intent)
response.add_speech(
"Intent '%s' is not yet configured within Home Assistant." %
intent)
return self.json(response)
except intent.UnknownIntent as err:
_LOGGER.warning('Received unknown intent %s', action)
apiai_response.add_speech(
"This intent is not yet configured within Home Assistant.")
return self.json(apiai_response)
speech = config.get(CONF_SPEECH)
action = config.get(CONF_ACTION)
async_action = config.get(CONF_ASYNC_ACTION)
except intent.InvalidSlotInfo as err:
_LOGGER.error('Received invalid slot data: %s', err)
return self.json_message('Invalid slot data received',
HTTP_BAD_REQUEST)
except intent.IntentError:
_LOGGER.exception('Error handling request for %s', action)
return self.json_message('Error handling intent', HTTP_BAD_REQUEST)
if action is not None:
# API.AI expects a response in less than 5s
if async_action:
# Do not wait for the action to be executed.
# Needed if the action will take longer than 5s to execute
self.hass.async_add_job(action.async_run(response.parameters))
else:
# Wait for the action to be executed so we can use results to
# render the answer
yield from action.async_run(response.parameters)
if 'plain' in intent_response.speech:
apiai_response.add_speech(
intent_response.speech['plain']['speech'])
# pylint: disable=unsubscriptable-object
if speech is not None:
response.add_speech(speech)
return self.json(response)
return self.json(apiai_response)
class ApiaiResponse(object):

View File

@@ -10,15 +10,15 @@ import logging
import voluptuous as vol
from typing import Union, TypeVar, Sequence
from homeassistant.const import (CONF_HOST, CONF_NAME, ATTR_ENTITY_ID)
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import discovery
from homeassistant.components.discovery import SERVICE_APPLE_TV
from homeassistant.loader import get_component
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyatv==0.3.2']
REQUIREMENTS = ['pyatv==0.3.5']
_LOGGER = logging.getLogger(__name__)
@@ -46,8 +46,19 @@ NOTIFICATION_AUTH_TITLE = 'Apple TV Authentication'
NOTIFICATION_SCAN_ID = 'apple_tv_scan_notification'
NOTIFICATION_SCAN_TITLE = 'Apple TV Scan'
T = TypeVar('T')
# This version of ensure_list interprets an empty dict as no value
def ensure_list(value: Union[T, Sequence[T]]) -> Sequence[T]:
"""Wrap value in list if it is not one."""
if value is None or (isinstance(value, dict) and not value):
return []
return value if isinstance(value, list) else [value]
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.All(cv.ensure_list, [vol.Schema({
DOMAIN: vol.All(ensure_list, [vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_LOGIN_ID): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -66,27 +77,24 @@ APPLE_TV_AUTHENTICATE_SCHEMA = vol.Schema({
def request_configuration(hass, config, atv, credentials):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
configurator = hass.components.configurator
@asyncio.coroutine
def configuration_callback(callback_data):
"""Handle the submitted configuration."""
from pyatv import exceptions
pin = callback_data.get('pin')
notification = get_component('persistent_notification')
try:
yield from atv.airplay.finish_authentication(pin)
notification.async_create(
hass,
hass.components.persistent_notification.async_create(
'Authentication succeeded!<br /><br />Add the following '
'to credentials: in your apple_tv configuration:<br /><br />'
'{0}'.format(credentials),
title=NOTIFICATION_AUTH_TITLE,
notification_id=NOTIFICATION_AUTH_ID)
except exceptions.DeviceAuthenticationError as ex:
notification.async_create(
hass,
hass.components.persistent_notification.async_create(
'Authentication failed! Did you enter correct PIN?<br /><br />'
'Details: {0}'.format(ex),
title=NOTIFICATION_AUTH_TITLE,
@@ -95,7 +103,7 @@ def request_configuration(hass, config, atv, credentials):
hass.async_add_job(configurator.request_done, instance)
instance = configurator.request_config(
hass, 'Apple TV Authentication', configuration_callback,
'Apple TV Authentication', configuration_callback,
description='Please enter PIN code shown on screen.',
submit_caption='Confirm',
fields=[{'id': 'pin', 'name': 'PIN Code', 'type': 'password'}]
@@ -119,9 +127,7 @@ def scan_for_apple_tvs(hass):
if not devices:
devices = ['No device(s) found']
notification = get_component('persistent_notification')
notification.async_create(
hass,
hass.components.persistent_notification.async_create(
'The following devices were found:<br /><br />' +
'<br /><br />'.join(devices),
title=NOTIFICATION_SCAN_TITLE,
@@ -139,6 +145,10 @@ def async_setup(hass, config):
"""Handler for service calls."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
if service.service == SERVICE_SCAN:
hass.async_add_job(scan_for_apple_tvs, hass)
return
if entity_ids:
devices = [device for device in hass.data[DATA_ENTITIES]
if device.entity_id in entity_ids]
@@ -146,16 +156,16 @@ def async_setup(hass, config):
devices = hass.data[DATA_ENTITIES]
for device in devices:
if service.service != SERVICE_AUTHENTICATE:
continue
atv = device.atv
if service.service == SERVICE_AUTHENTICATE:
credentials = yield from atv.airplay.generate_credentials()
yield from atv.airplay.load_credentials(credentials)
_LOGGER.debug('Generated new credentials: %s', credentials)
yield from atv.airplay.start_authentication()
hass.async_add_job(request_configuration,
hass, config, atv, credentials)
elif service.service == SERVICE_SCAN:
hass.async_add_job(scan_for_apple_tvs, hass)
credentials = yield from atv.airplay.generate_credentials()
yield from atv.airplay.load_credentials(credentials)
_LOGGER.debug('Generated new credentials: %s', credentials)
yield from atv.airplay.start_authentication()
hass.async_add_job(request_configuration,
hass, config, atv, credentials)
@asyncio.coroutine
def atv_discovered(service, info):

View File

@@ -9,11 +9,10 @@ import logging
import voluptuous as vol
from requests.exceptions import HTTPError, ConnectTimeout
import homeassistant.loader as loader
from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
REQUIREMENTS = ['pyarlo==0.0.4']
REQUIREMENTS = ['pyarlo==0.0.6']
_LOGGER = logging.getLogger(__name__)
@@ -40,7 +39,6 @@ def setup(hass, config):
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
persistent_notification = loader.get_component('persistent_notification')
try:
from pyarlo import PyArlo
@@ -50,8 +48,8 @@ def setup(hass, config):
hass.data[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 />'
hass.components.persistent_notification.create(
'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(ex),
title=NOTIFICATION_TITLE,

View File

@@ -0,0 +1,82 @@
"""Support for Asterisk Voicemail interface."""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.const import (CONF_HOST,
CONF_PORT, CONF_PASSWORD)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (async_dispatcher_connect,
async_dispatcher_send)
REQUIREMENTS = ['asterisk_mbox==0.4.0']
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
DOMAIN = 'asterisk_mbox'
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): int,
vol.Required(CONF_PASSWORD): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Set up for the Asterisk Voicemail box."""
conf = config.get(DOMAIN)
host = conf.get(CONF_HOST)
port = conf.get(CONF_PORT)
password = conf.get(CONF_PASSWORD)
hass.data[DOMAIN] = AsteriskData(hass, host, port, password)
discovery.load_platform(hass, "mailbox", DOMAIN, {}, config)
return True
class AsteriskData(object):
"""Store Asterisk mailbox data."""
def __init__(self, hass, host, port, password):
"""Init the Asterisk data object."""
from asterisk_mbox import Client as asteriskClient
self.hass = hass
self.client = asteriskClient(host, port, password, self.handle_data)
self.messages = []
async_dispatcher_connect(
self.hass, SIGNAL_MESSAGE_REQUEST, self._request_messages)
@callback
def handle_data(self, command, msg):
"""Handle changes to the mailbox."""
from asterisk_mbox.commands import CMD_MESSAGE_LIST
if command == CMD_MESSAGE_LIST:
_LOGGER.info("AsteriskVM sent updated message list")
self.messages = sorted(msg,
key=lambda item: item['info']['origtime'],
reverse=True)
async_dispatcher_send(self.hass, SIGNAL_MESSAGE_UPDATE,
self.messages)
@callback
def _request_messages(self):
"""Handle changes to the mailbox."""
_LOGGER.info("Requesting message list")
self.client.messages()

View File

@@ -13,6 +13,7 @@ import voluptuous as vol
from homeassistant.setup import async_prepare_setup_platform
from homeassistant.core import CoreState
from homeassistant.loader import bind_hass
from homeassistant import config as conf_util
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
@@ -26,7 +27,6 @@ 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'
DEPENDENCIES = ['group']
@@ -105,6 +105,7 @@ TRIGGER_SERVICE_SCHEMA = vol.Schema({
RELOAD_SERVICE_SCHEMA = vol.Schema({})
@bind_hass
def is_on(hass, entity_id):
"""
Return true if specified automation entity_id is on.
@@ -114,35 +115,41 @@ def is_on(hass, entity_id):
return hass.states.is_state(entity_id, STATE_ON)
@bind_hass
def turn_on(hass, entity_id=None):
"""Turn on specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
@bind_hass
def turn_off(hass, entity_id=None):
"""Turn off specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
@bind_hass
def toggle(hass, entity_id=None):
"""Toggle specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
@bind_hass
def trigger(hass, entity_id=None):
"""Trigger specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TRIGGER, data)
@bind_hass
def reload(hass):
"""Reload the automation from config."""
hass.services.call(DOMAIN, SERVICE_RELOAD)
@bind_hass
def async_reload(hass):
"""Reload the automation from config.
@@ -224,10 +231,6 @@ 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

View File

@@ -12,16 +12,18 @@ import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import (
CONF_VALUE_TEMPLATE, CONF_PLATFORM, CONF_ENTITY_ID,
CONF_BELOW, CONF_ABOVE)
from homeassistant.helpers.event import async_track_state_change
CONF_BELOW, CONF_ABOVE, CONF_FOR)
from homeassistant.helpers.event import (
async_track_state_change, async_track_same_state)
from homeassistant.helpers import condition, config_validation as cv
TRIGGER_SCHEMA = vol.All(vol.Schema({
vol.Required(CONF_PLATFORM): 'numeric_state',
vol.Required(CONF_ENTITY_ID): cv.entity_ids,
CONF_BELOW: vol.Coerce(float),
CONF_ABOVE: vol.Coerce(float),
vol.Optional(CONF_BELOW): vol.Coerce(float),
vol.Optional(CONF_ABOVE): vol.Coerce(float),
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_FOR): vol.All(cv.time_period, cv.positive_timedelta),
}), cv.has_at_least_one_key(CONF_BELOW, CONF_ABOVE))
_LOGGER = logging.getLogger(__name__)
@@ -33,15 +35,18 @@ def async_trigger(hass, config, action):
entity_id = config.get(CONF_ENTITY_ID)
below = config.get(CONF_BELOW)
above = config.get(CONF_ABOVE)
time_delta = config.get(CONF_FOR)
value_template = config.get(CONF_VALUE_TEMPLATE)
async_remove_track_same = None
if value_template is not None:
value_template.hass = hass
@callback
def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action."""
def check_numeric_state(entity, from_s, to_s):
"""Return True if they should trigger."""
if to_s is None:
return
return False
variables = {
'trigger': {
@@ -55,17 +60,56 @@ def async_trigger(hass, config, action):
# If new one doesn't match, nothing to do
if not condition.async_numeric_state(
hass, to_s, below, above, value_template, variables):
return False
return True
@callback
def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action."""
nonlocal async_remove_track_same
if not check_numeric_state(entity, from_s, to_s):
return
variables = {
'trigger': {
'platform': 'numeric_state',
'entity_id': entity,
'below': below,
'above': above,
'from_state': from_s,
'to_state': to_s,
}
}
# Only match if old didn't exist or existed but didn't match
# Written as: skip if old one did exist and matched
if from_s is not None and condition.async_numeric_state(
hass, from_s, below, above, value_template, variables):
return
variables['trigger']['from_state'] = from_s
variables['trigger']['to_state'] = to_s
@callback
def call_action():
"""Call action with right context."""
hass.async_run_job(action, variables)
hass.async_run_job(action, variables)
if not time_delta:
call_action()
return
return async_track_state_change(hass, entity_id, state_automation_listener)
async_remove_track_same = async_track_same_state(
hass, True, time_delta, call_action, entity_ids=entity_id,
async_check_func=check_numeric_state)
unsub = async_track_state_change(
hass, entity_id, state_automation_listener)
@callback
def async_remove():
"""Remove state listeners async."""
unsub()
if async_remove_track_same:
async_remove_track_same() # pylint: disable=not-callable
return async_remove

View File

@@ -8,32 +8,23 @@ import asyncio
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.util.dt as dt_util
from homeassistant.const import MATCH_ALL, CONF_PLATFORM
from homeassistant.const import MATCH_ALL, CONF_PLATFORM, CONF_FOR
from homeassistant.helpers.event import (
async_track_state_change, async_track_point_in_utc_time)
from homeassistant.helpers.deprecation import get_deprecated
async_track_state_change, async_track_same_state)
import homeassistant.helpers.config_validation as cv
CONF_ENTITY_ID = 'entity_id'
CONF_FROM = 'from'
CONF_TO = 'to'
CONF_STATE = 'state'
CONF_FOR = 'for'
TRIGGER_SCHEMA = vol.All(
vol.Schema({
vol.Required(CONF_PLATFORM): 'state',
vol.Required(CONF_ENTITY_ID): cv.entity_ids,
# These are str on purpose. Want to catch YAML conversions
CONF_FROM: str,
CONF_TO: str,
CONF_STATE: str,
CONF_FOR: vol.All(cv.time_period, cv.positive_timedelta),
}),
vol.Any(cv.key_dependency(CONF_FOR, CONF_TO),
cv.key_dependency(CONF_FOR, CONF_STATE))
)
TRIGGER_SCHEMA = vol.All(vol.Schema({
vol.Required(CONF_PLATFORM): 'state',
vol.Required(CONF_ENTITY_ID): cv.entity_ids,
# These are str on purpose. Want to catch YAML conversions
vol.Optional(CONF_FROM): str,
vol.Optional(CONF_TO): str,
vol.Optional(CONF_FOR): vol.All(cv.time_period, cv.positive_timedelta),
}), cv.key_dependency(CONF_FOR, CONF_TO))
@asyncio.coroutine
@@ -41,30 +32,17 @@ 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 = get_deprecated(config, CONF_TO, CONF_STATE, MATCH_ALL)
to_state = config.get(CONF_TO, 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():
"""Clear all unsub listener."""
nonlocal async_remove_state_for_cancel, async_remove_state_for_listener
# pylint: disable=not-callable
if async_remove_state_for_listener is not None:
async_remove_state_for_listener()
async_remove_state_for_listener = None
if async_remove_state_for_cancel is not None:
async_remove_state_for_cancel()
async_remove_state_for_cancel = None
async_remove_track_same = None
@callback
def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action."""
nonlocal async_remove_state_for_cancel, async_remove_state_for_listener
nonlocal async_remove_track_same
@callback
def call_action():
"""Call action with right context."""
hass.async_run_job(action, {
@@ -82,33 +60,12 @@ def async_trigger(hass, config, action):
from_s.last_changed == to_s.last_changed):
return
if time_delta is None:
if not time_delta:
call_action()
return
@callback
def state_for_listener(now):
"""Fire on state changes after a delay and calls action."""
nonlocal async_remove_state_for_listener
async_remove_state_for_listener = None
clear_listener()
call_action()
@callback
def state_for_cancel_listener(entity, inner_from_s, inner_to_s):
"""Fire on changes and cancel for listener if changed."""
if inner_to_s.state == to_s.state:
return
clear_listener()
# cleanup previous listener
clear_listener()
async_remove_state_for_listener = async_track_point_in_utc_time(
hass, state_for_listener, dt_util.utcnow() + time_delta)
async_remove_state_for_cancel = async_track_state_change(
hass, entity, state_for_cancel_listener)
async_remove_track_same = async_track_same_state(
hass, to_s.state, time_delta, call_action, entity_ids=entity_id)
unsub = async_track_state_change(
hass, entity_id, state_automation_listener, from_state, to_state)
@@ -117,6 +74,7 @@ def async_trigger(hass, config, action):
def async_remove():
"""Remove state listeners async."""
unsub()
clear_listener()
if async_remove_track_same:
async_remove_track_same() # pylint: disable=not-callable
return async_remove

View File

@@ -10,7 +10,7 @@ import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.const import CONF_AT, CONF_PLATFORM, CONF_AFTER
from homeassistant.const import CONF_AT, CONF_PLATFORM
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_change
@@ -23,12 +23,10 @@ _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_AT, CONF_AFTER))
}), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES, CONF_SECONDS, CONF_AT))
@asyncio.coroutine
@@ -37,11 +35,6 @@ def async_trigger(hass, config, action):
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)

View File

@@ -14,17 +14,16 @@ import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_HOST, CONF_INCLUDE, CONF_NAME,
CONF_PASSWORD, CONF_TRIGGER_TIME,
CONF_PASSWORD, CONF_PORT, 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.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component
REQUIREMENTS = ['axis==8']
REQUIREMENTS = ['axis==12']
_LOGGER = logging.getLogger(__name__)
@@ -52,6 +51,7 @@ DEVICE_SCHEMA = vol.Schema({
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(CONF_PORT, default=80): cv.positive_int,
vol.Optional(ATTR_LOCATION, default=''): cv.string,
})
@@ -77,9 +77,9 @@ SERVICE_SCHEMA = vol.Schema({
})
def request_configuration(hass, name, host, serialnumber):
def request_configuration(hass, config, name, host, serialnumber):
"""Request configuration steps from the user."""
configurator = get_component('configurator')
configurator = hass.components.configurator
def configuration_callback(callback_data):
"""Called when config is submitted."""
@@ -92,15 +92,15 @@ def request_configuration(hass, name, host, serialnumber):
if CONF_NAME not in callback_data:
callback_data[CONF_NAME] = name
try:
config = DEVICE_SCHEMA(callback_data)
device_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):
if setup_device(hass, config, device_config):
config_file = _read_config(hass)
config_file[serialnumber] = dict(config)
config_file[serialnumber] = dict(device_config)
del config_file[serialnumber]['hass']
_write_config(hass, config_file)
configurator.request_done(request_id)
@@ -111,7 +111,7 @@ def request_configuration(hass, name, host, serialnumber):
title = '{} ({})'.format(name, host)
request_id = configurator.request_config(
hass, title, configuration_callback,
title, configuration_callback,
description='Functionality: ' + str(AXIS_INCLUDE),
entity_picture="/static/images/logo_axis.png",
link_name='Axis platform documentation',
@@ -133,6 +133,9 @@ def request_configuration(hass, name, host, serialnumber):
{'id': ATTR_LOCATION,
'name': "Physical location of device (optional)",
'type': 'text'},
{'id': CONF_PORT,
'name': "HTTP port (default=80)",
'type': 'number'},
{'id': CONF_TRIGGER_TIME,
'name': "Sensor update interval (optional)",
'type': 'number'},
@@ -140,7 +143,7 @@ def request_configuration(hass, name, host, serialnumber):
)
def setup(hass, base_config):
def setup(hass, config):
"""Common setup for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
"""Stop the metadatastream on shutdown."""
@@ -161,16 +164,17 @@ def setup(hass, base_config):
if serialnumber in config_file:
# Device config saved to file
try:
config = DEVICE_SCHEMA(config_file[serialnumber])
config[CONF_HOST] = host
device_config = DEVICE_SCHEMA(config_file[serialnumber])
device_config[CONF_HOST] = host
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[CONF_NAME])
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s",
device_config[CONF_NAME])
else:
# New device, create configuration request for UI
request_configuration(hass, name, host, serialnumber)
request_configuration(hass, config, name, host, serialnumber)
else:
# Device already registered, but on a different IP
device = AXIS_DEVICES[serialnumber]
@@ -182,13 +186,13 @@ def setup(hass, base_config):
# Register discovery service
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[CONF_NAME])
if DOMAIN in config:
for device in config[DOMAIN]:
device_config = config[DOMAIN][device]
if CONF_NAME not in device_config:
device_config[CONF_NAME] = device
if not setup_device(hass, config, device_config):
_LOGGER.error("Couldn\'t set up %s", device_config[CONF_NAME])
# Services to communicate with device.
descriptions = load_yaml_config_file(
@@ -216,20 +220,20 @@ def setup(hass, base_config):
return True
def setup_device(hass, config):
def setup_device(hass, config, device_config):
"""Set up device."""
from axis import AxisDevice
config['hass'] = hass
device = AxisDevice(config) # Initialize device
device_config['hass'] = hass
device = AxisDevice(device_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])
_LOGGER.error("Couldn\'t connect to %s", device_config[CONF_HOST])
return False
for component in config[CONF_INCLUDE]:
for component in device_config[CONF_INCLUDE]:
if component in EVENT_TYPES:
# Sensors are created by device calling event_initialized
# when receiving initialize messages on metadatastream
@@ -237,17 +241,27 @@ def setup_device(hass, config):
if not enable_metadatastream:
enable_metadatastream = True
else:
discovery.load_platform(hass, component, DOMAIN, config)
camera_config = {
CONF_HOST: device_config[CONF_HOST],
CONF_NAME: device_config[CONF_NAME],
CONF_PORT: device_config[CONF_PORT],
CONF_USERNAME: device_config[CONF_USERNAME],
CONF_PASSWORD: device_config[CONF_PASSWORD]
}
discovery.load_platform(hass,
component,
DOMAIN,
camera_config,
config)
if enable_metadatastream:
device.initialize_new_event = event_initialized
if not device.initiate_metadatastream():
notification = get_component('persistent_notification')
notification.create(hass,
'Dependency missing for sensors, '
'please check documentation',
title=DOMAIN,
notification_id='axis_notification')
hass.components.persistent_notification.create(
'Dependency missing for sensors, '
'please check documentation',
title=DOMAIN,
notification_id='axis_notification')
AXIS_DEVICES[device.serial_number] = device

View File

@@ -14,7 +14,6 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (STATE_ON, STATE_OFF)
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.helpers.deprecation import deprecated_substitute
DOMAIN = 'binary_sensor'
SCAN_INTERVAL = timedelta(seconds=30)
@@ -66,7 +65,6 @@ class BinarySensorDevice(Entity):
return STATE_ON if self.is_on else STATE_OFF
@property
@deprecated_substitute('sensor_class')
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return None

View File

@@ -0,0 +1,74 @@
"""
This component provides HA binary_sensor support for Abode Security System.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.abode/
"""
import logging
from homeassistant.components.abode import (AbodeDevice, AbodeAutomation,
DOMAIN as ABODE_DOMAIN)
from homeassistant.components.binary_sensor import BinarySensorDevice
DEPENDENCIES = ['abode']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for an Abode device."""
import abodepy.helpers.constants as CONST
import abodepy.helpers.timeline as TIMELINE
data = hass.data[ABODE_DOMAIN]
device_types = [CONST.TYPE_CONNECTIVITY, CONST.TYPE_MOISTURE,
CONST.TYPE_MOTION, CONST.TYPE_OCCUPANCY,
CONST.TYPE_OPENING]
devices = []
for device in data.abode.get_devices(generic_type=device_types):
if data.is_excluded(device):
continue
devices.append(AbodeBinarySensor(data, device))
for automation in data.abode.get_automations(
generic_type=CONST.TYPE_QUICK_ACTION):
if data.is_automation_excluded(automation):
continue
devices.append(AbodeQuickActionBinarySensor(
data, automation, TIMELINE.AUTOMATION_EDIT_GROUP))
data.devices.extend(devices)
add_devices(devices)
class AbodeBinarySensor(AbodeDevice, BinarySensorDevice):
"""A binary sensor implementation for Abode device."""
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._device.is_on
@property
def device_class(self):
"""Return the class of the binary sensor."""
return self._device.generic_type
class AbodeQuickActionBinarySensor(AbodeAutomation, BinarySensorDevice):
"""A binary sensor implementation for Abode quick action automations."""
def trigger(self):
"""Trigger a quick automation."""
self._automation.trigger()
@property
def is_on(self):
"""Return True if the binary sensor is on."""
return self._automation.is_active

View File

@@ -102,11 +102,11 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._state = 1
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
@callback
def _restore_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._state = 0
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()

View File

@@ -20,9 +20,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up an Online Status binary sensor."""
add_entities((OnlineStatus(config, apcupsd.DATA),))
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up an APCUPSd Online Status binary sensor."""
add_devices([OnlineStatus(config, apcupsd.DATA)], True)
class OnlineStatus(BinarySensorDevice):
@@ -33,7 +33,6 @@ class OnlineStatus(BinarySensorDevice):
self._config = config
self._data = data
self._state = None
self.update()
@property
def name(self):

View File

@@ -13,10 +13,9 @@ import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
CONF_RESOURCE, CONF_PIN, CONF_NAME, CONF_SENSOR_CLASS, CONF_DEVICE_CLASS)
CONF_RESOURCE, CONF_PIN, CONF_NAME, CONF_DEVICE_CLASS)
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
_LOGGER = logging.getLogger(__name__)
@@ -26,7 +25,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_RESOURCE): cv.url,
vol.Optional(CONF_NAME): cv.string,
vol.Required(CONF_PIN): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
@@ -35,7 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the aREST binary sensor."""
resource = config.get(CONF_RESOURCE)
pin = config.get(CONF_PIN)
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = config.get(CONF_DEVICE_CLASS)
try:
response = requests.get(resource, timeout=10).json()

View File

@@ -0,0 +1,217 @@
"""
Use Bayesian Inference to trigger a binary sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.bayesian/
"""
import asyncio
import logging
from collections import OrderedDict
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_ABOVE, CONF_BELOW, CONF_DEVICE_CLASS, CONF_ENTITY_ID, CONF_NAME,
CONF_PLATFORM, CONF_STATE, STATE_UNKNOWN)
from homeassistant.core import callback
from homeassistant.helpers import condition
from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
CONF_OBSERVATIONS = 'observations'
CONF_PRIOR = 'prior'
CONF_PROBABILITY_THRESHOLD = 'probability_threshold'
CONF_P_GIVEN_F = 'prob_given_false'
CONF_P_GIVEN_T = 'prob_given_true'
CONF_TO_STATE = 'to_state'
DEFAULT_NAME = 'BayesianBinary'
NUMERIC_STATE_SCHEMA = vol.Schema({
CONF_PLATFORM: 'numeric_state',
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Optional(CONF_ABOVE): vol.Coerce(float),
vol.Optional(CONF_BELOW): vol.Coerce(float),
vol.Required(CONF_P_GIVEN_T): vol.Coerce(float),
vol.Optional(CONF_P_GIVEN_F): vol.Coerce(float)
}, required=True)
STATE_SCHEMA = vol.Schema({
CONF_PLATFORM: CONF_STATE,
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TO_STATE): cv.string,
vol.Required(CONF_P_GIVEN_T): vol.Coerce(float),
vol.Optional(CONF_P_GIVEN_F): vol.Coerce(float)
}, required=True)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME):
cv.string,
vol.Optional(CONF_DEVICE_CLASS): cv.string,
vol.Required(CONF_OBSERVATIONS): vol.Schema(
vol.All(cv.ensure_list, [vol.Any(NUMERIC_STATE_SCHEMA,
STATE_SCHEMA)])
),
vol.Required(CONF_PRIOR): vol.Coerce(float),
vol.Optional(CONF_PROBABILITY_THRESHOLD):
vol.Coerce(float),
})
def update_probability(prior, prob_true, prob_false):
"""Update probability using Bayes' rule."""
numerator = prob_true * prior
denominator = numerator + prob_false * (1 - prior)
probability = numerator / denominator
return probability
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Threshold sensor."""
name = config.get(CONF_NAME)
observations = config.get(CONF_OBSERVATIONS)
prior = config.get(CONF_PRIOR)
probability_threshold = config.get(CONF_PROBABILITY_THRESHOLD, 0.5)
device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices([
BayesianBinarySensor(name, prior, observations, probability_threshold,
device_class)
], True)
class BayesianBinarySensor(BinarySensorDevice):
"""Representation of a Bayesian sensor."""
def __init__(self, name, prior, observations, probability_threshold,
device_class):
"""Initialize the Bayesian sensor."""
self._name = name
self._observations = observations
self._probability_threshold = probability_threshold
self._device_class = device_class
self._deviation = False
self.prior = prior
self.probability = prior
self.current_obs = OrderedDict({})
to_observe = set(obs['entity_id'] for obs in self._observations)
self.entity_obs = dict.fromkeys(to_observe, [])
for ind, obs in enumerate(self._observations):
obs["id"] = ind
self.entity_obs[obs['entity_id']].append(obs)
self.watchers = {
'numeric_state': self._process_numeric_state,
'state': self._process_state
}
@asyncio.coroutine
def async_added_to_hass(self):
"""Call when entity about to be added to hass."""
@callback
# pylint: disable=invalid-name
def async_threshold_sensor_state_listener(entity, old_state,
new_state):
"""Handle sensor state changes."""
if new_state.state == STATE_UNKNOWN:
return
entity_obs_list = self.entity_obs[entity]
for entity_obs in entity_obs_list:
platform = entity_obs['platform']
self.watchers[platform](entity_obs)
prior = self.prior
for obs in self.current_obs.values():
prior = update_probability(prior, obs['prob_true'],
obs['prob_false'])
self.probability = prior
self.hass.async_add_job(self.async_update_ha_state, True)
entities = [obs['entity_id'] for obs in self._observations]
async_track_state_change(
self.hass, entities, async_threshold_sensor_state_listener)
def _update_current_obs(self, entity_observation, should_trigger):
"""Update current observation."""
obs_id = entity_observation['id']
if should_trigger:
prob_true = entity_observation['prob_given_true']
prob_false = entity_observation.get(
'prob_given_false', 1 - prob_true)
self.current_obs[obs_id] = {
'prob_true': prob_true,
'prob_false': prob_false
}
else:
self.current_obs.pop(obs_id, None)
def _process_numeric_state(self, entity_observation):
"""Add entity to current_obs if numeric state conditions are met."""
entity = entity_observation['entity_id']
should_trigger = condition.async_numeric_state(
self.hass, entity,
entity_observation.get('below'),
entity_observation.get('above'), None, entity_observation)
self._update_current_obs(entity_observation, should_trigger)
def _process_state(self, entity_observation):
"""Add entity to current observations if state conditions are met."""
entity = entity_observation['entity_id']
should_trigger = condition.state(
self.hass, entity, entity_observation.get('to_state'))
self._update_current_obs(entity_observation, should_trigger)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return true if sensor is on."""
return self._deviation
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_class(self):
"""Return the sensor class of the sensor."""
return self._device_class
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
return {
'observations': [val for val in self.current_obs.values()],
'probability': round(self.probability, 2),
'probability_threshold': self._probability_threshold
}
@asyncio.coroutine
def async_update(self):
"""Get the latest data and update the states."""
self._deviation = bool(self.probability > self._probability_threshold)

View File

@@ -37,7 +37,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for device in bloomsky.BLOOMSKY.devices.values():
for variable in sensors:
add_devices([BloomSkySensor(bloomsky.BLOOMSKY, device, variable)])
add_devices(
[BloomSkySensor(bloomsky.BLOOMSKY, device, variable)], True)
class BloomSkySensor(BinarySensorDevice):
@@ -50,7 +51,7 @@ class BloomSkySensor(BinarySensorDevice):
self._sensor_name = sensor_name
self._name = '{} {}'.format(device['DeviceName'], sensor_name)
self._unique_id = 'bloomsky_binary_sensor {}'.format(self._name)
self.update()
self._state = None
@property
def name(self):

View File

@@ -4,19 +4,18 @@ Support for custom shell commands to retrieve values.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.command_line/
"""
from datetime import timedelta
import logging
from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA)
from homeassistant.components.sensor.command_line import CommandSensorData
from homeassistant.const import (
CONF_PAYLOAD_OFF, CONF_PAYLOAD_ON, CONF_NAME, CONF_VALUE_TEMPLATE,
CONF_SENSOR_CLASS, CONF_COMMAND, CONF_DEVICE_CLASS)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
CONF_COMMAND, CONF_DEVICE_CLASS)
_LOGGER = logging.getLogger(__name__)
@@ -31,7 +30,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
})
@@ -44,15 +42,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
command = config.get(CONF_COMMAND)
payload_off = config.get(CONF_PAYLOAD_OFF)
payload_on = config.get(CONF_PAYLOAD_ON)
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = config.get(CONF_DEVICE_CLASS)
value_template = config.get(CONF_VALUE_TEMPLATE)
if value_template is not None:
value_template.hass = hass
data = CommandSensorData(command)
data = CommandSensorData(hass, command)
add_devices([CommandBinarySensor(
hass, data, name, device_class, payload_on, payload_off,
value_template)])
value_template)], True)
class CommandBinarySensor(BinarySensorDevice):
@@ -69,7 +67,6 @@ class CommandBinarySensor(BinarySensorDevice):
self._payload_on = payload_on
self._payload_off = payload_off
self._value_template = value_template
self.update()
@property
def name(self):

View File

@@ -72,9 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
)
)
add_devices(sensors)
return True
add_devices(sensors, True)
def get_opening_type(zone):
@@ -100,7 +98,6 @@ class Concord232ZoneSensor(BinarySensorDevice):
self._zone = zone
self._number = zone['number']
self._zone_type = zone_type
self.update()
@property
def device_class(self):
@@ -130,7 +127,7 @@ class Concord232ZoneSensor(BinarySensorDevice):
if last_update > datetime.timedelta(seconds=1):
self._client.zones = self._client.list_zones()
self._client.last_zone_update = datetime.datetime.now()
_LOGGER.debug("Updated from Zone: %s", self._zone['name'])
_LOGGER.debug("Updated from zone: %s", self._zone['name'])
if hasattr(self._client, 'zones'):
self._zone = next((x for x in self._client.zones

View File

@@ -19,7 +19,7 @@ from homeassistant.components.digital_ocean import (
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Droplet'
DEFAULT_SENSOR_CLASS = 'moving'
DEFAULT_DEVICE_CLASS = 'moving'
DEPENDENCIES = ['digital_ocean']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@@ -69,7 +69,7 @@ class DigitalOceanBinarySensor(BinarySensorDevice):
@property
def device_class(self):
"""Return the class of this sensor."""
return DEFAULT_SENSOR_CLASS
return DEFAULT_DEVICE_CLASS
@property
def device_state_attributes(self):

View File

@@ -0,0 +1,60 @@
"""Support for reading binary states from a DoorBird video doorbell."""
from datetime import timedelta
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN
from homeassistant.util import Throttle
DEPENDENCIES = ['doorbird']
_LOGGER = logging.getLogger(__name__)
_MIN_UPDATE_INTERVAL = timedelta(milliseconds=250)
SENSOR_TYPES = {
"doorbell": {
"name": "Doorbell Ringing",
"icon": {
True: "bell-ring",
False: "bell",
None: "bell-outline"
}
}
}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the DoorBird binary sensor component."""
device = hass.data.get(DOORBIRD_DOMAIN)
add_devices([DoorBirdBinarySensor(device, "doorbell")], True)
class DoorBirdBinarySensor(BinarySensorDevice):
"""A binary sensor of a DoorBird device."""
def __init__(self, device, sensor_type):
"""Initialize a binary sensor on a DoorBird device."""
self._device = device
self._sensor_type = sensor_type
self._state = None
@property
def name(self):
"""Get the name of the sensor."""
return SENSOR_TYPES[self._sensor_type]["name"]
@property
def icon(self):
"""Get an icon to display."""
state_icon = SENSOR_TYPES[self._sensor_type]["icon"][self._state]
return "mdi:{}".format(state_icon)
@property
def is_on(self):
"""Get the state of the binary sensor."""
return self._state
@Throttle(_MIN_UPDATE_INTERVAL)
def update(self):
"""Pull the latest value from the device."""
self._state = self._device.doorbell_state()

View File

@@ -26,7 +26,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
dev.append(EcobeeBinarySensor(sensor['name'], index))
add_devices(dev)
add_devices(dev, True)
class EcobeeBinarySensor(BinarySensorDevice):
@@ -39,7 +39,6 @@ class EcobeeBinarySensor(BinarySensorDevice):
self.index = sensor_index
self._state = None
self._device_class = 'occupancy'
self.update()
@property
def name(self):

View File

@@ -12,9 +12,8 @@ from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
from homeassistant.components import enocean
from homeassistant.const import (
CONF_NAME, CONF_ID, CONF_SENSOR_CLASS, CONF_DEVICE_CLASS)
CONF_NAME, CONF_ID, CONF_DEVICE_CLASS)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
_LOGGER = logging.getLogger(__name__)
@@ -24,7 +23,6 @@ DEFAULT_NAME = 'EnOcean binary sensor'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
@@ -33,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""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)
device_class = config.get(CONF_DEVICE_CLASS)
add_devices([EnOceanBinarySensor(dev_id, devname, device_class)])

View File

@@ -80,4 +80,4 @@ class EnvisalinkBinarySensor(EnvisalinkDevice, BinarySensorDevice):
def _update_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()

View File

@@ -73,7 +73,7 @@ class FFmpegBinarySensor(FFmpegBase, BinarySensorDevice):
def _async_callback(self, state):
"""HA-FFmpeg callback for noise detection."""
self._state = state
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
@property
def is_on(self):

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
CONF_SSL, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START,
ATTR_LAST_TRIP_TIME, CONF_CUSTOMIZE)
REQUIREMENTS = ['pyhik==0.1.3']
REQUIREMENTS = ['pyhik==0.1.4']
_LOGGER = logging.getLogger(__name__)
CONF_IGNORED = 'ignored'
@@ -47,6 +47,7 @@ DEVICE_CLASS_MAP = {
'PIR Alarm': 'motion',
'Face Detection': 'motion',
'Scene Change Detection': 'motion',
'I/O': None,
}
CUSTOMIZE_SCHEMA = vol.Schema({

View File

@@ -35,8 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
devices = []
for conf in discovery_info[ATTR_DISCOVER_DEVICES]:
new_device = HMBinarySensor(hass, conf)
new_device.link_homematic()
new_device = HMBinarySensor(conf)
devices.append(new_device)
add_devices(devices)

View File

@@ -55,12 +55,12 @@ class InsteonPLMBinarySensorDevice(BinarySensorDevice):
@property
def address(self):
"""Return the the address of the node."""
"""Return the address of the node."""
return self._address
@property
def name(self):
"""Return the the name of the node."""
"""Return the name of the node."""
return self._name
@property

View File

@@ -64,7 +64,6 @@ class IssBinarySensor(BinarySensorDevice):
self._state = None
self._name = name
self._show_on_map = show
self.update()
@property
def name(self):

View File

@@ -1,21 +1,145 @@
"""
Contains functionality to use a KNX group address as a binary.
Support for KNX/IP binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.knx/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.knx import (KNXConfig, KNXGroupAddress)
import asyncio
import voluptuous as vol
from homeassistant.components.knx import DATA_KNX, ATTR_DISCOVER_DEVICES, \
KNXAutomation
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, \
BinarySensorDevice
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
CONF_ADDRESS = 'address'
CONF_DEVICE_CLASS = 'device_class'
CONF_SIGNIFICANT_BIT = 'significant_bit'
CONF_DEFAULT_SIGNIFICANT_BIT = 1
CONF_AUTOMATION = 'automation'
CONF_HOOK = 'hook'
CONF_DEFAULT_HOOK = 'on'
CONF_COUNTER = 'counter'
CONF_DEFAULT_COUNTER = 1
CONF_ACTION = 'action'
CONF__ACTION = 'turn_off_action'
DEFAULT_NAME = 'KNX Binary Sensor'
DEPENDENCIES = ['knx']
AUTOMATION_SCHEMA = vol.Schema({
vol.Optional(CONF_HOOK, default=CONF_DEFAULT_HOOK): cv.string,
vol.Optional(CONF_COUNTER, default=CONF_DEFAULT_COUNTER): cv.port,
vol.Required(CONF_ACTION, default=None): cv.SCRIPT_SCHEMA
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the KNX binary sensor platform."""
add_devices([KNXSwitch(hass, KNXConfig(config))])
AUTOMATIONS_SCHEMA = vol.All(
cv.ensure_list,
[AUTOMATION_SCHEMA]
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ADDRESS): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): cv.string,
vol.Optional(CONF_SIGNIFICANT_BIT, default=CONF_DEFAULT_SIGNIFICANT_BIT):
cv.positive_int,
vol.Optional(CONF_AUTOMATION, default=None): AUTOMATIONS_SCHEMA,
})
class KNXSwitch(KNXGroupAddress, BinarySensorDevice):
"""Representation of a KNX binary sensor device."""
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up binary sensor(s) for KNX platform."""
if DATA_KNX not in hass.data \
or not hass.data[DATA_KNX].initialized:
return False
pass
if discovery_info is not None:
async_add_devices_discovery(hass, discovery_info, async_add_devices)
else:
async_add_devices_config(hass, config, async_add_devices)
return True
@callback
def async_add_devices_discovery(hass, discovery_info, async_add_devices):
"""Set up binary sensors for KNX platform configured via xknx.yaml."""
entities = []
for device_name in discovery_info[ATTR_DISCOVER_DEVICES]:
device = hass.data[DATA_KNX].xknx.devices[device_name]
entities.append(KNXBinarySensor(hass, device))
async_add_devices(entities)
@callback
def async_add_devices_config(hass, config, async_add_devices):
"""Set up binary senor for KNX platform configured within plattform."""
name = config.get(CONF_NAME)
import xknx
binary_sensor = xknx.devices.BinarySensor(
hass.data[DATA_KNX].xknx,
name=name,
group_address=config.get(CONF_ADDRESS),
device_class=config.get(CONF_DEVICE_CLASS),
significant_bit=config.get(CONF_SIGNIFICANT_BIT))
hass.data[DATA_KNX].xknx.devices.add(binary_sensor)
entity = KNXBinarySensor(hass, binary_sensor)
automations = config.get(CONF_AUTOMATION)
if automations is not None:
for automation in automations:
counter = automation.get(CONF_COUNTER)
hook = automation.get(CONF_HOOK)
action = automation.get(CONF_ACTION)
entity.automations.append(KNXAutomation(
hass=hass, device=binary_sensor, hook=hook,
action=action, counter=counter))
async_add_devices([entity])
class KNXBinarySensor(BinarySensorDevice):
"""Representation of a KNX binary sensor."""
def __init__(self, hass, device):
"""Initialization of KNXBinarySensor."""
self.device = device
self.hass = hass
self.async_register_callbacks()
self.automations = []
@callback
def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed."""
@asyncio.coroutine
def after_update_callback(device):
"""Callback after device was updated."""
# pylint: disable=unused-argument
yield from self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)
@property
def name(self):
"""Return the name of the KNX device."""
return self.device.name
@property
def should_poll(self):
"""No polling needed within KNX."""
return False
@property
def device_class(self):
"""Return the class of this sensor."""
return self.device.device_class
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.device.is_on()

View File

@@ -15,24 +15,34 @@ from homeassistant.components.binary_sensor import (
BinarySensorDevice, DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_VALUE_TEMPLATE, CONF_PAYLOAD_ON, CONF_PAYLOAD_OFF,
CONF_SENSOR_CLASS, CONF_DEVICE_CLASS)
from homeassistant.components.mqtt import (CONF_STATE_TOPIC, CONF_QOS)
CONF_DEVICE_CLASS)
from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_AVAILABILITY_TOPIC, CONF_QOS, valid_subscribe_topic)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
_LOGGER = logging.getLogger(__name__)
CONF_PAYLOAD_AVAILABLE = 'payload_available'
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
DEFAULT_NAME = 'MQTT Binary sensor'
DEFAULT_PAYLOAD_OFF = 'OFF'
DEFAULT_PAYLOAD_ON = 'ON'
DEFAULT_PAYLOAD_AVAILABLE = 'online'
DEFAULT_PAYLOAD_NOT_AVAILABLE = 'offline'
DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string,
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_AVAILABILITY_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_PAYLOAD_AVAILABLE,
default=DEFAULT_PAYLOAD_AVAILABLE): cv.string,
vol.Optional(CONF_PAYLOAD_NOT_AVAILABLE,
default=DEFAULT_PAYLOAD_NOT_AVAILABLE): cv.string,
})
@@ -49,10 +59,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async_add_devices([MqttBinarySensor(
config.get(CONF_NAME),
config.get(CONF_STATE_TOPIC),
get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS),
config.get(CONF_AVAILABILITY_TOPIC),
config.get(CONF_DEVICE_CLASS),
config.get(CONF_QOS),
config.get(CONF_PAYLOAD_ON),
config.get(CONF_PAYLOAD_OFF),
config.get(CONF_PAYLOAD_AVAILABLE),
config.get(CONF_PAYLOAD_NOT_AVAILABLE),
value_template
)])
@@ -60,15 +73,20 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class MqttBinarySensor(BinarySensorDevice):
"""Representation a binary sensor that is updated by MQTT."""
def __init__(self, name, state_topic, device_class, qos, payload_on,
payload_off, value_template):
def __init__(self, name, state_topic, availability_topic, device_class,
qos, payload_on, payload_off, payload_available,
payload_not_available, value_template):
"""Initialize the MQTT binary sensor."""
self._name = name
self._state = False
self._state = None
self._state_topic = state_topic
self._availability_topic = availability_topic
self._available = True if availability_topic is None else False
self._device_class = device_class
self._payload_on = payload_on
self._payload_off = payload_off
self._payload_available = payload_available
self._payload_not_available = payload_not_available
self._qos = qos
self._template = value_template
@@ -78,8 +96,8 @@ class MqttBinarySensor(BinarySensorDevice):
This method must be run in the event loop and returns a coroutine.
"""
@callback
def message_received(topic, payload, qos):
"""Handle a new received MQTT message."""
def state_message_received(topic, payload, qos):
"""Handle a new received MQTT state message."""
if self._template is not None:
payload = self._template.async_render_with_possible_json_value(
payload)
@@ -88,10 +106,25 @@ class MqttBinarySensor(BinarySensorDevice):
elif payload == self._payload_off:
self._state = False
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()
return mqtt.async_subscribe(
self.hass, self._state_topic, message_received, self._qos)
yield from mqtt.async_subscribe(
self.hass, self._state_topic, state_message_received, self._qos)
@callback
def availability_message_received(topic, payload, qos):
"""Handle a new received MQTT availability message."""
if payload == self._payload_available:
self._available = True
elif payload == self._payload_not_available:
self._available = False
self.async_schedule_update_ha_state()
if self._availability_topic is not None:
yield from mqtt.async_subscribe(
self.hass, self._availability_topic,
availability_message_received, self._qos)
@property
def should_poll(self):
@@ -103,6 +136,11 @@ class MqttBinarySensor(BinarySensorDevice):
"""Return the name of the binary sensor."""
return self._name
@property
def available(self) -> bool:
"""Return if the binary sensor is available."""
return self._available
@property
def is_on(self):
"""Return true if the binary sensor is on."""

View File

@@ -4,62 +4,27 @@ Support for MySensors binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.mysensors/
"""
import logging
from homeassistant.components import mysensors
from homeassistant.components.binary_sensor import (DEVICE_CLASSES,
from homeassistant.components.binary_sensor import (DEVICE_CLASSES, DOMAIN,
BinarySensorDevice)
from homeassistant.const import STATE_ON
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
def setup_platform(hass, config, add_devices, discovery_info=None):
"""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:
return
gateways = hass.data.get(mysensors.MYSENSORS_GATEWAYS)
if not gateways:
return
for gateway in gateways:
# Define the S_TYPES and V_TYPES that the platform should handle as
# states. Map them in a dict of lists.
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
map_sv_types = {
pres.S_DOOR: [set_req.V_TRIPPED],
pres.S_MOTION: [set_req.V_TRIPPED],
pres.S_SMOKE: [set_req.V_TRIPPED],
}
if float(gateway.protocol_version) >= 1.5:
map_sv_types.update({
pres.S_SPRINKLER: [set_req.V_TRIPPED],
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
pres.S_SOUND: [set_req.V_TRIPPED],
pres.S_VIBRATION: [set_req.V_TRIPPED],
pres.S_MOISTURE: [set_req.V_TRIPPED],
})
devices = {}
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
map_sv_types, devices, MySensorsBinarySensor, add_devices))
"""Setup the mysensors platform for binary sensors."""
mysensors.setup_mysensors_platform(
hass, DOMAIN, discovery_info, MySensorsBinarySensor,
add_devices=add_devices)
class MySensorsBinarySensor(
mysensors.MySensorsDeviceEntity, BinarySensorDevice):
mysensors.MySensorsEntity, BinarySensorDevice):
"""Represent the value of a MySensors Binary Sensor child node."""
@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.value_type in self._values:
return self._values[self.value_type] == STATE_ON
return False
return self._values.get(self.value_type) == STATE_ON
@property
def device_class(self):

View File

@@ -92,4 +92,4 @@ class MyStromBinarySensor(BinarySensorDevice):
def async_on_update(self, value):
"""Receive an update."""
self._state = value
self.hass.async_add_job(self.async_update_ha_state())
self.async_schedule_update_ha_state()

View File

@@ -44,18 +44,19 @@ CONF_WELCOME_SENSORS = 'welcome_sensors'
CONF_PRESENCE_SENSORS = 'presence_sensors'
CONF_TAG_SENSORS = 'tag_sensors'
DEFAULT_TIMEOUT = 15
DEFAULT_OFFSET = 90
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOME): cv.string,
vol.Optional(CONF_TIMEOUT): cv.positive_int,
vol.Optional(CONF_OFFSET): cv.positive_int,
vol.Optional(CONF_CAMERAS, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(
CONF_WELCOME_SENSORS, default=WELCOME_SENSOR_TYPES.keys()):
vol.All(cv.ensure_list, [vol.In(WELCOME_SENSOR_TYPES)]),
vol.Optional(
CONF_PRESENCE_SENSORS, default=PRESENCE_SENSOR_TYPES.keys()):
vol.Optional(CONF_HOME): cv.string,
vol.Optional(CONF_OFFSET, default=DEFAULT_OFFSET): cv.positive_int,
vol.Optional(CONF_PRESENCE_SENSORS, default=PRESENCE_SENSOR_TYPES):
vol.All(cv.ensure_list, [vol.In(PRESENCE_SENSOR_TYPES)]),
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_WELCOME_SENSORS, default=WELCOME_SENSOR_TYPES):
vol.All(cv.ensure_list, [vol.In(WELCOME_SENSOR_TYPES)]),
})
@@ -63,16 +64,16 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the access to Netatmo binary sensor."""
netatmo = get_component('netatmo')
home = config.get(CONF_HOME, None)
timeout = config.get(CONF_TIMEOUT, 15)
offset = config.get(CONF_OFFSET, 90)
home = config.get(CONF_HOME)
timeout = config.get(CONF_TIMEOUT)
offset = config.get(CONF_OFFSET)
module_name = None
import lnetatmo
try:
data = CameraData(netatmo.NETATMO_AUTH, home)
if data.get_camera_names() == []:
if not data.get_camera_names():
return None
except lnetatmo.NoDevice:
return None
@@ -93,7 +94,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for variable in welcome_sensors:
add_devices([NetatmoBinarySensor(
data, camera_name, module_name, home, timeout,
offset, camera_type, variable)])
offset, camera_type, variable)], True)
if camera_type == 'NOC':
if CONF_CAMERAS in config:
if config[CONF_CAMERAS] != [] and \
@@ -102,14 +103,14 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for variable in presence_sensors:
add_devices([NetatmoBinarySensor(
data, camera_name, module_name, home, timeout, offset,
camera_type, variable)])
camera_type, variable)], True)
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)])
camera_type, variable)], True)
class NetatmoBinarySensor(BinarySensorDevice):
@@ -137,7 +138,7 @@ class NetatmoBinarySensor(BinarySensorDevice):
self._unique_id = "Netatmo_binary_sensor {0} - {1}".format(
self._name, camera_id)
self._cameratype = camera_type
self.update()
self._state = None
@property
def name(self):

View File

@@ -48,7 +48,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
name, SENSOR_TYPES[octo_type][3], SENSOR_TYPES[octo_type][0],
SENSOR_TYPES[octo_type][1], 'flags')
devices.append(new_sensor)
add_devices(devices)
add_devices(devices, True)
class OctoPrintBinarySensor(BinarySensorDevice):
@@ -69,8 +69,6 @@ class OctoPrintBinarySensor(BinarySensorDevice):
self.api_endpoint = endpoint
self.api_group = group
self.api_tool = tool
# Set initial state
self.update()
_LOGGER.debug("Created OctoPrint binary sensor %r", self)
@property

View File

@@ -28,13 +28,16 @@ CONF_PING_COUNT = 'count'
DEFAULT_NAME = 'Ping Binary sensor'
DEFAULT_PING_COUNT = 5
DEFAULT_SENSOR_CLASS = 'connectivity'
DEFAULT_DEVICE_CLASS = 'connectivity'
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+)')
PING_MATCHER_BUSYBOX = re.compile(
r'(?P<min>\d+.\d+)\/(?P<avg>\d+.\d+)\/(?P<max>\d+.\d+)')
WIN32_PING_MATCHER = re.compile(
r'(?P<min>\d+)ms.+(?P<max>\d+)ms.+(?P<avg>\d+)ms')
@@ -70,7 +73,7 @@ class PingBinarySensor(BinarySensorDevice):
@property
def device_class(self):
"""Return the class of this sensor."""
return DEFAULT_SENSOR_CLASS
return DEFAULT_DEVICE_CLASS
@property
def is_on(self):
@@ -126,7 +129,14 @@ class PingData(object):
'avg': rtt_avg,
'max': rtt_max,
'mdev': ''}
if 'max/' not in str(out):
match = PING_MATCHER_BUSYBOX.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': ''}
match = PING_MATCHER.search(str(out).split('\n')[-1])
rtt_min, rtt_avg, rtt_max, rtt_mdev = match.groups()
return {

View File

@@ -0,0 +1,70 @@
"""
Support for Melnor RainCloud sprinkler water timer.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.raincloud/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.raincloud import (
BINARY_SENSORS, DATA_RAINCLOUD, ICON_MAP, RainCloudEntity)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import CONF_MONITORED_CONDITIONS
DEPENDENCIES = ['raincloud']
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MONITORED_CONDITIONS, default=list(BINARY_SENSORS)):
vol.All(cv.ensure_list, [vol.In(BINARY_SENSORS)]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for a raincloud device."""
raincloud = hass.data[DATA_RAINCLOUD].data
sensors = []
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
if sensor_type == 'status':
sensors.append(
RainCloudBinarySensor(raincloud.controller, sensor_type))
sensors.append(
RainCloudBinarySensor(raincloud.controller.faucet,
sensor_type))
else:
# create an sensor for each zone managed by faucet
for zone in raincloud.controller.faucet.zones:
sensors.append(RainCloudBinarySensor(zone, sensor_type))
add_devices(sensors, True)
return True
class RainCloudBinarySensor(RainCloudEntity, BinarySensorDevice):
"""A sensor implementation for raincloud device."""
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._state
def update(self):
"""Get the latest data and updates the state."""
_LOGGER.debug("Updating RainCloud sensor: %s", self._name)
self._state = getattr(self.data, self._sensor_type)
@property
def icon(self):
"""Return the icon of this device."""
if self._sensor_type == 'is_watering':
return 'mdi:water' if self.is_on else 'mdi:water-off'
elif self._sensor_type == 'status':
return 'mdi:pipe' if self.is_on else 'mdi:pipe-disconnected'
return ICON_MAP.get(self._sensor_type)

View File

@@ -14,11 +14,10 @@ from homeassistant.components.binary_sensor import (
from homeassistant.components.sensor.rest import RestData
from homeassistant.const import (
CONF_PAYLOAD, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_METHOD, CONF_RESOURCE,
CONF_SENSOR_CLASS, CONF_VERIFY_SSL, CONF_USERNAME, CONF_PASSWORD,
CONF_VERIFY_SSL, CONF_USERNAME, CONF_PASSWORD,
CONF_HEADERS, CONF_AUTHENTICATION, HTTP_BASIC_AUTHENTICATION,
HTTP_DIGEST_AUTHENTICATION, CONF_DEVICE_CLASS)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
_LOGGER = logging.getLogger(__name__)
@@ -35,7 +34,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PAYLOAD): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
@@ -53,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
headers = config.get(CONF_HEADERS)
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = config.get(CONF_DEVICE_CLASS)
value_template = config.get(CONF_VALUE_TEMPLATE)
if value_template is not None:
value_template.hass = hass
@@ -74,7 +72,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return False
add_devices([RestBinarySensor(
hass, rest, name, device_class, value_template)])
hass, rest, name, device_class, value_template)], True)
class RestBinarySensor(BinarySensorDevice):
@@ -89,7 +87,6 @@ class RestBinarySensor(BinarySensorDevice):
self._state = False
self._previous_data = None
self._value_template = value_template
self.update()
@property
def name(self):
@@ -107,6 +104,8 @@ class RestBinarySensor(BinarySensorDevice):
if self.rest.data is None:
return False
response = self.rest.data
if self._value_template is not None:
response = self._value_template.\
async_render_with_possible_json_value(self.rest.data, False)

View File

@@ -62,6 +62,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
entity[CONF_COMMAND_ON],
entity[CONF_COMMAND_OFF])
device.hass = hass
device.is_lighting4 = (packet_id[2:4] == '13')
sensors.append(device)
rfxtrx.RFX_DEVICES[device_id] = device
@@ -94,6 +95,8 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
pkt_id = "".join("{0:02x}".format(x) for x in event.data)
sensor = RfxtrxBinarySensor(event, pkt_id)
sensor.hass = hass
sensor.is_lighting4 = (pkt_id[2:4] == '13')
rfxtrx.RFX_DEVICES[device_id] = sensor
add_devices_callback([sensor])
_LOGGER.info("Added binary sensor %s "
@@ -111,12 +114,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype)
if sensor.is_pt2262:
cmd = rfxtrx.get_pt2262_cmd(device_id, sensor.data_bits)
_LOGGER.info("applying cmd %s to device_id: %s)",
cmd, sensor.masked_id)
sensor.apply_cmd(int(cmd, 16))
if sensor.is_lighting4:
if sensor.data_bits is not None:
cmd = rfxtrx.get_pt2262_cmd(device_id, sensor.data_bits)
sensor.apply_cmd(int(cmd, 16))
else:
sensor.update_state(True)
else:
rfxtrx.apply_received_command(event)
@@ -151,6 +154,7 @@ class RfxtrxBinarySensor(BinarySensorDevice):
self._device_class = device_class
self._off_delay = off_delay
self._state = False
self.is_lighting4 = False
self.delay_listener = None
self._data_bits = data_bits
self._cmd_on = cmd_on
@@ -170,11 +174,6 @@ class RfxtrxBinarySensor(BinarySensorDevice):
"""Return the device name."""
return self._name
@property
def is_pt2262(self):
"""Return true if the device is PT2262-based."""
return self._data_bits is not None
@property
def masked_id(self):
"""Return the masked device id (isolated address bits)."""

View File

@@ -103,7 +103,8 @@ class RingBinarySensor(BinarySensorDevice):
self._data.check_alerts()
if self._data.alert:
self._state = (self._sensor_type ==
self._data.alert.get('kind'))
if self._sensor_type == self._data.alert.get('kind') and \
self._data.account_id == self._data.alert.get('doorbot_id'):
self._state = True
else:
self._state = False

View File

@@ -0,0 +1,90 @@
"""
Support for Satel Integra zone states- represented as binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.satel_integra/
"""
import asyncio
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.satel_integra import (CONF_ZONES,
CONF_ZONE_NAME,
CONF_ZONE_TYPE,
SIGNAL_ZONES_UPDATED)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
DEPENDENCIES = ['satel_integra']
_LOGGER = logging.getLogger(__name__)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Satel Integra binary sensor devices."""
if not discovery_info:
return
configured_zones = discovery_info[CONF_ZONES]
devices = []
for zone_num, device_config_data in configured_zones.items():
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
device = SatelIntegraBinarySensor(zone_num, zone_name, zone_type)
devices.append(device)
async_add_devices(devices)
class SatelIntegraBinarySensor(BinarySensorDevice):
"""Representation of an Satel Integra binary sensor."""
def __init__(self, zone_number, zone_name, zone_type):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
self._name = zone_name
self._zone_type = zone_type
self._state = 0
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ZONES_UPDATED, self._zones_updated)
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def icon(self):
"""Icon for device by its type."""
if self._zone_type == 'smoke':
return "mdi:fire"
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state == 1
@property
def device_class(self):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return self._zone_type
@callback
def _zones_updated(self, zones):
"""Update the zone's state, if needed."""
if self._zone_number in zones \
and self._state != zones[self._zone_number]:
self._state = zones[self._zone_number]
self.async_schedule_update_ha_state()

View File

@@ -41,14 +41,14 @@ def _create_sensor(hass, zone):
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_entities,
def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Initialize the platform."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_DEVICES] is None):
return
async_add_entities(
async_add_devices(
_create_sensor(hass, zone)
for zone in discovery_info[ATTR_DISCOVER_DEVICES]
if _get_device_class(zone['type']))

View File

@@ -15,23 +15,28 @@ from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_VALUE_TEMPLATE,
CONF_SENSOR_CLASS, CONF_SENSORS, CONF_DEVICE_CLASS,
EVENT_HOMEASSISTANT_START, STATE_ON)
CONF_SENSORS, CONF_DEVICE_CLASS, EVENT_HOMEASSISTANT_START, STATE_ON)
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.deprecation import get_deprecated
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.event import (
async_track_state_change, async_track_same_state)
from homeassistant.helpers.restore_state import async_get_last_state
_LOGGER = logging.getLogger(__name__)
CONF_DELAY_ON = 'delay_on'
CONF_DELAY_OFF = 'delay_off'
SENSOR_SCHEMA = vol.Schema({
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DELAY_ON):
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_DELAY_OFF):
vol.All(cv.time_period, cv.positive_timedelta),
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@@ -49,8 +54,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
entity_ids = (device_config.get(ATTR_ENTITY_ID) or
value_template.extract_entities())
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
device_class = get_deprecated(
device_config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = device_config.get(CONF_DEVICE_CLASS)
delay_on = device_config.get(CONF_DELAY_ON)
delay_off = device_config.get(CONF_DELAY_OFF)
if value_template is not None:
value_template.hass = hass
@@ -58,13 +64,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
sensors.append(
BinarySensorTemplate(
hass, device, friendly_name, device_class, value_template,
entity_ids)
entity_ids, delay_on, delay_off)
)
if not sensors:
_LOGGER.error("No sensors added")
return False
async_add_devices(sensors, True)
async_add_devices(sensors)
return True
@@ -72,7 +78,7 @@ class BinarySensorTemplate(BinarySensorDevice):
"""A virtual binary sensor that triggers from another sensor."""
def __init__(self, hass, device, friendly_name, device_class,
value_template, entity_ids):
value_template, entity_ids, delay_on, delay_off):
"""Initialize the Template binary sensor."""
self.hass = hass
self.entity_id = async_generate_entity_id(
@@ -82,6 +88,8 @@ class BinarySensorTemplate(BinarySensorDevice):
self._template = value_template
self._state = None
self._entities = entity_ids
self._delay_on = delay_on
self._delay_off = delay_off
@asyncio.coroutine
def async_added_to_hass(self):
@@ -93,7 +101,7 @@ class BinarySensorTemplate(BinarySensorDevice):
@callback
def template_bsensor_state_listener(entity, old_state, new_state):
"""Handle the target device state changes."""
self.hass.async_add_job(self.async_update_ha_state(True))
self.async_check_state()
@callback
def template_bsensor_startup(event):
@@ -101,7 +109,7 @@ class BinarySensorTemplate(BinarySensorDevice):
async_track_state_change(
self.hass, self._entities, template_bsensor_state_listener)
self.hass.async_add_job(self.async_update_ha_state(True))
self.hass.async_add_job(self.async_check_state)
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, template_bsensor_startup)
@@ -126,11 +134,11 @@ class BinarySensorTemplate(BinarySensorDevice):
"""No polling needed."""
return False
@asyncio.coroutine
def async_update(self):
"""Update the state from the template."""
@callback
def _async_render(self, *args):
"""Get the state of template."""
try:
self._state = self._template.async_render().lower() == 'true'
return self._template.async_render().lower() == 'true'
except TemplateError as ex:
if ex.args and ex.args[0].startswith(
"UndefinedError: 'None' has no attribute"):
@@ -139,4 +147,29 @@ class BinarySensorTemplate(BinarySensorDevice):
"the state is unknown", self._name)
return
_LOGGER.error("Could not render template %s: %s", self._name, ex)
self._state = False
@callback
def async_check_state(self):
"""Update the state from the template."""
state = self._async_render()
# return if the state don't change or is invalid
if state is None or state == self.state:
return
@callback
def set_state():
"""Set state of template binary sensor."""
self._state = state
self.async_schedule_update_ha_state()
# state without delay
if (state and not self._delay_on) or \
(not state and not self._delay_off):
set_state()
return
period = self._delay_on if state else self._delay_off
async_track_same_state(
self.hass, state, period, set_state, entity_ids=self._entities,
async_check_func=self._async_render)

View File

@@ -0,0 +1,57 @@
"""
Support for Tesla binary sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tesla/
"""
import logging
from homeassistant.components.binary_sensor import (
BinarySensorDevice, ENTITY_ID_FORMAT)
from homeassistant.components.tesla import DOMAIN as TESLA_DOMAIN, TeslaDevice
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['tesla']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Tesla binary sensor."""
devices = [
TeslaBinarySensor(
device, hass.data[TESLA_DOMAIN]['controller'], 'connectivity')
for device in hass.data[TESLA_DOMAIN]['devices']['binary_sensor']]
add_devices(devices, True)
class TeslaBinarySensor(TeslaDevice, BinarySensorDevice):
"""Implement an Tesla binary sensor for parking and charger."""
def __init__(self, tesla_device, controller, sensor_type):
"""Initialisation of binary sensor."""
super().__init__(tesla_device, controller)
self._name = self.tesla_device.name
self._state = False
self.entity_id = ENTITY_ID_FORMAT.format(self.tesla_id)
self._sensor_type = sensor_type
@property
def device_class(self):
"""Return the class of this binary sensor."""
return self._sensor_type
@property
def name(self):
"""Return the name of the binary sensor."""
return self._name
@property
def is_on(self):
"""Return the state of the binary sensor."""
return self._state
def update(self):
"""Update the state of the device."""
_LOGGER.debug("Updating sensor: %s", self._name)
self.tesla_device.update()
self._state = self.tesla_device.get_value()

View File

@@ -13,23 +13,25 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_ENTITY_ID, CONF_TYPE, STATE_UNKNOWN, CONF_SENSOR_CLASS,
CONF_NAME, CONF_ENTITY_ID, CONF_TYPE, STATE_UNKNOWN,
ATTR_ENTITY_ID, CONF_DEVICE_CLASS)
from homeassistant.core import callback
from homeassistant.helpers.deprecation import get_deprecated
from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
ATTR_HYSTERESIS = 'hysteresis'
ATTR_SENSOR_VALUE = 'sensor_value'
ATTR_THRESHOLD = 'threshold'
ATTR_TYPE = 'type'
CONF_HYSTERESIS = 'hysteresis'
CONF_LOWER = 'lower'
CONF_THRESHOLD = 'threshold'
CONF_UPPER = 'upper'
DEFAULT_NAME = 'Threshold'
DEFAULT_HYSTERESIS = 0.0
SENSOR_TYPES = [CONF_LOWER, CONF_UPPER]
@@ -37,8 +39,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_THRESHOLD): vol.Coerce(float),
vol.Required(CONF_TYPE): vol.In(SENSOR_TYPES),
vol.Optional(
CONF_HYSTERESIS, default=DEFAULT_HYSTERESIS): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
@@ -49,28 +52,32 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
entity_id = config.get(CONF_ENTITY_ID)
name = config.get(CONF_NAME)
threshold = config.get(CONF_THRESHOLD)
hysteresis = config.get(CONF_HYSTERESIS)
limit_type = config.get(CONF_TYPE)
device_class = get_deprecated(config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices([ThresholdSensor(
hass, entity_id, name, threshold,
hysteresis, limit_type, device_class)
], True)
async_add_devices(
[ThresholdSensor(hass, entity_id, name, threshold, limit_type,
device_class)], True)
return True
class ThresholdSensor(BinarySensorDevice):
"""Representation of a Threshold sensor."""
def __init__(self, hass, entity_id, name, threshold, limit_type,
device_class):
def __init__(self, hass, entity_id, name, threshold,
hysteresis, limit_type, device_class):
"""Initialize the Threshold sensor."""
self._hass = hass
self._entity_id = entity_id
self.is_upper = limit_type == 'upper'
self._name = name
self._threshold = threshold
self._hysteresis = hysteresis
self._device_class = device_class
self._deviation = False
self._state = False
self.sensor_value = 0
@callback
@@ -99,7 +106,7 @@ class ThresholdSensor(BinarySensorDevice):
@property
def is_on(self):
"""Return true if sensor is on."""
return self._deviation
return self._state
@property
def should_poll(self):
@@ -118,13 +125,16 @@ class ThresholdSensor(BinarySensorDevice):
ATTR_ENTITY_ID: self._entity_id,
ATTR_SENSOR_VALUE: self.sensor_value,
ATTR_THRESHOLD: self._threshold,
ATTR_HYSTERESIS: self._hysteresis,
ATTR_TYPE: CONF_UPPER if self.is_upper else CONF_LOWER,
}
@asyncio.coroutine
def async_update(self):
"""Get the latest data and updates the states."""
if self.is_upper:
self._deviation = bool(self.sensor_value > self._threshold)
else:
self._deviation = bool(self.sensor_value < self._threshold)
if self._hysteresis == 0 and self.sensor_value == self._threshold:
self._state = False
elif self.sensor_value > (self._threshold + self._hysteresis):
self._state = self.is_upper
elif self.sensor_value < (self._threshold - self._hysteresis):
self._state = not self.is_upper

View File

@@ -16,9 +16,7 @@ from homeassistant.components.binary_sensor import (
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,)
from homeassistant.helpers.deprecation import get_deprecated
ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_DEVICE_CLASS, STATE_UNKNOWN)
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.event import track_state_change
@@ -32,7 +30,6 @@ SENSOR_SCHEMA = vol.Schema({
vol.Optional(CONF_ATTRIBUTE): cv.string,
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
vol.Optional(CONF_INVERT, default=False): cv.boolean,
vol.Optional(CONF_SENSOR_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
@@ -50,8 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
entity_id = device_config[ATTR_ENTITY_ID]
attribute = device_config.get(CONF_ATTRIBUTE)
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
device_class = get_deprecated(
device_config, CONF_DEVICE_CLASS, CONF_SENSOR_CLASS)
device_class = device_config.get(CONF_DEVICE_CLASS)
invert = device_config[CONF_INVERT]
sensors.append(

View File

@@ -0,0 +1,96 @@
"""
Support for Velbus Binary Sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.velbus/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.const import CONF_NAME, CONF_DEVICES
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA
from homeassistant.components.velbus import DOMAIN
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['velbus']
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_DEVICES): vol.All(cv.ensure_list, [
{
vol.Required('module'): cv.positive_int,
vol.Required('channel'): cv.positive_int,
vol.Required(CONF_NAME): cv.string,
vol.Optional('is_pushbutton'): cv.boolean
}
])
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Velbus binary sensors."""
velbus = hass.data[DOMAIN]
add_devices(VelbusBinarySensor(sensor, velbus)
for sensor in config[CONF_DEVICES])
class VelbusBinarySensor(BinarySensorDevice):
"""Representation of a Velbus Binary Sensor."""
def __init__(self, binary_sensor, velbus):
"""Initialize a Velbus light."""
self._velbus = velbus
self._name = binary_sensor[CONF_NAME]
self._module = binary_sensor['module']
self._channel = binary_sensor['channel']
self._is_pushbutton = 'is_pushbutton' in binary_sensor \
and binary_sensor['is_pushbutton']
self._state = False
@asyncio.coroutine
def async_added_to_hass(self):
"""Add listener for Velbus messages on bus."""
yield from self.hass.async_add_job(
self._velbus.subscribe, self._on_message)
def _on_message(self, message):
import velbus
if isinstance(message, velbus.PushButtonStatusMessage):
if message.address == self._module and \
self._channel in message.get_channels():
if self._is_pushbutton:
if self._channel in message.closed:
self._toggle()
else:
pass
else:
self._toggle()
def _toggle(self):
if self._state is True:
self._state = False
else:
self._state = True
self.schedule_update_ha_state()
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the display name of this sensor."""
return self._name
@property
def is_on(self):
"""Return true if the sensor is on."""
return self._state

View File

@@ -34,5 +34,5 @@ class VolvoSensor(VolvoEntity, BinarySensorDevice):
@property
def device_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
"""Return the class of this sensor, from DEVICE_CLASSES."""
return 'safety'

View File

@@ -136,8 +136,9 @@ class WinkHub(WinkBinarySensorDevice):
def device_state_attributes(self):
"""Return the state attributes."""
return {
'update needed': self.wink.update_needed(),
'firmware version': self.wink.firmware_version()
'update_needed': self.wink.update_needed(),
'firmware_version': self.wink.firmware_version(),
'pairing_mode': self.wink.pairing_mode()
}

View File

@@ -6,13 +6,12 @@ https://home-assistant.io/components/binary_sensor.workday/
"""
import asyncio
import logging
import datetime
from datetime import datetime, timedelta
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_NAME, WEEKDAYS
import homeassistant.util.dt as dt_util
from homeassistant.components.binary_sensor import BinarySensorDevice
import homeassistant.helpers.config_validation as cv
@@ -39,11 +38,14 @@ CONF_EXCLUDES = 'excludes'
DEFAULT_EXCLUDES = ['sat', 'sun', 'holiday']
DEFAULT_NAME = 'Workday Sensor'
ALLOWED_DAYS = WEEKDAYS + ['holiday']
CONF_OFFSET = 'days_offset'
DEFAULT_OFFSET = 0
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COUNTRY): vol.In(ALL_COUNTRIES),
vol.Optional(CONF_PROVINCE, default=None): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_OFFSET, default=DEFAULT_OFFSET): vol.Coerce(int),
vol.Optional(CONF_WORKDAYS, default=DEFAULT_WORKDAYS):
vol.All(cv.ensure_list, [vol.In(ALLOWED_DAYS)]),
vol.Optional(CONF_EXCLUDES, default=DEFAULT_EXCLUDES):
@@ -60,8 +62,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
province = config.get(CONF_PROVINCE)
workdays = config.get(CONF_WORKDAYS)
excludes = config.get(CONF_EXCLUDES)
days_offset = config.get(CONF_OFFSET)
year = datetime.datetime.now().year
year = (datetime.now() + timedelta(days=days_offset)).year
obj_holidays = getattr(holidays, country)(years=year)
if province:
@@ -85,7 +88,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.debug("%s %s", date, name)
add_devices([IsWorkdaySensor(
obj_holidays, workdays, excludes, sensor_name)], True)
obj_holidays, workdays, excludes, days_offset, sensor_name)], True)
def day_to_string(day):
@@ -99,12 +102,13 @@ def day_to_string(day):
class IsWorkdaySensor(BinarySensorDevice):
"""Implementation of a Workday sensor."""
def __init__(self, obj_holidays, workdays, excludes, name):
def __init__(self, obj_holidays, workdays, excludes, days_offset, name):
"""Initialize the Workday sensor."""
self._name = name
self._obj_holidays = obj_holidays
self._workdays = workdays
self._excludes = excludes
self._days_offset = days_offset
self._state = None
@property
@@ -135,6 +139,16 @@ class IsWorkdaySensor(BinarySensorDevice):
return False
@property
def state_attributes(self):
"""Return the attributes of the entity."""
# return self._attributes
return {
CONF_WORKDAYS: self._workdays,
CONF_EXCLUDES: self._excludes,
CONF_OFFSET: self._days_offset
}
@asyncio.coroutine
def async_update(self):
"""Get date and look whether it is a holiday."""
@@ -142,11 +156,12 @@ class IsWorkdaySensor(BinarySensorDevice):
self._state = False
# Get iso day of the week (1 = Monday, 7 = Sunday)
day = datetime.datetime.today().isoweekday() - 1
date = datetime.today() + timedelta(days=self._days_offset)
day = date.isoweekday() - 1
day_of_week = day_to_string(day)
if self.is_include(day_of_week, dt_util.now()):
if self.is_include(day_of_week, date):
self._state = True
if self.is_exclude(day_of_week, dt_util.now()):
if self.is_exclude(day_of_week, date):
self._state = False

View File

@@ -0,0 +1,348 @@
"""Support for Xiaomi aqara binary sensors."""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY,
XiaomiDevice)
_LOGGER = logging.getLogger(__name__)
NO_CLOSE = 'no_close'
ATTR_OPEN_SINCE = 'Open since'
MOTION = 'motion'
NO_MOTION = 'no_motion'
ATTR_NO_MOTION_SINCE = 'No motion since'
DENSITY = 'density'
ATTR_DENSITY = 'Density'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Perform the setup for Xiaomi devices."""
devices = []
for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items():
for device in gateway.devices['binary_sensor']:
model = device['model']
if model == 'motion':
devices.append(XiaomiMotionSensor(device, hass, gateway))
elif model == 'sensor_motion.aq2':
devices.append(XiaomiMotionSensor(device, hass, gateway))
elif model == 'magnet':
devices.append(XiaomiDoorSensor(device, gateway))
elif model == 'sensor_magnet.aq2':
devices.append(XiaomiDoorSensor(device, gateway))
elif model == 'sensor_wleak.aq1':
devices.append(XiaomiWaterLeakSensor(device, gateway))
elif model == 'smoke':
devices.append(XiaomiSmokeSensor(device, gateway))
elif model == 'natgas':
devices.append(XiaomiNatgasSensor(device, gateway))
elif model == 'switch':
devices.append(XiaomiButton(device, 'Switch', 'status',
hass, gateway))
elif model == 'sensor_switch.aq2':
devices.append(XiaomiButton(device, 'Switch', 'status',
hass, gateway))
elif model == '86sw1':
devices.append(XiaomiButton(device, 'Wall Switch', 'channel_0',
hass, gateway))
elif model == '86sw2':
devices.append(XiaomiButton(device, 'Wall Switch (Left)',
'channel_0', hass, gateway))
devices.append(XiaomiButton(device, 'Wall Switch (Right)',
'channel_1', hass, gateway))
devices.append(XiaomiButton(device, 'Wall Switch (Both)',
'dual_channel', hass, gateway))
elif model == 'cube':
devices.append(XiaomiCube(device, hass, gateway))
add_devices(devices)
class XiaomiBinarySensor(XiaomiDevice, BinarySensorDevice):
"""Representation of a base XiaomiBinarySensor."""
def __init__(self, device, name, xiaomi_hub, data_key, device_class):
"""Initialize the XiaomiSmokeSensor."""
self._data_key = data_key
self._device_class = device_class
self._should_poll = False
self._density = 0
XiaomiDevice.__init__(self, device, name, xiaomi_hub)
@property
def should_poll(self):
"""Return True if entity has to be polled for state."""
return self._should_poll
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state
@property
def device_class(self):
"""Return the class of binary sensor."""
return self._device_class
def update(self):
"""Update the sensor state."""
_LOGGER.debug('Updating xiaomi sensor by polling')
self._get_from_hub(self._sid)
class XiaomiNatgasSensor(XiaomiBinarySensor):
"""Representation of a XiaomiNatgasSensor."""
def __init__(self, device, xiaomi_hub):
"""Initialize the XiaomiSmokeSensor."""
self._density = None
XiaomiBinarySensor.__init__(self, device, 'Natgas Sensor', xiaomi_hub,
'alarm', 'gas')
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {ATTR_DENSITY: self._density}
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
"""Parse data sent by gateway."""
if DENSITY in data:
self._density = int(data.get(DENSITY))
value = data.get(self._data_key)
if value is None:
return False
if value == '1':
if self._state:
return False
self._state = True
return True
elif value == '0':
if self._state:
self._state = False
return True
return False
class XiaomiMotionSensor(XiaomiBinarySensor):
"""Representation of a XiaomiMotionSensor."""
def __init__(self, device, hass, xiaomi_hub):
"""Initialize the XiaomiMotionSensor."""
self._hass = hass
self._no_motion_since = 0
XiaomiBinarySensor.__init__(self, device, 'Motion Sensor', xiaomi_hub,
'status', 'motion')
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {ATTR_NO_MOTION_SINCE: self._no_motion_since}
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
"""Parse data sent by gateway."""
self._should_poll = False
if NO_MOTION in data: # handle push from the hub
self._no_motion_since = data[NO_MOTION]
self._state = False
return True
value = data.get(self._data_key)
if value is None:
return False
if value == MOTION:
self._should_poll = True
if self.entity_id is not None:
self._hass.bus.fire('motion', {
'entity_id': self.entity_id
})
self._no_motion_since = 0
if self._state:
return False
self._state = True
return True
elif value == NO_MOTION:
if not self._state:
return False
self._state = False
return True
class XiaomiDoorSensor(XiaomiBinarySensor):
"""Representation of a XiaomiDoorSensor."""
def __init__(self, device, xiaomi_hub):
"""Initialize the XiaomiDoorSensor."""
self._open_since = 0
XiaomiBinarySensor.__init__(self, device, 'Door Window Sensor',
xiaomi_hub, 'status', 'opening')
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {ATTR_OPEN_SINCE: self._open_since}
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
"""Parse data sent by gateway."""
self._should_poll = False
if NO_CLOSE in data: # handle push from the hub
self._open_since = data[NO_CLOSE]
return True
value = data.get(self._data_key)
if value is None:
return False
if value == 'open':
self._should_poll = True
if self._state:
return False
self._state = True
return True
elif value == 'close':
self._open_since = 0
if self._state:
self._state = False
return True
return False
class XiaomiWaterLeakSensor(XiaomiBinarySensor):
"""Representation of a XiaomiWaterLeakSensor."""
def __init__(self, device, xiaomi_hub):
"""Initialize the XiaomiWaterLeakSensor."""
XiaomiBinarySensor.__init__(self, device, 'Water Leak Sensor',
xiaomi_hub, 'status', 'moisture')
def parse_data(self, data):
"""Parse data sent by gateway."""
self._should_poll = False
value = data.get(self._data_key)
if value is None:
return False
if value == 'leak':
self._should_poll = True
if self._state:
return False
self._state = True
return True
elif value == 'no_leak':
if self._state:
self._state = False
return True
return False
class XiaomiSmokeSensor(XiaomiBinarySensor):
"""Representation of a XiaomiSmokeSensor."""
def __init__(self, device, xiaomi_hub):
"""Initialize the XiaomiSmokeSensor."""
self._density = 0
XiaomiBinarySensor.__init__(self, device, 'Smoke Sensor', xiaomi_hub,
'alarm', 'smoke')
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {ATTR_DENSITY: self._density}
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
"""Parse data sent by gateway."""
if DENSITY in data:
self._density = int(data.get(DENSITY))
value = data.get(self._data_key)
if value is None:
return False
if value == '1':
if self._state:
return False
self._state = True
return True
elif value == '0':
if self._state:
self._state = False
return True
return False
class XiaomiButton(XiaomiBinarySensor):
"""Representation of a Xiaomi Button."""
def __init__(self, device, name, data_key, hass, xiaomi_hub):
"""Initialize the XiaomiButton."""
self._hass = hass
XiaomiBinarySensor.__init__(self, device, name, xiaomi_hub,
data_key, None)
def parse_data(self, data):
"""Parse data sent by gateway."""
value = data.get(self._data_key)
if value is None:
return False
if value == 'long_click_press':
self._state = True
click_type = 'long_click_press'
elif value == 'long_click_release':
self._state = False
click_type = 'hold'
elif value == 'click':
click_type = 'single'
elif value == 'double_click':
click_type = 'double'
elif value == 'both_click':
click_type = 'both'
else:
return False
self._hass.bus.fire('click', {
'entity_id': self.entity_id,
'click_type': click_type
})
if value in ['long_click_press', 'long_click_release']:
return True
return False
class XiaomiCube(XiaomiBinarySensor):
"""Representation of a Xiaomi Cube."""
def __init__(self, device, hass, xiaomi_hub):
"""Initialize the Xiaomi Cube."""
self._hass = hass
self._state = False
XiaomiBinarySensor.__init__(self, device, 'Cube', xiaomi_hub,
None, None)
def parse_data(self, data):
"""Parse data sent by gateway."""
if 'status' in data:
self._hass.bus.fire('cube_action', {
'entity_id': self.entity_id,
'action_type': data['status']
})
if 'rotate' in data:
self._hass.bus.fire('cube_action', {
'entity_id': self.entity_id,
'action_type': 'rotate',
'action_value': float(data['rotate'].replace(",", "."))
})
return False

View File

@@ -23,9 +23,7 @@ def get_device(values, **kwargs):
"""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
re_arm_multiplier = zwave.get_config_value(values.primary.node, 9) or 4
return ZWaveTriggerSensor(values, "motion", re_arm_multiplier * 8)
return ZWaveTriggerSensor(values, "motion")
if workaround.get_device_component_mapping(values.primary) == DOMAIN:
return ZWaveBinarySensor(values, None)
@@ -62,15 +60,21 @@ class ZWaveBinarySensor(BinarySensorDevice, zwave.ZWaveDeviceEntity):
class ZWaveTriggerSensor(ZWaveBinarySensor):
"""Representation of a stateless sensor within Z-Wave."""
def __init__(self, values, device_class, re_arm_sec=60):
def __init__(self, values, device_class):
"""Initialize the sensor."""
super(ZWaveTriggerSensor, self).__init__(values, device_class)
self.re_arm_sec = re_arm_sec
# Set default off delay to 60 sec
self.re_arm_sec = 60
self.invalidate_after = None
def update_properties(self):
"""Handle value changes for this entity's node."""
self._state = self.values.primary.data
_LOGGER.debug('off_delay=%s', self.values.off_delay)
# Set re_arm_sec if off_delay is provided from the sensor
if self.values.off_delay:
_LOGGER.debug('off_delay.data=%s', self.values.off_delay.data)
self.re_arm_sec = self.values.off_delay.data * 8
# only allow this value to be true for re_arm secs
if not self.hass:
return

View File

@@ -12,6 +12,7 @@ import re
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 PLATFORM_SCHEMA # noqa
from homeassistant.helpers.config_validation import time_period_str
from homeassistant.helpers.entity import Entity, generate_entity_id
from homeassistant.helpers.entity_component import EntityComponent

View File

@@ -0,0 +1,19 @@
todoist:
new_task:
description: Create a new task and add it to a project.
fields:
content:
description: The name of the task. [Required]
example: Pick up the mail
project:
description: The name of the project this task should belong to. Defaults to Inbox. [Optional]
example: Errands
labels:
description: Any labels that you want to apply to this task, separated by a comma. [Optional]
example: Chores,Deliveries
priority:
description: The priority of this task, from 1 (normal) to 4 (urgent). [Optional]
example: 2
due_date:
description: The day this task is due, in format YYYY-MM-DD. [Optional]
example: "2018-04-01"

View File

@@ -0,0 +1,544 @@
"""
Support for Todoist task management (https://todoist.com).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/calendar.todoist/
"""
from datetime import datetime
from datetime import timedelta
import logging
import os
import voluptuous as vol
from homeassistant.components.calendar import (
CalendarEventDevice, PLATFORM_SCHEMA)
from homeassistant.components.google import (
CONF_DEVICE_ID)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
CONF_ID, CONF_NAME, CONF_TOKEN)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.util import dt
from homeassistant.util import Throttle
REQUIREMENTS = ['todoist-python==7.0.17']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'todoist'
# Calendar Platform: Does this calendar event last all day?
ALL_DAY = 'all_day'
# Attribute: All tasks in this project
ALL_TASKS = 'all_tasks'
# Todoist API: "Completed" flag -- 1 if complete, else 0
CHECKED = 'checked'
# Attribute: Is this task complete?
COMPLETED = 'completed'
# Todoist API: What is this task about?
# Service Call: What is this task about?
CONTENT = 'content'
# Calendar Platform: Get a calendar event's description
DESCRIPTION = 'description'
# Calendar Platform: Used in the '_get_date()' method
DATETIME = 'dateTime'
# Attribute: When is this task due?
# Service Call: When is this task due?
DUE_DATE = 'due_date'
# Todoist API: Look up a task's due date
DUE_DATE_UTC = 'due_date_utc'
# Attribute: Is this task due today?
DUE_TODAY = 'due_today'
# Calendar Platform: When a calendar event ends
END = 'end'
# Todoist API: Look up a Project/Label/Task ID
ID = 'id'
# Todoist API: Fetch all labels
# Service Call: What are the labels attached to this task?
LABELS = 'labels'
# Todoist API: "Name" value
NAME = 'name'
# Attribute: Is this task overdue?
OVERDUE = 'overdue'
# Attribute: What is this task's priority?
# Todoist API: Get a task's priority
# Service Call: What is this task's priority?
PRIORITY = 'priority'
# Todoist API: Look up the Project ID a Task belongs to
PROJECT_ID = 'project_id'
# Service Call: What Project do you want a Task added to?
PROJECT_NAME = 'project'
# Todoist API: Fetch all Projects
PROJECTS = 'projects'
# Calendar Platform: When does a calendar event start?
START = 'start'
# Calendar Platform: What is the next calendar event about?
SUMMARY = 'summary'
# Todoist API: Fetch all Tasks
TASKS = 'items'
SERVICE_NEW_TASK = 'new_task'
NEW_TASK_SERVICE_SCHEMA = vol.Schema({
vol.Required(CONTENT): cv.string,
vol.Optional(PROJECT_NAME, default='inbox'): vol.All(cv.string, vol.Lower),
vol.Optional(LABELS): cv.ensure_list_csv,
vol.Optional(PRIORITY): vol.All(vol.Coerce(int),
vol.Range(min=1, max=4)),
vol.Optional(DUE_DATE): cv.string
})
CONF_EXTRA_PROJECTS = 'custom_projects'
CONF_PROJECT_DUE_DATE = 'due_date_days'
CONF_PROJECT_WHITELIST = 'include_projects'
CONF_PROJECT_LABEL_WHITELIST = 'labels'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_TOKEN): cv.string,
vol.Optional(CONF_EXTRA_PROJECTS, default=[]):
vol.All(cv.ensure_list, vol.Schema([
vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_PROJECT_DUE_DATE): vol.Coerce(int),
vol.Optional(CONF_PROJECT_WHITELIST, default=[]):
vol.All(cv.ensure_list, [vol.All(cv.string, vol.Lower)]),
vol.Optional(CONF_PROJECT_LABEL_WHITELIST, default=[]):
vol.All(cv.ensure_list, [vol.All(cv.string, vol.Lower)])
})
]))
})
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Todoist platform."""
# Check token:
token = config.get(CONF_TOKEN)
# Look up IDs based on (lowercase) names.
project_id_lookup = {}
label_id_lookup = {}
from todoist.api import TodoistAPI
api = TodoistAPI(token)
api.sync()
# Setup devices:
# Grab all projects.
projects = api.state[PROJECTS]
# Grab all labels
labels = api.state[LABELS]
# Add all Todoist-defined projects.
project_devices = []
for project in projects:
# Project is an object, not a dict!
# Because of that, we convert what we need to a dict.
project_data = {
CONF_NAME: project[NAME],
CONF_ID: project[ID]
}
project_devices.append(
TodoistProjectDevice(hass, project_data, labels, api)
)
# Cache the names so we can easily look up name->ID.
project_id_lookup[project[NAME].lower()] = project[ID]
# Cache all label names
for label in labels:
label_id_lookup[label[NAME].lower()] = label[ID]
# Check config for more projects.
extra_projects = config.get(CONF_EXTRA_PROJECTS)
for project in extra_projects:
# Special filter: By date
project_due_date = project.get(CONF_PROJECT_DUE_DATE)
# Special filter: By label
project_label_filter = project.get(CONF_PROJECT_LABEL_WHITELIST)
# Special filter: By name
# Names must be converted into IDs.
project_name_filter = project.get(CONF_PROJECT_WHITELIST)
project_id_filter = [
project_id_lookup[project_name.lower()]
for project_name in project_name_filter]
# Create the custom project and add it to the devices array.
project_devices.append(
TodoistProjectDevice(
hass, project, labels, api, project_due_date,
project_label_filter, project_id_filter
)
)
add_devices(project_devices)
# Services:
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def handle_new_task(call):
"""Called when a user creates a new Todoist Task from HASS."""
project_name = call.data[PROJECT_NAME]
project_id = project_id_lookup[project_name]
# Create the task
item = api.items.add(call.data[CONTENT], project_id)
if LABELS in call.data:
task_labels = call.data[LABELS]
label_ids = [
label_id_lookup[label.lower()]
for label in task_labels]
item.update(labels=label_ids)
if PRIORITY in call.data:
item.update(priority=call.data[PRIORITY])
if DUE_DATE in call.data:
due_date = dt.parse_datetime(call.data[DUE_DATE])
if due_date is None:
due = dt.parse_date(call.data[DUE_DATE])
due_date = datetime(due.year, due.month, due.day)
# Format it in the manner Todoist expects
due_date = dt.as_utc(due_date)
date_format = '%Y-%m-%dT%H:%M'
due_date = datetime.strftime(due_date, date_format)
item.update(due_date_utc=due_date)
# Commit changes
api.commit()
_LOGGER.debug("Created Todoist task: %s", call.data[CONTENT])
hass.services.register(DOMAIN, SERVICE_NEW_TASK, handle_new_task,
descriptions[DOMAIN][SERVICE_NEW_TASK],
schema=NEW_TASK_SERVICE_SCHEMA)
class TodoistProjectDevice(CalendarEventDevice):
"""A device for getting the next Task from a Todoist Project."""
def __init__(self, hass, data, labels, token,
latest_task_due_date=None, whitelisted_labels=None,
whitelisted_projects=None):
"""Create the Todoist Calendar Event Device."""
self.data = TodoistProjectData(
data, labels, token, latest_task_due_date,
whitelisted_labels, whitelisted_projects
)
# Set up the calendar side of things
calendar_format = {
CONF_NAME: data[CONF_NAME],
# Set Entity ID to use the name so we can identify calendars
CONF_DEVICE_ID: data[CONF_NAME]
}
super().__init__(hass, calendar_format)
def update(self):
"""Update all Todoist Calendars."""
# Set basic calendar data
super().update()
# Set Todoist-specific data that can't easily be grabbed
self._cal_data[ALL_TASKS] = [
task[SUMMARY] for task in self.data.all_project_tasks]
def cleanup(self):
"""Clean up all calendar data."""
super().cleanup()
self._cal_data[ALL_TASKS] = []
@property
def device_state_attributes(self):
"""Return the device state attributes."""
if self.data.event is None:
# No tasks, we don't REALLY need to show anything.
return {}
attributes = super().device_state_attributes
# Add additional attributes.
attributes[DUE_TODAY] = self.data.event[DUE_TODAY]
attributes[OVERDUE] = self.data.event[OVERDUE]
attributes[ALL_TASKS] = self._cal_data[ALL_TASKS]
attributes[PRIORITY] = self.data.event[PRIORITY]
attributes[LABELS] = self.data.event[LABELS]
return attributes
class TodoistProjectData(object):
"""
Class used by the Task Device service object to hold all Todoist Tasks.
This is analogous to the GoogleCalendarData found in the Google Calendar
component.
Takes an object with a 'name' field and optionally an 'id' field (either
user-defined or from the Todoist API), a Todoist API token, and an optional
integer specifying the latest number of days from now a task can be due (7
means everything due in the next week, 0 means today, etc.).
This object has an exposed 'event' property (used by the Calendar platform
to determine the next calendar event) and an exposed 'update' method (used
by the Calendar platform to poll for new calendar events).
The 'event' is a representation of a Todoist Task, with defined parameters
of 'due_today' (is the task due today?), 'all_day' (does the task have a
due date?), 'task_labels' (all labels assigned to the task), 'message'
(the content of the task, e.g. 'Fetch Mail'), 'description' (a URL pointing
to the task on the Todoist website), 'end_time' (what time the event is
due), 'start_time' (what time this event was last updated), 'overdue' (is
the task past its due date?), 'priority' (1-4, how important the task is,
with 4 being the most important), and 'all_tasks' (all tasks in this
project, sorted by how important they are).
'offset_reached', 'location', and 'friendly_name' are defined by the
platform itself, but are not used by this component at all.
The 'update' method polls the Todoist API for new projects/tasks, as well
as any updates to current projects/tasks. This is throttled to every
MIN_TIME_BETWEEN_UPDATES minutes.
"""
def __init__(self, project_data, labels, api,
latest_task_due_date=None, whitelisted_labels=None,
whitelisted_projects=None):
"""Initialize a Todoist Project."""
self.event = None
self._api = api
self._name = project_data.get(CONF_NAME)
# If no ID is defined, fetch all tasks.
self._id = project_data.get(CONF_ID)
# All labels the user has defined, for easy lookup.
self._labels = labels
# Not tracked: order, indent, comment_count.
self.all_project_tasks = []
# The latest date a task can be due (for making lists of everything
# due today, or everything due in the next week, for example).
if latest_task_due_date is not None:
self._latest_due_date = dt.utcnow() + timedelta(
days=latest_task_due_date)
else:
self._latest_due_date = None
# Only tasks with one of these labels will be included.
if whitelisted_labels is not None:
self._label_whitelist = whitelisted_labels
else:
self._label_whitelist = []
# This project includes only projects with these names.
if whitelisted_projects is not None:
self._project_id_whitelist = whitelisted_projects
else:
self._project_id_whitelist = []
def create_todoist_task(self, data):
"""
Create a dictionary based on a Task passed from the Todoist API.
Will return 'None' if the task is to be filtered out.
"""
task = {}
# Fields are required to be in all returned task objects.
task[SUMMARY] = data[CONTENT]
task[COMPLETED] = data[CHECKED] == 1
task[PRIORITY] = data[PRIORITY]
task[DESCRIPTION] = 'https://todoist.com/showTask?id={}'.format(
data[ID])
# All task Labels (optional parameter).
task[LABELS] = [
label[NAME].lower() for label in self._labels
if label[ID] in data[LABELS]]
if self._label_whitelist and (
not any(label in task[LABELS]
for label in self._label_whitelist)):
# We're not on the whitelist, return invalid task.
return None
# Due dates (optional parameter).
# The due date is the END date -- the task cannot be completed
# past this time.
# That means that the START date is the earliest time one can
# complete the task.
# Generally speaking, that means right now.
task[START] = dt.utcnow()
if data[DUE_DATE_UTC] is not None:
due_date = data[DUE_DATE_UTC]
# Due dates are represented in RFC3339 format, in UTC.
# Home Assistant exclusively uses UTC, so it'll
# handle the conversion.
time_format = '%a %d %b %Y %H:%M:%S %z'
# HASS' built-in parse time function doesn't like
# Todoist's time format; strptime has to be used.
task[END] = datetime.strptime(due_date, time_format)
if self._latest_due_date is not None and (
task[END] > self._latest_due_date):
# This task is out of range of our due date;
# it shouldn't be counted.
return None
task[DUE_TODAY] = task[END].date() == datetime.today().date()
# Special case: Task is overdue.
if task[END] <= task[START]:
task[OVERDUE] = True
# Set end time to the current time plus 1 hour.
# We're pretty much guaranteed to update within that 1 hour,
# so it should be fine.
task[END] = task[START] + timedelta(hours=1)
else:
task[OVERDUE] = False
else:
# If we ask for everything due before a certain date, don't count
# things which have no due dates.
if self._latest_due_date is not None:
return None
# Define values for tasks without due dates
task[END] = None
task[ALL_DAY] = True
task[DUE_TODAY] = False
task[OVERDUE] = False
# Not tracked: id, comments, project_id order, indent, recurring.
return task
@staticmethod
def select_best_task(project_tasks):
"""
Search through a list of events for the "best" event to select.
The "best" event is determined by the following criteria:
* A proposed event must not be completed
* A proposed event must have a end date (otherwise we go with
the event at index 0, selected above)
* A proposed event must be on the same day or earlier as our
current event
* If a proposed event is an earlier day than what we have so
far, select it
* If a proposed event is on the same day as our current event
and the proposed event has a higher priority than our current
event, select it
* If a proposed event is on the same day as our current event,
has the same priority as our current event, but is due earlier
in the day, select it
"""
# Start at the end of the list, so if tasks don't have a due date
# the newest ones are the most important.
event = project_tasks[-1]
for proposed_event in project_tasks:
if event == proposed_event:
continue
if proposed_event[COMPLETED]:
# Event is complete!
continue
if proposed_event[END] is None:
# No end time:
if event[END] is None and (
proposed_event[PRIORITY] < event[PRIORITY]):
# They also have no end time,
# but we have a higher priority.
event = proposed_event
continue
else:
continue
elif event[END] is None:
# We have an end time, they do not.
event = proposed_event
continue
if proposed_event[END].date() > event[END].date():
# Event is too late.
continue
elif proposed_event[END].date() < event[END].date():
# Event is earlier than current, select it.
event = proposed_event
continue
else:
if proposed_event[PRIORITY] > event[PRIORITY]:
# Proposed event has a higher priority.
event = proposed_event
continue
elif proposed_event[PRIORITY] == event[PRIORITY] and (
proposed_event[END] < event[END]):
event = proposed_event
continue
return event
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
if self._id is None:
project_task_data = [
task for task in self._api.state[TASKS]
if not self._project_id_whitelist or
task[PROJECT_ID] in self._project_id_whitelist]
else:
project_task_data = self._api.projects.get_data(self._id)[TASKS]
# If we have no data, we can just return right away.
if not project_task_data:
self.event = None
return True
# Keep an updated list of all tasks in this project.
project_tasks = []
for task in project_task_data:
todoist_task = self.create_todoist_task(task)
if todoist_task is not None:
# A None task means it is invalid for this project
project_tasks.append(todoist_task)
if not project_tasks:
# We had no valid tasks
return True
# Organize the best tasks (so users can see all the tasks
# they have, organized)
while len(project_tasks) > 0:
best_task = self.select_best_task(project_tasks)
_LOGGER.debug("Found Todoist Task: %s", best_task[SUMMARY])
project_tasks.remove(best_task)
self.all_project_tasks.append(best_task)
self.event = self.all_project_tasks[0]
# Convert datetime to a string again
if self.event is not None:
if self.event[START] is not None:
self.event[START] = {
DATETIME: self.event[START].strftime(DATE_STR_FORMAT)
}
if self.event[END] is not None:
self.event[END] = {
DATETIME: self.event[END].strftime(DATE_STR_FORMAT)
}
else:
# HASS gets cranky if a calendar event never ends
# Let's set our "due date" to tomorrow
self.event[END] = {
DATETIME: (
datetime.utcnow() +
timedelta(days=1)
).strftime(DATE_STR_FORMAT)
}
_LOGGER.debug("Updated %s", self._name)
return True

View File

@@ -23,6 +23,7 @@ from homeassistant.core import callback
from homeassistant.const import (ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE)
from homeassistant.config import load_yaml_config_file
from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import bind_hass
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@@ -55,6 +56,7 @@ CAMERA_SERVICE_SCHEMA = vol.Schema({
})
@bind_hass
def enable_motion_detection(hass, entity_id=None):
"""Enable Motion Detection."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
@@ -62,6 +64,7 @@ def enable_motion_detection(hass, entity_id=None):
DOMAIN, SERVICE_EN_MOTION, data))
@bind_hass
def disable_motion_detection(hass, entity_id=None):
"""Disable Motion Detection."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None

View File

@@ -0,0 +1,101 @@
"""
This component provides HA camera support for Abode Security System.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.abode/
"""
import asyncio
import logging
from datetime import timedelta
import requests
from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN
from homeassistant.components.camera import Camera
from homeassistant.util import Throttle
DEPENDENCIES = ['abode']
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discoveryy_info=None):
"""Set up Abode camera devices."""
import abodepy.helpers.constants as CONST
import abodepy.helpers.timeline as TIMELINE
data = hass.data[ABODE_DOMAIN]
devices = []
for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA):
if data.is_excluded(device):
continue
devices.append(AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE))
data.devices.extend(devices)
add_devices(devices)
class AbodeCamera(AbodeDevice, Camera):
"""Representation of an Abode camera."""
def __init__(self, data, device, event):
"""Initialize the Abode device."""
AbodeDevice.__init__(self, data, device)
Camera.__init__(self)
self._event = event
self._response = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe Abode events."""
yield from super().async_added_to_hass()
self.hass.async_add_job(
self._data.abode.events.add_timeline_callback,
self._event, self._capture_callback
)
def capture(self):
"""Request a new image capture."""
return self._device.capture()
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def refresh_image(self):
"""Find a new image on the timeline."""
if self._device.refresh_image():
self.get_image()
def get_image(self):
"""Attempt to download the most recent capture."""
if self._device.image_url:
try:
self._response = requests.get(
self._device.image_url, stream=True)
self._response.raise_for_status()
except requests.HTTPError as err:
_LOGGER.warning("Failed to get camera image: %s", err)
self._response = None
else:
self._response = None
def camera_image(self):
"""Get a camera image."""
self.refresh_image()
if self._response:
return self._response.content
return None
def _capture_callback(self, capture):
"""Update the image with the device then refresh device."""
self._device.update_image_location(capture)
self.get_image()
self.schedule_update_ha_state()

View File

@@ -62,7 +62,7 @@ class AmcrestCam(Camera):
self._token = self._auth = authentication
def camera_image(self):
"""Return a still image reponse from the camera."""
"""Return a still image response from the camera."""
# Send the request to snap a picture and return raw jpg data
response = self._camera.snapshot(channel=self._resolution)
return response.data

View File

@@ -14,15 +14,31 @@ from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
from homeassistant.components.arlo import DEFAULT_BRAND, DATA_ARLO
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import ATTR_BATTERY_LEVEL
DEPENDENCIES = ['arlo', 'ffmpeg']
_LOGGER = logging.getLogger(__name__)
ATTR_BRIGHTNESS = 'brightness'
ATTR_FLIPPED = 'flipped'
ATTR_MIRRORED = 'mirrored'
ATTR_MOTION_SENSITIVITY = 'motion_detection_sensitivity'
ATTR_POWER_SAVE_MODE = 'power_save_mode'
ATTR_SIGNAL_STRENGTH = 'signal_strength'
ATTR_UNSEEN_VIDEOS = 'unseen_videos'
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
ARLO_MODE_ARMED = 'armed'
ARLO_MODE_DISARMED = 'disarmed'
POWERSAVE_MODE_MAPPING = {
1: 'best_battery_life',
2: 'optimized',
3: 'best_video'
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
})
@@ -80,6 +96,28 @@ class ArloCam(Camera):
"""Return the name of this camera."""
return self._name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_BATTERY_LEVEL:
self._camera.get_battery_level,
ATTR_BRIGHTNESS:
self._camera.get_brightness,
ATTR_FLIPPED:
self._camera.get_flip_state,
ATTR_MIRRORED:
self._camera.get_mirror_state,
ATTR_MOTION_SENSITIVITY:
self._camera.get_motion_detection_sensitivity,
ATTR_POWER_SAVE_MODE:
POWERSAVE_MODE_MAPPING[self._camera.get_powersave_mode],
ATTR_SIGNAL_STRENGTH:
self._camera.get_signal_strength,
ATTR_UNSEEN_VIDEOS:
self._camera.unseen_videos
}
@property
def model(self):
"""Camera model."""

View File

@@ -7,7 +7,7 @@ https://home-assistant.io/components/camera.axis/
import logging
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT,
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
@@ -19,38 +19,44 @@ DOMAIN = 'axis'
DEPENDENCIES = [DOMAIN]
def _get_image_url(host, mode):
def _get_image_url(host, port, mode):
if mode == 'mjpeg':
return 'http://{}/axis-cgi/mjpg/video.cgi'.format(host)
return 'http://{}:{}/axis-cgi/mjpg/video.cgi'.format(host, port)
elif mode == 'single':
return 'http://{}/axis-cgi/jpg/image.cgi'.format(host)
return 'http://{}:{}/axis-cgi/jpg/image.cgi'.format(host, port)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis camera."""
config = {
camera_config = {
CONF_NAME: discovery_info[CONF_NAME],
CONF_USERNAME: discovery_info[CONF_USERNAME],
CONF_PASSWORD: discovery_info[CONF_PASSWORD],
CONF_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST], 'mjpeg'),
CONF_MJPEG_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'mjpeg'),
CONF_STILL_IMAGE_URL: _get_image_url(discovery_info[CONF_HOST],
str(discovery_info[CONF_PORT]),
'single'),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
add_devices([AxisCamera(hass, config)])
add_devices([AxisCamera(hass,
camera_config,
str(discovery_info[CONF_PORT]))])
class AxisCamera(MjpegCamera):
"""AxisCamera class."""
def __init__(self, hass, config):
def __init__(self, hass, config, port):
"""Initialize Axis Communications camera component."""
super().__init__(hass, config)
self.port = port
async_dispatcher_connect(hass,
DOMAIN + '_' + config[CONF_NAME] + '_new_ip',
self._new_ip)
def _new_ip(self, host):
"""Set new IP for video stream."""
self._mjpeg_url = _get_image_url(host, 'mjpeg')
self._still_image_url = _get_image_url(host, 'mjpeg')
self._mjpeg_url = _get_image_url(host, self.port, 'mjpeg')
self._still_image_url = _get_image_url(host, self.port, 'single')

View File

@@ -76,6 +76,6 @@ class BlinkCamera(Camera):
return self.data.camera_thumbs[self._name]
def camera_image(self):
"""Return a still image reponse from the camera."""
"""Return a still image response from the camera."""
self.request_image()
return self.response.content

View File

@@ -0,0 +1,90 @@
"""Support for viewing the camera feed from a DoorBird video doorbell."""
import asyncio
import datetime
import logging
import voluptuous as vol
import aiohttp
import async_timeout
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
from homeassistant.components.doorbird import DOMAIN as DOORBIRD_DOMAIN
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
DEPENDENCIES = ['doorbird']
_CAMERA_LIVE = "DoorBird Live"
_CAMERA_LAST_VISITOR = "DoorBird Last Ring"
_LIVE_INTERVAL = datetime.timedelta(seconds=1)
_LAST_VISITOR_INTERVAL = datetime.timedelta(minutes=1)
_LOGGER = logging.getLogger(__name__)
_TIMEOUT = 10 # seconds
CONF_SHOW_LAST_VISITOR = 'last_visitor'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SHOW_LAST_VISITOR, default=False): cv.boolean
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the DoorBird camera platform."""
device = hass.data.get(DOORBIRD_DOMAIN)
_LOGGER.debug("Adding DoorBird camera %s", _CAMERA_LIVE)
entities = [DoorBirdCamera(device.live_image_url, _CAMERA_LIVE,
_LIVE_INTERVAL)]
if config.get(CONF_SHOW_LAST_VISITOR):
_LOGGER.debug("Adding DoorBird camera %s", _CAMERA_LAST_VISITOR)
entities.append(DoorBirdCamera(device.history_image_url(1),
_CAMERA_LAST_VISITOR,
_LAST_VISITOR_INTERVAL))
async_add_devices(entities)
_LOGGER.info("Added DoorBird camera(s)")
class DoorBirdCamera(Camera):
"""The camera on a DoorBird device."""
def __init__(self, url, name, interval=None):
"""Initialize the camera on a DoorBird device."""
self._url = url
self._name = name
self._last_image = None
self._interval = interval or datetime.timedelta
self._last_update = datetime.datetime.min
super().__init__()
@property
def name(self):
"""Get the name of the camera."""
return self._name
@asyncio.coroutine
def async_camera_image(self):
"""Pull a still image from the camera."""
now = datetime.datetime.now()
if self._last_image and now - self._last_update < self._interval:
return self._last_image
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(_TIMEOUT, loop=self.hass.loop):
response = yield from websession.get(self._url)
self._last_image = yield from response.read()
self._last_update = now
return self._last_image
except asyncio.TimeoutError:
_LOGGER.error("Camera image timed out")
return self._last_image
except aiohttp.ClientError as error:
_LOGGER.error("Error getting camera image: %s", error)
return self._last_image

View File

@@ -6,7 +6,6 @@ https://home-assistant.io/components/camera.foscam/
"""
import logging
import requests
import voluptuous as vol
from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
@@ -16,11 +15,15 @@ from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['libpyfoscam==1.0']
CONF_IP = 'ip'
DEFAULT_NAME = 'Foscam Camera'
DEFAULT_PORT = 88
FOSCAM_COMM_ERROR = -8
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_IP): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
@@ -33,46 +36,60 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a Foscam IP Camera."""
add_devices([FoscamCamera(config)])
add_devices([FoscamCam(config)])
class FoscamCamera(Camera):
class FoscamCam(Camera):
"""An implementation of a Foscam IP camera."""
def __init__(self, device_info):
"""Initialize a Foscam camera."""
super(FoscamCamera, self).__init__()
super(FoscamCam, self).__init__()
ip_address = device_info.get(CONF_IP)
port = device_info.get(CONF_PORT)
self._base_url = 'http://{}:{}/'.format(ip_address, port)
uri_template = self._base_url \
+ 'cgi-bin/CGIProxy.fcgi?' \
+ 'cmd=snapPicture2&usr={}&pwd={}'
self._username = device_info.get(CONF_USERNAME)
self._password = device_info.get(CONF_PASSWORD)
self._snap_picture_url = uri_template.format(
self._username,
self._password
)
self._name = device_info.get(CONF_NAME)
self._motion_status = False
_LOGGER.info("Using the following URL for %s: %s",
self._name, uri_template.format('***', '***'))
from libpyfoscam import FoscamCamera
self._foscam_session = FoscamCamera(ip_address, port, self._username,
self._password, verbose=False)
def camera_image(self):
"""Return a still image reponse from the camera."""
"""Return a still image response from the camera."""
# Send the request to snap a picture and return raw jpg data
# Handle exception if host is not reachable or url failed
try:
response = requests.get(self._snap_picture_url, timeout=10)
except requests.exceptions.ConnectionError:
result, response = self._foscam_session.snap_picture_2()
if result == FOSCAM_COMM_ERROR:
return None
return response
@property
def motion_detection_enabled(self):
"""Camera Motion Detection Status."""
return self._motion_status
def enable_motion_detection(self):
"""Enable motion detection in camera."""
ret, err = self._foscam_session.enable_motion_detection()
if ret == FOSCAM_COMM_ERROR:
_LOGGER.debug("Unable to communicate with Foscam Camera: %s", err)
self._motion_status = True
else:
return response.content
self._motion_status = False
def disable_motion_detection(self):
"""Disable motion detection."""
ret, err = self._foscam_session.disable_motion_detection()
if ret == FOSCAM_COMM_ERROR:
_LOGGER.debug("Unable to communicate with Foscam Camera: %s", err)
self._motion_status = True
else:
self._motion_status = False
@property
def name(self):

View File

@@ -6,6 +6,7 @@ https://home-assistant.io/components/camera.onvif/
"""
import asyncio
import logging
import os
import voluptuous as vol
@@ -54,6 +55,7 @@ class ONVIFCamera(Camera):
def __init__(self, hass, config):
"""Initialize a ONVIF camera."""
from onvif import ONVIFService
import onvif
super().__init__()
self._name = config.get(CONF_NAME)
@@ -63,7 +65,7 @@ class ONVIFCamera(Camera):
config.get(CONF_HOST), config.get(CONF_PORT)),
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
'{}/deps/onvif/wsdl/media.wsdl'.format(hass.config.config_dir)
'{}/wsdl/media.wsdl'.format(os.path.dirname(onvif.__file__))
)
self._input = media.GetStreamUri().Uri
_LOGGER.debug("ONVIF Camera Using the following URL for %s: %s",

View File

@@ -7,44 +7,25 @@ https://home-assistant.io/components/camera.synology/
import asyncio
import logging
import requests
import voluptuous as vol
import aiohttp
import async_timeout
from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, CONF_TIMEOUT)
from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_create_clientsession,
async_create_clientsession,
async_aiohttp_proxy_web)
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
REQUIREMENTS = ['py-synology==0.1.1']
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Synology Camera'
DEFAULT_STREAM_ID = '0'
DEFAULT_TIMEOUT = 5
CONF_CAMERA_NAME = 'camera_name'
CONF_STREAM_ID = 'stream_id'
QUERY_CGI = 'query.cgi'
QUERY_API = 'SYNO.API.Info'
AUTH_API = 'SYNO.API.Auth'
CAMERA_API = 'SYNO.SurveillanceStation.Camera'
STREAMING_API = 'SYNO.SurveillanceStation.VideoStream'
SESSION_ID = '0'
WEBAPI_PATH = '/webapi/'
AUTH_PATH = 'auth.cgi'
CAMERA_PATH = 'camera.cgi'
STREAMING_PATH = 'SurveillanceStation/videoStreaming.cgi'
CONTENT_TYPE_HEADER = 'Content-Type'
SYNO_API_URL = '{0}{1}{2}'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -62,189 +43,89 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up a Synology IP Camera."""
verify_ssl = config.get(CONF_VERIFY_SSL)
timeout = config.get(CONF_TIMEOUT)
websession_init = async_get_clientsession(hass, verify_ssl)
# Determine API to use for authentication
syno_api_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, QUERY_CGI)
query_payload = {
'api': QUERY_API,
'method': 'Query',
'version': '1',
'query': 'SYNO.'
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
query_req = yield from websession_init.get(
syno_api_url,
params=query_payload
)
# Skip content type check because Synology doesn't return JSON with
# right content type
query_resp = yield from query_req.json(content_type=None)
auth_path = query_resp['data'][AUTH_API]['path']
camera_api = query_resp['data'][CAMERA_API]['path']
camera_path = query_resp['data'][CAMERA_API]['path']
streaming_path = query_resp['data'][STREAMING_API]['path']
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_api_url)
from synology.surveillance_station import SurveillanceStation
surveillance = SurveillanceStation(
config.get(CONF_URL),
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
verify_ssl=verify_ssl,
timeout=timeout
)
except (requests.exceptions.RequestException, ValueError):
_LOGGER.exception("Error when initializing SurveillanceStation")
return False
# Authticate to NAS to get a session id
syno_auth_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, auth_path)
session_id = yield from get_session_id(
hass,
websession_init,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD),
syno_auth_url,
timeout
)
# init websession
websession = async_create_clientsession(
hass, verify_ssl, cookies={'id': session_id})
# Use SessionID to get cameras in system
syno_camera_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, camera_api)
camera_payload = {
'api': CAMERA_API,
'method': 'List',
'version': '1'
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
camera_req = yield from websession.get(
syno_camera_url,
params=camera_payload
)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_camera_url)
return False
camera_resp = yield from camera_req.json(content_type=None)
cameras = camera_resp['data']['cameras']
cameras = surveillance.get_all_cameras()
websession = async_create_clientsession(hass, verify_ssl)
# add cameras
devices = []
for camera in cameras:
if not config.get(CONF_WHITELIST):
camera_id = camera['id']
snapshot_path = camera['snapshot_path']
device = SynologyCamera(
hass, websession, config, camera_id, camera['name'],
snapshot_path, streaming_path, camera_path, auth_path, timeout
)
device = SynologyCamera(websession, surveillance, camera.camera_id)
devices.append(device)
async_add_devices(devices)
@asyncio.coroutine
def get_session_id(hass, websession, username, password, login_url, timeout):
"""Get a session id."""
auth_payload = {
'api': AUTH_API,
'method': 'Login',
'version': '2',
'account': username,
'passwd': password,
'session': 'SurveillanceStation',
'format': 'sid'
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
auth_req = yield from websession.get(
login_url,
params=auth_payload
)
auth_resp = yield from auth_req.json(content_type=None)
return auth_resp['data']['sid']
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", login_url)
return False
class SynologyCamera(Camera):
"""An implementation of a Synology NAS based IP camera."""
def __init__(self, hass, websession, config, camera_id,
camera_name, snapshot_path, streaming_path, camera_path,
auth_path, timeout):
def __init__(self, websession, surveillance, camera_id):
"""Initialize a Synology Surveillance Station camera."""
super().__init__()
self.hass = hass
self._websession = websession
self._name = camera_name
self._synology_url = config.get(CONF_URL)
self._camera_name = config.get(CONF_CAMERA_NAME)
self._stream_id = config.get(CONF_STREAM_ID)
self._surveillance = surveillance
self._camera_id = camera_id
self._snapshot_path = snapshot_path
self._streaming_path = streaming_path
self._camera_path = camera_path
self._auth_path = auth_path
self._timeout = timeout
self._camera = self._surveillance.get_camera(camera_id)
self._motion_setting = self._surveillance.get_motion_setting(camera_id)
self.is_streaming = self._camera.is_enabled
def camera_image(self):
"""Return bytes of camera image."""
return run_coroutine_threadsafe(
self.async_camera_image(), self.hass.loop).result()
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
image_url = SYNO_API_URL.format(
self._synology_url, WEBAPI_PATH, self._camera_path)
image_payload = {
'api': CAMERA_API,
'method': 'GetSnapshot',
'version': '1',
'cameraId': self._camera_id
}
try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
response = yield from self._websession.get(
image_url,
params=image_payload
)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error fetching %s", image_url)
return None
image = yield from response.read()
return image
return self._surveillance.get_camera_image(self._camera_id)
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Return a MJPEG stream image response directly from the camera."""
streaming_url = SYNO_API_URL.format(
self._synology_url, WEBAPI_PATH, self._streaming_path)
streaming_payload = {
'api': STREAMING_API,
'method': 'Stream',
'version': '1',
'cameraId': self._camera_id,
'format': 'mjpeg'
}
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
streaming_url = self._camera.video_stream_url
stream_coro = self._websession.get(streaming_url)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
@property
def name(self):
"""Return the name of this device."""
return self._name
return self._camera.name
@property
def is_recording(self):
"""Return true if the device is recording."""
return self._camera.is_recording
def should_poll(self):
"""Update the recording state periodically."""
return True
def update(self):
"""Update the status of the camera."""
self._surveillance.update()
self._camera = self._surveillance.get_camera(self._camera.camera_id)
self._motion_setting = self._surveillance.get_motion_setting(
self._camera.camera_id)
self.is_streaming = self._camera.is_enabled
@property
def motion_detection_enabled(self):
"""Return the camera motion detection status."""
return self._motion_setting.is_enabled
def enable_motion_detection(self):
"""Enable motion detection in the camera."""
self._surveillance.enable_motion_detection(self._camera_id)
def disable_motion_detection(self):
"""Disable motion detection in camera."""
self._surveillance.disable_motion_detection(self._camera_id)

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