Compare commits

..

892 Commits
0.56 ... 0.61.1

Author SHA1 Message Date
Paulus Schoutsen
37eb6c90b6 Merge pull request #11716 from home-assistant/release-0-61-1
0.61.1
2018-01-16 14:33:12 -08:00
Lukas Barth
fc302186b2 Fix purge with MariaDB / MySQL (#11713) 2018-01-16 13:37:38 -08:00
Bob Anderson
903ca567c5 History order bugfix and opt-in option (#11686)
* make history view re-ordering optional and opt-in, also fix type bug

* use python false for default value

* whitespace cleanup
2018-01-16 13:37:37 -08:00
Daniel Høyer Iversen
fd75f157d6 Rfxtrx fix (#11678)
* fix voluptuous bug in rfxtrx

* fix voluptuous bug in rfxtrx

* fix voluptuous bug in rfxtrx
2018-01-16 13:37:37 -08:00
Anders Melchiorsen
f86224a64e Move several local services to their right domain (#11677)
* Move several local services to their right domain

* Fix lint
2018-01-16 13:37:37 -08:00
Russell Cloran
ed41252207 Fix zha color probe (#11670) 2018-01-16 13:37:36 -08:00
Paulus Schoutsen
6ec411b4bb Version bump to 0.61.1 2018-01-16 13:37:19 -08:00
Fabian Affolter
fc442361cd Bump release to 0.61.0 2018-01-15 13:27:54 +01:00
Fabian Affolter
c3ff5de016 Merge pull request #11589 from home-assistant/release-0-61
0.61
2018-01-15 10:35:00 +01:00
Alok Saboo
9b15cfa5a5 Update Pyarlo to 0.1.2 (#11626) 2018-01-15 09:38:08 +01:00
Jesse Hills
7837b4893f Use kelvin/mireds correctly for setting iglo white (#11622)
* Use kelvin/mireds correctly for setting iglo white

* Update requirements_all.txt

* Fix line lengths
2018-01-15 09:38:07 +01:00
Thijs de Jong
d88edb0661 patch stop command (#11612) 2018-01-15 09:38:07 +01:00
Fabian Affolter
7a50450b92 Upgrade yarl to 0.18.0 (#11609) 2018-01-15 09:38:06 +01:00
Daniel Høyer Iversen
3afa4726bf Xiaomi lib upgrade (#11603)
* upgrade xiaomi lib

* xiaomi lib
2018-01-15 09:38:06 +01:00
Daniel Høyer Iversen
218e97d965 Bugfix and cleanup for Rfxtrx (#11600)
* rfxtrx clean up

* rfxtrx clean up

* rfxtrx clean up
2018-01-15 09:38:05 +01:00
Bob Anderson
8d19192c9c Concord232 alarm arm away fix (#11597)
* fix arming away cmd for concord232 client

* bump required version of concord232 to 0.15
2018-01-15 09:38:05 +01:00
tschmidty69
179d99381d Snips add say and say_actions services (new) (#11596)
* Added snips.say and snips.say_action services

* Added snips.say and snips.say_action services

* Merged services.yaml changes I missed

* added tests for new service configs

* Woof

* Woof Woof

* Changed attribute names to follow hass standards.

* updated test_snips with new attribute names
2018-01-15 09:38:04 +01:00
Adam Mills
b8dfa4c3d2 Fix state for trigger with forced updates (#11595) 2018-01-15 09:38:03 +01:00
tschmidty69
92bec562ab Pushbullet email support (fix) (#11590)
* Simplified push calls

* Cleaned up and added unittests

* Fixed email parameter

* Fixed email parameter
2018-01-15 09:38:03 +01:00
Thijs de Jong
1802c0a922 Fix Tahoma stop command for 2 types of shutters (#11588)
* add working stop command

This fixes the stop command for 2 types of roller shutters

* fix line too long

* fix indentation

* fix indentation
2018-01-15 09:38:02 +01:00
Pascal Vizeli
f2cc00cc64 Core support for hass.io calls & Bugfix check_config (#11571)
* Initial overwrites

* Add check_config function.

* Update hassio.py

* Address comments

* add hassio support

* add more tests

* revert core changes

* Address check_config

* Address comment with api_bool

* Bugfix check_config

* Update core.py

* Update test_core.py

* Update config.py

* Update hassio.py

* Update config.py

* Update test_config.py
2018-01-15 09:38:02 +01:00
Jack Fan
033e06868e Avoid returning empty media_image_url string (#11557)
This relates to issue
https://github.com/home-assistant/home-assistant/issues/11556
2018-01-15 09:37:57 +01:00
Paulus Schoutsen
4883036789 Update frontend 2018-01-11 15:50:50 -08:00
Andreas Björshammar
965bd8a2e0 Added support for enable/disable motion detection (#11583)
* Added support for enable/disable motion detection

* Changed name of variable for exception, lint error

* Moved motion_detection_enabled property to the other properties
2018-01-11 15:44:23 -08:00
Thijs de Jong
2224d05607 add velux roller shutter to tahoma (#11586)
With #11538 I forgot to add another type of Roller shutters that should be supported.
2018-01-11 15:43:31 -08:00
cgtobi
ed66c21035 Cast return values and add unit tests for the yahoo weather component. (#10699)
* Add unittest for yahoo weather component.

* Add requitements for tests.

* Update requirements.

* Revert "Update requirements."

This reverts commit 024b5cf8bf.

* Update requirements.

* Randomize sample data.

* Remove unnecessary methods.

* Remove dependency and replace with MockDependency.

* Add temporary fix for yahoo weather API issue.
2018-01-11 15:42:51 -08:00
Paulus Schoutsen
9384e2c963 Pr/11430 (#11587)
* Fix error when name is non-latin script

When the name is non-latin script (e.g. Japanese), slugify returns empty string and causes failure in a later stage. This commit fixes the issue by using default name.

* Add test
2018-01-11 15:26:48 -08:00
Bob Anderson
6af42b4372 Control ordering of display in history component (#11340)
* Make the order shown in the history component match the ordering given in the configuration of included entities (if any)

* return the sorted result

* optimize sorting.  since entities only appear once, we can break from a search on first find, and no copy of the list is needed
2018-01-11 15:21:23 -08:00
Laqoore
1235dae5b8 Changed device type of media player and cover to switch (#11483)
* Changed device type of media player and cover to switch

Covers and media players should not be of device type 'light'. Example: If user requests all lights to switch to off, covers are closed and media players are affected too.

* Fix test
2018-01-11 15:01:38 -08:00
Andrey
a35d05ac7e Remove aux_heat support from Sensibo now that UI supports on/off (#11579) 2018-01-11 23:25:15 +01:00
kennedyshead
6b103cc9b9 Fix for asuswrt, telnet not working and presence-detection for router mode (#11422)
* Fix for telnet mode
Fix for presence detection

closes #11349 #10231

* Optimisation when device is disconnected
2018-01-11 14:12:27 -08:00
Sean Wilson
a23f60315f Fix bluetooth tracker source (#11469)
* Add new sources for bluetooth and bluetooth_le trackers (rather than just using the generic 'gps' catch-all).

* Fix lint issues
2018-01-11 14:10:47 -08:00
Adam Mills
3bc6a7da32 Support OSRAM lights on ZHA (#11522) 2018-01-11 13:56:00 -08:00
hawk259
dc5b610ee5 Alarmdecoder add validation of the zone types (#11488)
* Alarmdecoder add validation of the zone types

* fix line length
2018-01-11 13:53:14 -08:00
Daniel Perna
7723db9e62 Update pyhomematic, support new devices (#11578) 2018-01-11 20:08:09 +01:00
Ulrich Dobramysl
cf612c3d5b Make the rpi_rf component thread-safe using an RLock (#11487)
* Make the rpi_rf component thread-safe

The previous implementation suffered from race conditions when two rpi_rf switches are triggered at the same time. This implementation uses an RLock to give one thread at a time exclusive access to the rfdevice object.

* cleanup

* fix lint
2018-01-11 13:47:05 +01:00
Josh Anderson
d2b6660881 Tado improvements - hot water zone sensors and climate precision (#11521)
* Add tado hot water zone sensors

* Set precision to match tado app/website
2018-01-11 10:49:41 +01:00
Dan Nixon
3972d1d4c6 Mark REST binary_sensor unavailable if request fails (#11506)
* Mark REST binary_sensor unavailable if request fails

* Add test suite for RESTful binary sensor
2018-01-11 10:48:15 +01:00
Anders Melchiorsen
5fda78cf91 Fix new squeezebox service descriptions for lazy loading (#11574) 2018-01-11 10:15:59 +01:00
randellhodges
60ce2b343d Tracking all groups to allow changing of existing groups (#11444)
* Tracking all groups to allow changing of existing groups

* Unit tests

* Fix flake8 warnings in test
2018-01-10 14:13:22 -08:00
Andrey
6cc285aea5 Add sensibo_assume_state service to Sensibo climate (#11567) 2018-01-10 14:04:35 -08:00
Pascal Vizeli
c5d5d57e9b Extend hass.io services / updater (#11549)
* Extend hass.io services

* Add warning for carfuly options with hass.io

* update tests

* finish tests

* remove update calls

* address comments

* address comments p2

* fix tests

* fix tests

* Use token also for proxy

* Add test for server_host

* Fix test

* Fix tests

* Add test for version

* Address comments
2018-01-10 19:48:31 +01:00
Thijs de Jong
02979db3d6 Add Velux Windows to Tahoma (#11538)
* Add Velux Windows to Tahoma

* fix linit

* add supported Tahoma devices by @glpatcern

* hound

* lint

* fix logging

* lint

* lint

* remove blank line after docstring

* changes based on notes by @armills

* fix logging
2018-01-10 09:41:16 -05:00
Paulus Schoutsen
0d06e8c1c9 Test tweaks (#11560)
* Fix is_allowed_path on OS X

* Move APNS2 inside func in test
2018-01-10 10:48:17 +01:00
Paulus Schoutsen
d793cfeb68 Update frontend to 20180110.0 2018-01-10 00:52:35 -08:00
Paulus Schoutsen
382a62346b Update frontend to 20180110.0 2018-01-10 00:52:12 -08:00
Anders Melchiorsen
9e0ca719ed Deprecate explicit entity_id in template platforms (#11123)
* Deprecate explicit entity_id in template platforms

* Use config validator for deprecation

* Fix copy/paste typos

* Also print the config value

* Add test for config validator

* Assert the module name that logged the message
2018-01-10 00:06:26 -08:00
Paulus Schoutsen
88b70e964c Remove execution file perm (#11563) 2018-01-09 23:55:14 -08:00
Andrea Campi
4dda842b16 Try to fix crashes after Hue refactoring (#11270)
* Try to fix crashes after Hue refactoring

Refs #11183

* Fix usage of dispatcher_send via helper.

* Address review feedback.
2018-01-10 08:05:04 +01:00
Nolan Gilley
3cba09c6f6 Coinbase.com sensor platform (#11036)
* coinbase sensors

use hass.data, load_platform

* add exchange rate sensors

dont pass complex object over event bus

* check for auth error
2018-01-10 07:47:22 +01:00
Fabian Affolter
c4bc42d527 Upgrade keyring to 10.3.2 (#11531) 2018-01-09 19:51:35 -08:00
Phil Kates
cf04a81f70 Fix error on entity_config missing (#11561)
If the `google_assistant` key exists in the config but has no
`entity_config` key under it you'll get an error.

```
  File "/Users/pkates/src/home-assistant/homeassistant/components/google_assistant/http.py", line 51, in is_exposed
    entity_config.get(entity.entity_id, {}).get(CONF_EXPOSE)
AttributeError: 'NoneType' object has no attribute 'get'
```
2018-01-09 19:47:24 -08:00
Eric Pignet
92014bf1d1 Add 2 media_player services and 1 custom service to Squeezebox platform (#10969)
* Add 2 media_player services and 1 custom service to Squeezebox platform

* Fix pylint error

* Remove apostrophe in example

* Split method into command and parameters

* Fix Lint error
2018-01-09 22:05:21 -05:00
cdce8p
cba55402b1 Improved test runtime (#11553)
* Remove redundent assert statements and cleanup

* Added 'get_date' function

* Replace 'freeze_time' with 'mock.patch'
* Tox in 185s (py35)

* Removed obsolete 'freeze_time' from test_updater
* Tox 162s (py35)

* Remove test requirement 'freezegun'

* Fixed flake8 errors

* Added 'mock.patch' for 'feedparser.parse'

* Made 'FUNCTION_PATH' a constant

* Remove debug statements.
2018-01-09 16:00:49 -08:00
Fabian Affolter
f56b3d8e9c Upgrade lightify to 1.0.6.1 (#11545) 2018-01-09 15:35:34 -08:00
Paulus Schoutsen
8313225b40 Move Google Assistant entity config out of customize (#11499)
* Move Google Assistant entity config out of customize

* CONF_ALIAS -> CONF_ALIASES

* Lint
2018-01-09 15:14:56 -08:00
Sean Wilson
13042d5557 ZoneMinder event sensor updates (#11369)
* Switch the ZoneMinder event sensor over to use the consoleEvents API, and add monitored_conditions for 'hour', 'day', 'week', 'month' and 'all'. 'all' is enabled by default to provide backward compatibility with the old behaviour.

* - Switch to new string formatting
- Remove redundant validator

* De-lint
2018-01-09 21:58:26 +01:00
Fabian Affolter
85858885c8 Upgrade Sphinx to 1.6.6 (#11534) 2018-01-09 15:30:54 +01:00
Fabian Affolter
10f48fbf6b Upgrade python-etherscan-api to 0.0.2 (#11535) 2018-01-09 15:30:36 +01:00
Fabian Affolter
8b267e3faf Upgrade numpy to 1.14.0 (#11542) 2018-01-09 15:30:00 +01:00
tschmidty69
13effee679 Snips: (fix) support new intentName format (#11509)
* support new intentName format

* Added tests for new and old format names

* pylint warning fixes
2018-01-08 13:00:21 -08:00
Cameron Llewellyn
903cda08b1 Insteon local update (#11088)
* trying to rework device discovery. now the main component will do the getlinked and pass it to the sub-components. no longer any config needed other than what is needed to connect to the hub. device names are no longer stored. core team told us to stop using configurator to ask for names. there should be a way to set names in hass...possibly this https://home-assistant.io/docs/configuration/customizing-devices/

* fix device types

* make device names just be the isnteon device id

* revert some config changes

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* update insteon client

* linting fixes

* Error Clean up

* Update to make requested changes

* more changes

* Finish requested changes to components

* Fixing Rebase Conflicts

* fix device types

* make device names just be the isnteon device id

* revert some config changes

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* Update insteon_local.py

* update insteon client

* linting fixes

* Error Clean up

* Update to make requested changes

* more changes

* Finish requested changes to components

* Update Insteon_Local for performance improvements

* Fix errors from get_linked

* Fix typo

* Requested changes

* Fix spacing

* Clean up

* Requested Changes
2018-01-08 18:18:10 +01:00
Yien Xu
e0e2f739ba Add options feature to Baidu TTS. (#11462)
* Add options feature to Baidu TTS.

Add options feature: supported_options() and default_options() added, get_tts_audio updated to accommodate options.

* Add options feature to Baidu TTS.

Add options feature: supported_options() and default_options() added, get_tts_audio updated to accommodate options.

* Fix style

* Fix style

Change the order of content of lists and dictionaries.

* Fix style

Changed order of imports, and fixed grammar errors.
2018-01-08 17:22:50 +01:00
Michael Kutý
0e710099e0 Support pushing all sensors and fix wrong metrics. (#11159)
For example all metrics with unit % match humidity.
This generate correct metrics like this
# HELP nut_ups_battery_charge sensor.nut_ups_battery_charge
# TYPE nut_ups_battery_charge gauge
nut_ups_battery_charge{entity="sensor.nut_ups_battery_charge",friendly_name="NUT UPS Battery Charge"} 98.0
nut_ups_battery_charge{entity="sensor.nut_ups_battery_charge_2",friendly_name="NUT UPS Battery Charge"} 97.0
2018-01-08 17:11:45 +01:00
timstanley1985
b1b0a2589e MQTT json attributes (#11439)
* MQTT json attributes

* Fix lint

* Amends following comments

* Fix lint

* Fix lint

* Add test

* Fix typo

* Amends following comments

* New tests

* Fix lint

* Fix tests
2018-01-08 17:07:39 +01:00
ChristianKuehnel
dff36b8087 Extension sensor alpha_vantage (#11427)
* Extended sensor alpha_vantage

* Improved error handling if symbol is not found. Now we add the symbols that were found,
* Added option to give names and currencies to symbols.
* Added support to read foreign exchange information.
* Icons are selected based on the currency (where MDI has a matching icon)

* added missing line at the end of the file...

* renamed variable as required by pylint

* Fix typos, ordering, and style
2018-01-08 16:58:34 +01:00
florianj1
46ad194097 Fix Kodi channels media type (#11505)
* Update kodi.py

* Update kodi.py

Fix code style
2018-01-08 16:57:50 +01:00
Alex Osadchyy
2ba9d825a0 Reconnect before mochad switch send command (#11296)
* Connection to mochad occasionally stalls on RPi and CM19A. Reconnect one switch send command.

* Formatting and exception hanling fixes

* Moved import inside the method. Logging outside the try-catch.

* Tailing whitespaces.

* MockDependency on pymochad in unit tests to resolve exceptions

* patch pymochad MochadException in unit tests to resolve exceptions

* patch pymochad MochadException in unit tests to resolve exceptions

* cleaned unused import

* lint issue fixed

* pylint issue fixed
2018-01-08 08:32:24 +01:00
Chris Cowart
8c0035c5b3 New features for Owntracks device_tracker (#11480)
* New features for Owntracks device_tracker

- Supporting a mapping of region names in OT to zones in HA, allowing
  separate namespaces in both applications. This is especially helpful
  if using one OT instance to update geofences for multiple homes.
- Creating a setting to ignore all location updates, allowing users to
  rely completely on enter and leave events. I have personally always
  used OT integrations with home automation this way and find it the
  most reliable.
- Allowing the OT topic to be overridden in configuration

* Fixing configuration of MQTT topic, related tests

* Tests for Owntracks events_only feature

* Tests for customizing mqtt topic, region mapping

* Fixing _parse and http for owntracks custom topic

* Making tests more thorough and cleaning up lint
2018-01-08 08:16:45 +01:00
Dan Nixon
c53fc94e84 Address missed review comments for Dark Sky weather (#11520)
See #11435
2018-01-07 19:54:27 -08:00
Paulus Schoutsen
bccd880395 Fix canary flaky test (#11519) 2018-01-07 15:05:19 -08:00
Anders Melchiorsen
8267a21bfe Lazy loading of service descriptions (#11479)
* Lazy loading of service descriptions

* Fix tests

* Load YAML in executor

* Return a copy of available services to allow mutations

* Remove lint

* Add zha/services.yaml

* Only cache descriptions for known services

* Remove lint

* Remove description loading during service registration

* Remove description parameter from async_register

* Test async_get_all_descriptions

* Remove lint

* Fix typos from multi-edit

* Remove unused arguments

* Remove unused import os

* Remove unused import os, part 2

* Remove unneeded coroutine decorator

* Only use executor for loading files

* Cleanups suggested in review

* Increase test coverage

* Fix races in existing tests
2018-01-07 14:54:16 -08:00
Dan Nixon
3cbd77f6ac Add Dark Sky weather component (#11435) 2018-01-07 13:59:32 -08:00
tschmidty69
4a6b5ba02b Snips (new) added speech response, parse snips/duration (#11513)
* Snips changed to using <username>:intentName instead of user_IDSTRING__intentName

* added response to snips

* blank line

* added unittests

* houndy

* sdtuff

* more stuff

* Update test_snips.py

* Update snips.py

* Split log tests to avoid dict ordering in py34

* Update test_snips.py

* Update test_snips.py

* still broken

* fixed tests

* fixed tests

* removed fix submitted in another PR
2018-01-07 13:42:08 -08:00
Julius Mittenzwei
efb83dde19 More tolerant KNX component if gateway cant be connected (#11511)
* Issue #11432: Do not stop initializing KNX when tunelling device cant be reached

*  Issue #11432: Mark devices as unavailable if gateway cant be connected
2018-01-07 13:39:14 -08:00
Julio Guerra
c20324793e timer: include the remaining time in the state attributes (#11510)
Add the amount of remaining time before a timer is finished in its state attributes, so that it is received when fetching a timer state.

In my particular case, it allows me to correctly implement a UI for a timer when it is paused and when the UI didn't received the pause state change (which happens when the UI is started after the pause). In this case, it is impossible to show the remaining amount of time before the timer ends.
2018-01-07 13:37:19 -08:00
Julian Kahnert
4496ee5af0 upgrade schiene to 0.20 (#11504) 2018-01-07 14:57:26 +01:00
Christopher Viel
e42c4859c2 Upgrade pywebpush to 1.5.0 (#11497)
This version includes a fix for the serialization errors that occurred
when updating push subscriptions.

Changes since version 1.3.0:
https://github.com/web-push-libs/pywebpush/compare/1.3.0...28d2b55f
2018-01-06 21:08:11 -08:00
Tom Waters
939d1b5ff6 Fix time functions would throw errors in python scripts (#11414)
* Fix time functions would throw errors in python scripts

* Added unit test for time.strptime, change variable name to satisfy lint

* Added docstring for time attribute wrapper method to satisfy lint

* Fixed line too long lint problem
2018-01-07 01:50:55 +01:00
Paulus Schoutsen
5c2cecde70 Clean up Alexa.intent and DialogFlow.intent (#11492)
* Clean up Alexa.intent

* Restructure dialogflow too

* Lint

* Lint
2018-01-07 01:39:32 +01:00
Fabian Affolter
c613585100 Add missing configuration variables (#11390)
* Add missing configuration variables

* Minor changes

* Sync platforms and other minor updates
2018-01-06 21:53:14 +01:00
Jesse Hills
3fbf09e7d9 Add new iGlo component (#11171)
* Add new iGlo component

* Missing comma
Add extra blank lines

* Dont change state in turn_on
Remove unused variables
Update before add

* Fixing some lint issues
2018-01-06 21:52:31 +01:00
Fabian Affolter
b22a26891e Upgrade pysnmp to 4.4.4 (#11485) 2018-01-06 19:54:15 +01:00
abondoe
a0df165011 Add Touchline climate platform (#10547)
* Add toucline platform

* Fix bugs suggested by houndci-bot

* Fix bugs suggested by houndci-bot

* Fix style based on comments and lint

* Remove target temperature

* Fix unexpected EOF

* Fix unexpected EOF

* Fix wrongfully named numberOfDevices variable

* Add target temperature

* Update requirements_all.txt

* Change after review

* Add supported features
* Remove update in constructor
* Set member variables to none in constructor

* Minor changes
2018-01-06 11:23:24 +01:00
Fabian Affolter
f6307a1523 Upgrade yarl to 0.17.0 (#11478) 2018-01-06 09:42:09 +01:00
Paulus Schoutsen
ff9688bb7a Fix vultr tests (#11477) 2018-01-05 16:34:03 -08:00
Fabian Affolter
c714658384 Upgrade alpha_vantage to 1.8.0 (#11476) 2018-01-05 16:05:51 -08:00
Fabian Affolter
f21914d1f3 Upgrade psutil to 5.4.3 (#11468) 2018-01-05 14:30:12 -08:00
hawk259
417c193c0d AlarmDecoder remove icon function as BinarySensorDevice handles it correctly now (#11467)
* remove icon function as BinarySensorDevice handles it correctly now

* removing _type, not used
2018-01-05 14:29:57 -08:00
Christopher Viel
455c629f47 Don't duplicate html5 registrations (#11451)
* Don't duplicate html5 registrations

If a registration is posted and another registration with the same
endpoint URL exists, update that one instead. That way, we preserve
the device name that has been configured. The previous behavior used to
append 'unnamed device' registrations over and over, leading to
multiple copies of the same registration. The endpoint URL is unique per
service worker so it is safe to update matching registrations.

* Refactor html5 registration view to not write json in the event loop
2018-01-05 14:29:27 -08:00
Paulus Schoutsen
01896786fb Merge branch 'master' into dev 2018-01-05 13:10:29 -08:00
Paulus Schoutsen
c526fcd40f Merge pull request #11470 from home-assistant/release-0-60-1
0.60.1
2018-01-05 12:55:13 -08:00
Paulus Schoutsen
8b57777ce9 Alexa to not use customize for entity config (#11461)
* Alexa to not use customize for entity config

* Test Alexa entity config

* Improve tests

* Fix test
2018-01-05 12:33:22 -08:00
Matt N
b61197196e iOS 10 should be served javascript_version:es5 (#11387)
* iOS 10 should be served javascript_version:es5

Fixes #11234

* Update min Safari version to 12
2018-01-05 11:29:20 -08:00
Pascal Vizeli
e0a1b87296 Don't block on sevice call for alexa (#11358)
* Don't block on sevice call for alexa

* fix tests
2018-01-05 11:29:19 -08:00
Greg Laabs
d3ed6d5242 Fix leak sensors always showing Unknown until Wet (#11313)
Leak sensors were using the "wet" node as a negative node, which prevented them from ever gettng a Dry status unless the user pressed the button on the hardware after every Hass reboot.

This change ignores the Wet node, as it is just always the exact inverse of the Dry node. We don't need to watch both.
2018-01-05 11:29:19 -08:00
Bob Anderson
5d6455415e Fix unpredictable entity names in concord232 binary_sensor (#11292) 2018-01-05 11:29:18 -08:00
maxlaverse
a27b37be59 Fix allday events in custom_calendars (#11272) 2018-01-05 11:29:18 -08:00
Zio Tibia
f6a67d99e4 Fix handling zero values for state_on/state_off (#11264) 2018-01-05 11:29:17 -08:00
CTLS
486263fff7 Fix inverted sensors on the concord232 binary sensor component (#11261)
* Fix inverted sensors on the concord232 binary sensor component

* Changed from == Tripped to != Normal
2018-01-05 11:29:17 -08:00
Andrea Campi
5a469f4d4b Support multiple Hue bridges with lights of the same id (#11259)
* Improve support for multiple Hue bridges with lights that have the same id.

The old code pre-refactoring kept a per-bridge list of lights in a closure; my refactoring moved that to hass.data, which is convenient but caused them to conflict with each other.

Fixes #11183

* Update test_hue.py
2018-01-05 11:29:16 -08:00
Pascal Vizeli
889eef78e4 Bugfix homematic available modus (#11256) 2018-01-05 11:29:16 -08:00
Greg Laabs
794cfb7976 Fix detection of if a negative node is in use (#11255)
* Fix detection of if a negative node is in use

Fix a problem where every negative node gets detected as in-use. Code was not checking the correct property.

* Allow protected access
2018-01-05 11:29:15 -08:00
Daniel Watkins
9055922153 Fix webostv select source (#11227)
* Fix reuse of variable name

This should fix #11224.

* Add tests for LgWebOSDevice.select_source
2018-01-05 11:29:15 -08:00
Janne Grunau
2330268842 homematic: add username and password to interface config schema (#11214)
Fixes #11191, the json-rpc name resolving method requires user account
and password.
2018-01-05 11:29:15 -08:00
maxlaverse
e81d17dae0 Fix webdav calendar schema (#11185) 2018-01-05 11:29:14 -08:00
Ben Randall
5820bab9b1 Fix async IO in Sesame lock component. (#11054)
* Call update on Sesame devices to cache initial state

* Switch to using async_add_devices

* Fix line length

* Fix Lint errors

* Fix more Lint errors

* Cache pysesame properties

* Updates from CR feedback
2018-01-05 11:29:14 -08:00
Paulus Schoutsen
3a00c3de64 Version bump to 0.60.1 2018-01-05 11:28:39 -08:00
Matt N
71fb7a6ef6 iOS 10 should be served javascript_version:es5 (#11387)
* iOS 10 should be served javascript_version:es5

Fixes #11234

* Update min Safari version to 12
2018-01-05 11:28:03 -08:00
Daniel Høyer Iversen
65d841c3a6 Update PULL_REQUEST_TEMPLATE.md (#11465)
* Update PULL_REQUEST_TEMPLATE.md

* Add period
2018-01-05 18:31:41 +01:00
William Scanlon
5236b720ab Catch everything when calling to OctoPrint API to fix #10557 (#11457) 2018-01-05 18:07:09 +01:00
Thibault Cohen
ebbdce1f42 Update hydroquebec component to use hass httpsession (#11412)
* Update hydroquebec component to use hass httpsession

* Remove blank line
2018-01-05 10:22:40 +01:00
cdce8p
1490ccf7fb Updated gitignore file (#11452) 2018-01-05 00:03:32 -08:00
cdce8p
642e9c53ba Input Boolean - Deleted 'DEFAULT_INITIAL' (#11453) 2018-01-05 00:03:00 -08:00
cdce8p
1e9e6927eb Input Select - Added service description (#11456) 2018-01-05 00:02:10 -08:00
Paulus Schoutsen
b7c7041873 Add some tests to the cloud component (#11460) 2018-01-04 21:40:18 -08:00
Thijs de Jong
35f35050ff Set tahoma cover scan interval to 60 seconds (#11447)
* Increase tahoma scan interval to 60

* import timedelta

* fix lint
2018-01-05 01:02:31 +01:00
PhracturedBlue
6cb02128b4 Reconnect to alarmdecoder on disconnect (#11383)
* Reconnect to alarmdecoder on disconnect

* Use track_point_in_time instead of call_later

* use alarmdecoder 1.13.2 which has a more robust reconnection fix
2018-01-04 20:10:56 +01:00
Marius
3436676de2 Updated generic thermostat to respect operation_mode and added away mode (#11445)
* Updated generic thermostat to respect operation_mode and added away mode

* Updated tests to include away mode and corrected init problem for sensor state
Added more tests to improve coverage and corrected again lint errors
Fixed new test by moving to correct package
Fixed bug not restoring away mode on restart

* Added support for idle on interface through state

* Added back initial_operation_mode and modified away_temp to be only one for now

* Fixed houndci-bot errors

* Added back check for None on restore temperature

* Fixed failing tests as well

* Removed unused definitions from tests

* Added use case for no initial temperature and no previously saved temperature
2018-01-04 19:05:11 +01:00
Frantz
04de22613c Added new climate component from Daikin (#10983)
* Added Daikin climate component

* Fixed tox & hound

* Place up the REQUIREMENTS var

* Update .coveragerc

* Removed unused customization

* Prevent setting invalid operation state

* Fixed hound

* Small refactor according to code review

* Fixed latest code review comments

* Used host instead of ip_address

* No real change

* No real change

* Fixed lint errors

* More pylint fixes

* Shush Hound

* Applied suggested changes for temperature & humidity settings

* Fixed hound

* Fixed upper case texts

* Fixed hound

* Fixed hound

* Fixed hound

* Removed humidity since even the device has the feature it cant be set from API

* Code review requested changes

* Fixed hound

* Fixed hound

* Trigger update after adding device

* Added Daikin sensors

* Fixed hound

* Fixed hound

* Fixed travis

* Fixed hound

* Fixed hound

* Fixed travis

* Fixed coverage decrease issue

* Do less API calls and fixed Travis failures

* Distributed code from platform to climate and sensor componenets

* Rename sensor state to device_attribute

* Fixed hound

* Updated requirements

* Simplified code

* Implemented requested changes

* Forgot one change

* Don't allow customizing temperature unit and take it from hass (FOR NOW)

* Additional code review changes applied

* Condensed import even more

* Simplify condition check

* Reordered imports

* Disabled autodiscovery FOR NOW :(

* Give more suggestive names to sensors
2018-01-04 11:05:27 +01:00
Paulus Schoutsen
2cf5acdfd2 Google Assistant -> Google Actions (#11442) 2018-01-03 17:26:58 -08:00
Andrey
4512a972d1 Climate: fix missing "|" (#11441)
* Add on/off supported feature to climate

* Lint

* Fix missing |

* lint
2018-01-04 02:26:11 +02:00
cdce8p
65df6f0013 Fix CONF_FRIENDLY_NAME (#11438) 2018-01-03 23:52:36 +01:00
Andrey
eb00e54eba Add on/off supported feature to climate (#11379)
* Add on/off supported feature to climate

* Lint
2018-01-03 23:10:54 +01:00
Steve Easley
ec700c2cd6 Fix errors in zigbee push state (#11386)
* Fix errors in zigbee push state

* Fix formatting

* Added comment
2018-01-03 12:48:36 -08:00
Daniel Claes
75a39352ff fix: hmip-etrv-2 now working with homeassistant (#11175)
* fix: hmip-etrv-2 now working with homeassistant (see also pull request at pyhomematic)

* fix linting issue and typo

* address comment @pvizeli

* Only use cached data in current operation mode

* fix linting issue
2018-01-03 19:36:25 +01:00
Ryan McLean
36c7fbe06a Plex api update (#11423)
* Updated plexapi to 3.0.5

* Removed removed server obj that was being used for the media URL generation as .url() is available in self._session
seasons() no longer exists -> renamed to season()
season() returns obj not list so corrected
season.index returns int, cast to str to zerofill

* Fixed Linting
2018-01-03 19:28:43 +01:00
Dave Finlay
bb2191b2b0 Add support for the renaming of Yamaha Receiver Zones via configuration file. Added a test to cover the change, plus previously untested options. (#11402) 2018-01-03 19:25:16 +01:00
Paulus Schoutsen
f314b6cb6c Cloud Updates (#11404)
* Verify stored keys on startup

* Handle Google Assistant messages

* Fix tests

* Don't verify expiration when getting claims

* Remove email based check

* Lint

* Lint

* Lint
2018-01-03 19:16:59 +01:00
Trevor Joynson
86e1d0f952 Account for User-Agent being non-existent, causing a TypeError (#11064)
* Account for User-Agent being non-existent, causing a TypeError

* Actually fix case of no user-agent with last resort

* Return es5 as last resort

* Update __init__.py

* Update __init__.py
2018-01-02 16:42:41 -08:00
Anders Melchiorsen
02c3ea1917 Silence redundant warnings about slow setup (#11352)
* Downgrade slow domain setup warning

* Revert "Downgrade slow domain setup warning"

This reverts commit 64472c006b.

* Remove warning for entity components

* Remove lint

* Fix original test with extra call
2018-01-02 12:16:32 -08:00
tomaszduda23
0a67195529 Fixing OpenWeatherMap Sensor. Current weather is 'unknown' if forecast: false. It was reported as #8640. (#11417) 2018-01-02 20:54:06 +01:00
Anders Melchiorsen
feb70b47df Performance optimization of TP-Link switch (#11416) 2018-01-02 19:31:33 +01:00
Lukas Barth
909f613324 Do not purge the most recent state for an entity (#11039)
* Protect states that are the most recent states of their entity

* Also protect events

* Some documentation

* Fix SQL
2018-01-01 18:43:10 -08:00
Dan Nixon
f0bf7b0def More support for availability reporting on MQTT components (#11336)
* Abstract MQTT availability from individual components

- Moved availability topic and payloads to MQTT base schema.
- Updated components that already report availability:
  - Switch
  - Binary sensor
  - Cover

* Add availability reporting to additional MQTT components

- Light
- JSON light
- Template light
- Lock
- Fan
- HVAC
- Sensor
- Vacuum
- Alarm control panel

* Annotate MQTT platform coroutines
2018-01-01 18:32:29 -08:00
Marius
541707c3e7 Removed status block to allow https://github.com/home-assistant/home-assistant-polymer/pull/766 with no impact (#11345) 2018-01-01 18:22:27 -08:00
Mitko Masarliev
add89d6973 Notify webos timeout error fix (#11027)
* use smaller icon

* add timeout option

* remove default icon, remove timeout option

* add image again
2018-01-01 18:19:49 -08:00
NotoriousBDG
e2cec9b3ae Move IMAP Email Content body to an attribute (#11096)
* Move IMAP Email Content body to an attribute

* Fix variable names
2018-01-01 18:09:40 -08:00
Paulus Schoutsen
0f914b4c20 Update frontend to 20180102.0 2018-01-01 17:20:27 -08:00
Paulus Schoutsen
1b4be0460c Log exceptions that happen during service call (#11394)
* Log exceptions that happen during service call

* Lint
2018-01-01 15:32:39 -08:00
Greg Dowling
cb899a9465 Bump pywemo to fix request include problems. (#11401) 2018-01-01 15:32:26 -08:00
Otto Winter
b362017206 Upgrade pychromecast to 1.0.3 (#11410) 2018-01-01 14:52:36 -08:00
Paulus Schoutsen
6e63a4ed8a Fix broken tests (#11395)
* Do not leave remember the milk config file behind

* Fix exception in service causing service timeout

* Change max service timeout to 9 to catch services timing out

* Fix Google Sync service test

* Update and pin test requirements
2018-01-01 14:30:09 -08:00
Kane610
b9c852392c Add deCONZ component (#10321)
* Base implementation of component, no sensors yet

* Added senor files

* First fully working chain of sensors and binary sensors going from hardware in to hass

* Clean up

* Clean up

* Added light platform

* Turning lights on and off and set brightness now works

* Pydeconz is now a proper pypi package
Stop sessions when Home Assistant is shutting down
Use a simpler websocket client

* Updated pydocstrings
Followed recommendations from pylint and flake8

* Clean up

* Updated requirements_all.txt

* Updated Codeowners to include deconz.py
Also re-added the Axis component since it had gotten removed

* Bump requirement

* Bumped to v2
Reran script/gen_requirements

* Removed global DECONZ since it wasn't relevant any more

* Username and password is only relevant in the context of getting a API key

* Add support for additional sensors

* Added support for groups

* Moved import of component library to inside of methods

* Moved the need for device id to library

* Bump pydeconz to v5

* Add support for colored lights

* Pylint and flake8 import improvements

* DATA_DECONZ TO DECONZ_DATA

* Add support for transition time

* Add support for flash

* Bump to v7

* ZHASwitch devices will now only generate events by default, instead of being a sensor entity

* Clean up

* Add battery sensor when device signals through an event

* Third-party library communicates with service

* Add support for effect colorloop

* Bump to pydeconz v8

* Same domain everywhere

* Clean up

* Updated requirements_all

* Generated API key will now be stored in a config file

* Change battery sensor to register to callback since library now supports multiple callbacks
Move DeconzEvent to hub
Bump to v9

* Improve entity attributes

* Change end of battery name to battery level
No need for static icon variable when using battery level helper

* Bump requirement to v10

* Improve pydocstring for DeconzEvent
Rename TYPE_AS_EVENT to CONF_TYPE_AS_EVENT

* Allow separate brightness to override RGB brightness

* Expose device.reachable in entity available property

* Bump requirement to 11 (it goes up to 11!)

* Pylint comment

* Binary sensors don't have unit of measurement

* Removed service to generate API key in favor of just generating it as a last resort of no API key is specified in configuration.yaml or deconz.conf

* Replace clear text to attribute definitions

* Use more constants

* Bump requirements to v12

* Color temp requires xy color support

* Only ZHASwitch should be an event

* Bump requirements to v13

* Added effect_list property

* Add attribute to battery sensor to easy find event id

* Bump requirements to v14

* Fix hound comment

* Bumped requirements_all information to v14

* Add service to configure devices on deCONZ

* Add initial support for scenes

* Bump requirements to v15

* Fix review comments

* Python doc string improvement

* Improve setup and error handling during setup

* Changed how to evaluate light features

* Remove 'ghost' events by not triggering updates if the signal originates from a config event
Bump requirement to v17

* Fix pylint issue by moving scene ownership in to groups in requirement pydeconz
Bump requirement to v18

* Added configurator option to register to deCONZ when unlocking gateway through settings
Bump requirement to v20

* Improve async configurator

* No user interaction for deconz.conf

* No file management in event loop

* Improve readability of load platform

* Fewer entity attributes

* Use values() instead of items() for dicts where applicable

* Do one add devices per platform

* Clean up of unused attributes

* Make sure that discovery info is not None

* Only register configure service and shutdown service when deconz has been setup properly

* Move description

* Fix lines longer than 80

* Moved deconz services to a separate file and moved hub to deconz/__init__.py

* Remove option to configure switch as entity

* Moved DeconzEvent to sensor since it is only Switch buttonpress that will be sent as event

* Added support for automatic discovery of deconz
Thanks to Kroimon for adding support to netdisco

* Use markup for configuration description

* Fix coveragerc

* Remove deCONZ support from Hue component

* Improved docstrings and readability

* Remove unnecessary extra name for storing in hass.data, using domain instead

* Improve readability by renaming all async methods
Bump to v21 - improved async naming on methods

* Fix first line not being in imperative mood

* Added logo to configurator
Let deconz.conf be visible since it will be the main config for the component after initial setup

* Removed bridge_type from new unit tests as part of removing deconz support from hue component

* Capitalize first letters of Battery Level

* Properly update state of sensor as well as reachable and battery
Bump dependency to v22

* Fix flake8 Multi-line docstring closing quotes should be on a separate line

* Fix martinhjelmares comments

Bump dependency to v23
Use only HASS aiohttp session
Change when to use 'deconz' or domain or deconz data
Clean up unused logger defines
Remove unnecessary return values
Fix faulty references to component documentation
Move callback registration to after entity has been initialized by HASS
Less inception style on pydocs ;)
Simplify loading platforms by using a for loop
Added voluptous schema for service
Yaml file is for deconz only, no need to have the domain present
Remove domain constraint when creating event title
2018-01-01 17:08:13 +01:00
Jeroen ter Heerdt
976a0fe38c Adding support for Egardia / Woonveilig version GATE-03 (#11397) 2018-01-01 02:10:52 +01:00
Paulus Schoutsen
fc8b25a71f Clean up Google Assistant (#11375)
* Clean up Google Assistant

* Fix tests
2017-12-31 15:04:49 -08:00
Dan Nixon
fcbf7abdaa Reverts unit conversions in TP-Link bulb (#11381)
Reverts energy and power unit conversions added in #10979 as they break
early devices.

A proper fix should be implemented in the pyhs100 library which should
return common units across all devices/firmwares.
2017-12-31 15:58:22 +01:00
ChristianKuehnel
7759ab6919 Remember the Milk - updating and completing tasks (#11069)
* Remember the Milk - updating and completing tasks

Added new feature so that tasks can be updated and completed.
For this feature a task id must be set when creating the task.

* fixed hould complaints

* fixed review comments by @MartinHjelmare

* removed unnecessary check as proposed by @MartinHjelmare
2017-12-29 19:20:36 +01:00
William Scanlon
3fd620198e Support for EcoNet water heaters (#11260)
* Support for EcoNet water heaters.

* Fixed requested changes.

* Added logging when temp or operation mode are None

* More fixes from PR review.

* Updated pyeconet version to fix natural gas water heater error. Last PR review fix.
2017-12-29 19:05:58 +01:00
Pascal Vizeli
49bc95549b Don't block on sevice call for alexa (#11358)
* Don't block on sevice call for alexa

* fix tests
2017-12-29 09:44:06 -08:00
Thibault Cohen
ba0f7a4101 Fido component use now asyncio (#11244)
* Fido component use now asyncio

* Fix comments

* Fix comments 2

* Fix assertion for test error

* Update to pyfido 2.1.0
2017-12-29 18:33:11 +01:00
tschmidty69
4914ad1dd9 Ping device tracker now respects interval_seconds (#11348)
* Ping device tracker now respects interval_seconds
2017-12-29 16:18:39 +01:00
Dan Nixon
f07a4684e0 Fix RGB template ordering in MQTT Light (#11362)
* Use different colour channel intensities in tests

Uses a different value for each colour channel in MQTT light tests to
properly differentiate between colour channels.

* Correct ordering of RGB channels in MQTT light
2017-12-29 15:28:20 +01:00
Paulus Schoutsen
cfd78f7b02 Add HTTP endpoint for resending email confirmation (#11354) 2017-12-29 14:46:10 +01:00
Dan Nixon
2a2e6b6334 Correct units used in TP-Link energy monioring (#10979)
* Correct units used in TP-Link energy monioring

- Energy is measured in kWh for swtches
- Power is reported in mW for bulbs
- Energy is reported in Wh for bulbs

* TP-Ling energy: store units in attribute names

Stores the unit in the attrbute names for TP-Link devices that support
energy monitoring.
2017-12-29 13:13:15 +01:00
Fabian Affolter
b635637541 Upgrade youtube_dl to 2017.12.28 (#11357) 2017-12-29 10:16:18 +01:00
Fabian Affolter
a7ebba6863 Upgrade python-telegram-bot to 9.0.0 (#11341) 2017-12-29 10:08:14 +01:00
Fabian Affolter
1f8acb49bc Upgrade mypy to 0.560 (#11334) 2017-12-29 10:07:25 +01:00
Fabian Affolter
5a4bca9780 Upgrade sqlalchemy to 1.2.0 (#11333) 2017-12-29 10:07:04 +01:00
Fabian Affolter
d7e52d8014 Upgrade pyowm to 2.8.0 (#11332) 2017-12-29 10:06:52 +01:00
Fabian Affolter
391a8901c8 Upgrade fuzzywuzzy to 0.16.0 (#11331) 2017-12-29 10:06:39 +01:00
Fabian Affolter
b98e03b5bc Upgrade aiohttp to 2.3.7 (#11329) 2017-12-29 10:06:25 +01:00
Andy Castille
6b586c268a DoorBird feature update (#11193)
* Allow disabling the DoorBird camera live view

* Support for push notifications from DoorBird devices

* use DoorBirdPy 0.1.1 instead of 0.1.0

* Fix lint errors in DoorBird binary sensor

* Change DoorBird push notifications from binary sensor to event

* Remove DoorBird camera options

* use DoorBirdPy 0.1.2 to fix history image urls

* clean up doorbird event code and remove unused doorbird camera imports

* use async for doorbird doorbell events

* Minor changes

* Update file header

* Fix my mess

* Fix docstring
2017-12-29 10:05:45 +01:00
goldminenine
9a34e7174c Update modbus.py (#11238)
Support of MODBUS RTU over TCP ethernet mode. See more description here:  https://www.eltima.com/modbus-over-ethernet/
2017-12-29 09:19:34 +01:00
Fabian Affolter
3203849b60 Move data instance to setup (#11350) 2017-12-29 09:03:03 +01:00
Thom Troy
2e582a4597 pass stops_at to get_station_by_name (#11304) 2017-12-28 22:37:51 +01:00
Gregory Benner
5d38eec37d Sochain cryptocurrency sensor (#11335)
* add required files

* add sochain sensor

* add missing schema

* end first sentence with a period to make travis happy

* upgrade to python-sochain-api 0.0.2 and use asyncio

* add missing _LOGGER and fix long line

* move object setup to async_setup_platform

* rename chainSo variable to chainso
2017-12-28 21:39:24 +01:00
Dan Nixon
a6c7fe04da Add default names and validation for TP-Link devices (#11346)
Adds missing platform schema for TP-Link smart sockets and adds default
names for smart sockets and bulbs.
2017-12-28 21:22:46 +01:00
Jeroen ter Heerdt
966ab20f26 Remove need for separate EgardiaServer setup (#11344)
* Removing need for separate Egardiaserver setup

* Fixing linting errors

* Updating egardia component based on review

* Updating egardia component based on review

* Updating egardia component based on review

* Removed return False twice based on review
2017-12-28 20:20:44 +01:00
Matt Schmitt
b2e9dc5c8f Additional device classes for binary sensors (#11280)
* Add additional device classes for binary sensor
2017-12-28 18:55:22 +01:00
Lukas Barth
fc3a8e4e79 Merge pull request #10535 from broox/nuheat
Adding a Nuheat Thermostat component
2017-12-28 09:14:27 +01:00
Derek Brooks
00352d41a7 remove return value as requested 2017-12-27 18:20:12 -08:00
Derek Brooks
29c26e0015 fix bad nuheat component test 2017-12-27 13:06:04 -08:00
Derek Brooks
63d9bd4a9c test resume program service 2017-12-27 12:42:56 -08:00
Bob Igo
cb23549af6 closes #11314 by not restricting the voice to anything but a string (#11326) 2017-12-27 19:49:06 +01:00
Rene Nulsch
e5cc5a58e1 Bugfix for HA Issue 7292, 9412 - switch to gamertag to receive ssl image url (#11315) 2017-12-27 09:24:37 +01:00
Greg Laabs
aa8db784d5 Fix leak sensors always showing Unknown until Wet (#11313)
Leak sensors were using the "wet" node as a negative node, which prevented them from ever gettng a Dry status unless the user pressed the button on the hardware after every Hass reboot.

This change ignores the Wet node, as it is just always the exact inverse of the Dry node. We don't need to watch both.
2017-12-27 09:23:21 +01:00
Fabian Affolter
d68d4d1129 Upgrade alpha_vantage to 1.6.0 (#11307) 2017-12-27 09:21:28 +01:00
Fabian Affolter
8d32e883bd Upgrade youtube_dl to 2017.12.23 (#11308) 2017-12-27 09:21:07 +01:00
Fabian Affolter
7826b9aa72 Upgrade python-digitalocean to 1.13.2 (#11311) 2017-12-27 09:20:44 +01:00
Fabian Affolter
e91d47db96 Upgrade distro to 1.2.0 (#11312) 2017-12-27 09:20:08 +01:00
Fabian Affolter
e92e433805 Upgrade yahooweather to 0.10 (#11309) 2017-12-27 09:19:46 +01:00
Fabian Affolter
40e1d35268 Upgrade luftdaten to 0.1.3 (#11316) 2017-12-27 09:19:02 +01:00
Fabian Affolter
af6c39f4d1 Upgrade pysnmp to 4.4.3 (#11317) 2017-12-27 09:17:43 +01:00
Fabian Affolter
5be949f00f Upgrade aiohttp_cors to 0.6.0 (#11310) 2017-12-27 09:17:03 +01:00
Mitko Masarliev
169459b57f Fix for track_new_devices BC (#11202)
* BC fix

* more tests

* inline if change

* inline if change
2017-12-26 14:49:24 -08:00
Conrad Juhl Andersen
05926b1994 xiaomi_aqara: Fix covers never being closed (#11319)
Bug in equality testing
2017-12-26 23:35:48 +01:00
awkwardDuck
54b4142530 Fix typo in bitcoin.py component for mined blocks. (#11318) 2017-12-26 22:02:59 +01:00
Derek Brooks
f0244d7982 add a bit more test coverage 2017-12-26 11:12:28 -08:00
Paulus Schoutsen
7a600ea064 Add heartbeat to websocket connections (#11298) 2017-12-26 10:59:41 +01:00
Greg Laabs
d687bc073e Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware (#11243)
* Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware

# * No more globals - store on hass.data
# * Parent ISY994 component handles categorizing nodes in to Hass components, rather than each individual domain filtering all nodes themselves
# * Remove hidden string, replace with ignore string. Hidden should be done via the customize block; ignore fully prevents the node from getting a Hass entity
# * Removed a few unused methods in the ISYDevice class
# * Cleaned up the hostname parsing
# * Removed broken logic in the fan Program component. It was setting properties that have no setters
# * Added the missing SUPPORTED_FEATURES to the fan component to indicate that it can set speed
# * Added better error handling and a log warning when an ISY994 program entity fails to initialize
# * Cleaned up a few instances of unecessarily complicated logic paths, and other cases of unnecessary logic that is already handled by base classes

* Use `super()` instead of explicit base class calls

* Move `hass` argument to first position

* Use str.format instead of string addition

* Move program structure building and validation to component

Removes the need for a bunch of duplicate exception handling in each individual platform

* Fix climate nodes, fix climate names, add config to disable climate

Sensor platform was crashing when the ISY reported climate nodes. Logic has been fixed. Also added a config option to prevent climate sensors from getting imported from the ISY. Also replace the underscore from climate node names with spaces so they default to friendly names.

* Space missing in error message

* Fix string comparison to use `==`

* Explicitly check for attributes rather than catch AttributeError

Also removes two stray debug lines

* Remove null checks on hass.data, as they are always null at this point
2017-12-26 09:26:37 +01:00
Mike Megally
a59b02b6b4 Removed error log used as debug (#11301)
An error was being logged to display debug info. Removed it
2017-12-25 20:49:02 +01:00
Frantz
14919082a3 Better error handling (#11297)
* Better error handling

* Fixed hound
2017-12-25 17:46:42 +01:00
Bob Anderson
802a95eac5 Fix unpredictable entity names in concord232 binary_sensor (#11292) 2017-12-25 13:26:22 +01:00
Michael Irigoyen
a44181fd35 Add Chime status and control to Alarm Decoder component (#11271)
* Enable more alarm decoder attributes, including chime status and ready status

* Expose chime service in the alarm decoder component

* Fix line length linting issue

* Fix spacing lint issue

* Update PR based on reviewer requests

* Update based on linting catches

* Fix descriptions include from async to sync
2017-12-25 11:34:07 +01:00
PhracturedBlue
b280a791a6 Store raw state of RF sensors from alarmdecoder (#10841)
* Store raw state of RF sensors from alarmdecoder

* Fix resync.  Fix issue with RFID not being truly optional

* Breakdown RF attributes per bit

* Preserve import style
2017-12-25 10:52:33 +01:00
Jordy
7269070d97 Added rainsensor (#11023)
* Added rainsensor

Added rainsensor

* Added to coverage ignore

* Fixed issues

* script\gen_requirements_all.py

script\gen_requirements_all.py

* Gen requirements

* requirements

* requirements

* Fix docstring

* Fix log message

* Revert change
2017-12-25 10:07:17 +01:00
Phil Kates
94ac0b5ed8 alexa: Add handling for covers (#11242)
* alexa: Add handling for covers

Covers don't support either cover.turn_on or homeassistant.turn_on so
use cover.[open|close]_cover.

* alexa: Add tests for covers
2017-12-24 15:05:56 -08:00
Derek Brooks
8ef8dbc868 pleasin the hound 2017-12-24 11:15:50 -07:00
Derek Brooks
7de3c62846 register nuheat_resume_program service 2017-12-24 11:10:22 -07:00
Derek Brooks
fb90dab471 add ability to change the Nuheat thermostat hold mode 2017-12-24 10:09:27 -07:00
Derek Brooks
419ec7f7a7 bump to python-nuheat 0.3.0 2017-12-24 09:43:56 -07:00
Derek Brooks
bdf64ccbbb Merge branch 'dev' into nuheat 2017-12-24 09:40:21 -07:00
Ryan McLean
3fa45375d9 Plex refactor (#11235)
* Cleaned up '_clear_media()'

* Moved media Type to new method

* renamed "clear_media()' to ' clear_media_details()'
reset 'app_name' (Library Name) in clear_media_details
moved thumbs to '_set_media_image()'

* Moved playback info into setmedia type as it was just used for the next anyway

* Moved library name & image download to only happen if session and player active as else no point anyway

* Fixed Linting issue

* Some tweaks to clean up unintended complexity

* Removed redundant declarations

* Fixed whitespace

* Revert "Fixed whitespace"

This reverts commit 0985445c47.

* Revert "Removed redundant declarations"

This reverts commit 6f9d5a85b0.
2017-12-24 11:18:31 -05:00
Aaron Bach
5566ea8c81 Adds support for disabled Tiles and automatic session renewal (#11172)
* Adds support for disabled Tiles and automatic session renewal

* Updated requirements

* Collaborator-requested changes

* Collaborator-requested changes
2017-12-24 01:19:04 +01:00
Andrea Campi
8c303bf48c Support multiple Hue bridges with lights of the same id (#11259)
* Improve support for multiple Hue bridges with lights that have the same id.

The old code pre-refactoring kept a per-bridge list of lights in a closure; my refactoring moved that to hass.data, which is convenient but caused them to conflict with each other.

Fixes #11183

* Update test_hue.py
2017-12-24 01:12:54 +01:00
David Fiel
8683d75aa1 Greenwave Reality (TCP Connected) Lighting Component (#11282)
* Create greenwave.py

* Update .coveragerc

* Update requirements_all.txt

* Update greenwave.py

Line too long

* Update greenwave.py

* Update requirements_all.txt

* Update greenwave.py

* Update greenwave.py

* fix style
2017-12-24 01:11:45 +01:00
Andrey Kupreychik
4f5d7cea11 Added password for GPS logger endpoint (#11245)
* Added password for GPS logger endpoint

* Fixed lint error

* Update gpslogger.py

* fix lint

* fix import
2017-12-24 00:15:06 +01:00
Andrey
ab9ffc4f05 Report Sensibo as off when it is off (#11281) 2017-12-23 11:10:54 +01:00
Paulus Schoutsen
6e2bfcfe65 Update frontend to 20171223.0 2017-12-22 21:31:31 -08:00
Andrey
240098dd7e Change manifest path to /states as this is the path / actually sets. (#11274) 2017-12-22 21:05:15 -08:00
Daniel Watkins
353bb62687 Fix webostv select source (#11227)
* Fix reuse of variable name

This should fix #11224.

* Add tests for LgWebOSDevice.select_source
2017-12-22 18:38:00 +01:00
maxlaverse
46df91ff45 Fix allday events in custom_calendars (#11272) 2017-12-22 14:08:34 +01:00
Ben Randall
295caeb065 Fix async IO in Sesame lock component. (#11054)
* Call update on Sesame devices to cache initial state

* Switch to using async_add_devices

* Fix line length

* Fix Lint errors

* Fix more Lint errors

* Cache pysesame properties

* Updates from CR feedback
2017-12-22 10:28:51 +01:00
Egor Tsinko
eeb309aea1 Functinality to save/restore snapshots for monoprice platform (#10296)
* added functionality to save/restore snapshots to monoprice platform

* renamed monoprice_snapshot, monoprice_restore to snapshot, restore

This is to simplify refactoring of snapshot/restore functionality for monoprice, snapcast and sonos in the future
2017-12-22 10:26:34 +01:00
Pascal Vizeli
9e0a765801 Revert "Backup configuration files before overwriting" (#11269)
* Revert "Adding MotionIP to BinarySensors for HMIP-SMI (#11268)"

This reverts commit c94cc34a8f.

* Revert "Bugfix: 10509 - http is hard coded in plex sensor (#11072)"

This reverts commit 901d4b5489.

* Revert "Fix handling zero values for state_on/state_off (#11264)"

This reverts commit 2e4e3a42cc.

* Revert "Fix inverted sensors on the concord232 binary sensor component (#11261)"

This reverts commit b866687cd7.

* Revert "Proper Steam game names and small fixes (#11182)"

This reverts commit 7faa94046c.

* Revert "Bugfix homematic available modus (#11256)"

This reverts commit 1d579587c1.

* Revert "Fix detection of if a negative node is in use (#11255)"

This reverts commit b28bfad496.

* Revert "added myself to become code owner for miflora and plant (#11251)"

This reverts commit e0682044f0.

* Revert "Add workaround for running tox on Windows platforms (#11188)"

This reverts commit 81f1a65fae.

* Revert "Upgrade to new miflora version 0.2.0 (#11250)"

This reverts commit 8efc4b5ba9.

* Revert "homematic: add username and password to interface config schema (#11214)"

This reverts commit b4e2537de3.

* Revert "Backup configuration files before overwriting (#11216)"

This reverts commit 90e25a6dfb.
2017-12-21 22:33:37 +01:00
schnoetz
c94cc34a8f Adding MotionIP to BinarySensors for HMIP-SMI (#11268)
* Adding MotionIP to BinarySensors for HMIP-SMI

My HmIP-SMI (Homematic IP Motion Sensor) only shows "ILLUMINATION" and no MOTION, because the binary values are not recognized. The "old" homematic-motion detectors are working well showing motion, too. 
I found out that "MotionIP" was missing at the binary_sensors - after adding "Motion" and "Motion Detection Activated" are shown.

* Removed trailing blanks

removed trailing blanks from my previous change
2017-12-21 21:46:42 +01:00
Ryan McLean
901d4b5489 Bugfix: 10509 - http is hard coded in plex sensor (#11072)
* Fix for sensor no ssl

* Line length Fixes

* Removed unneeded schema extensions

* Removed unrequired variable

* Added defaults for SSL & SSL Verify

* Moved Defaults to Variables

Corrected SSL Defaults to match the other Defaults style

* Fixed Typo

* Removed option to disable verify ssl

* Removed unused import

* Removed unused CONST

* Fixed error handling

* Cleanup of unneeded vars & logging

* Fix for linting
2017-12-21 10:24:57 -05:00
Zio Tibia
2e4e3a42cc Fix handling zero values for state_on/state_off (#11264) 2017-12-21 14:24:19 +01:00
CTLS
b866687cd7 Fix inverted sensors on the concord232 binary sensor component (#11261)
* Fix inverted sensors on the concord232 binary sensor component

* Changed from == Tripped to != Normal
2017-12-21 06:29:42 +01:00
Frank Wickström
7faa94046c Proper Steam game names and small fixes (#11182)
* Use constant for offline state

* Use constant for no game name

* Rename trade and play constant their proper names

Trade and Play are not the correct names for the states. For instance
Play might be seens as the user is actually is playing, which is not
correct as there is no such state is returned from the Steam API.
Just having "trade" does not say much about what is happening and
might be misintepreted that the user is currently trading, which is not
correct either.

We instead use the names from the underlying library for naming the
states [1]

[1] 2e518ad84f/steam/user.py (L109)

* Get the proper game name if no extra info is given from the api

The third `current_game` parameter that was used before hold
extra information about the game that is being played. This might
contain the game name, it might also be empty. The correct way to
get the game name is to fetch it from the API depending on the
game id that might be returned in the `current_game` attribute if
the user is playing a game.

To not break existing implementations we keep the functionality
to first go with the extra information and only then fetch the proper
game name.

* Refactor getting game name to its own function

This cleans up the code and removed "ugly" else statements
from the sensor and makes the game fetching easier to read.

* Let state constant values be lower snake case

* Return None instead of 'None' when no current game exists

* Initialize steam app list only once to benefit form caching

* Return None as state attributes if no current game is present
2017-12-20 22:32:33 -05:00
Pascal Vizeli
1d579587c1 Bugfix homematic available modus (#11256) 2017-12-20 23:59:11 +01:00
Greg Laabs
b28bfad496 Fix detection of if a negative node is in use (#11255)
* Fix detection of if a negative node is in use

Fix a problem where every negative node gets detected as in-use. Code was not checking the correct property.

* Allow protected access
2017-12-20 23:58:22 +01:00
ChristianKuehnel
e0682044f0 added myself to become code owner for miflora and plant (#11251) 2017-12-20 12:11:56 +01:00
Ben Randall
81f1a65fae Add workaround for running tox on Windows platforms (#11188)
* Add workaround for running tox on Windows platforms

* Remove install_command override
2017-12-20 11:50:31 +01:00
ChristianKuehnel
8efc4b5ba9 Upgrade to new miflora version 0.2.0 (#11250) 2017-12-20 11:35:03 +01:00
Janne Grunau
b4e2537de3 homematic: add username and password to interface config schema (#11214)
Fixes #11191, the json-rpc name resolving method requires user account
and password.
2017-12-20 00:38:59 +01:00
Charles Garwood
90e25a6dfb Backup configuration files before overwriting (#11216)
* Backup configuration files before overwriting

* Changed timestamp format from epoch to iso8601 (minus punctuation)
2017-12-19 17:55:24 +01:00
Dan Chen
3d90855ca6 Bump python-miio version (#11232) 2017-12-19 08:22:13 +01:00
Dan Nixon
200c927087 Extend Threshold binary sensor to support ranges (#11110)
* Extend Threshold binary sensor to support ranges

- Adds support for ranges
- Threshold type (lower, upper, range) is defined by supplied
  thresholds (lower, upper)
- Adds verbose status/position relative to threshold as attribute
  (position)

* Minor changes (ordering, names, etc.)

* Update name

* Update name
2017-12-19 01:52:19 +01:00
markferry
ef22a6e18d Fix statistics sensor mean and median when only one sample is available. (#11180)
* Fix statistics sensor mean and median when only one sample is available.

With only one data point stddev and variance throw an exception.
This would clear the (valid) mean and median calculations.

Separate the try..catch blocks for one-or-more and two-or-more stats so
that this doesn't happen.

Test this with a new sampling_size_1 test.

* test_statistics trivial whitespace fix
2017-12-18 21:21:27 +01:00
Thibault Maekelbergh
061395d2f8 Add Discogs Sensor platform (#10957)
* Add Discogs Sensor platform

* Add discogs module to requirements_all

* Fix wrong style var name

* PR Feedback (scan interval, mod. docstring)

* Added sensor.discogs to coveragerc

* Use SERVER_SOFTWARE helper for UA-String

* Don't setup platform if token is invalid

* Fix trailing whitespace for Hound CI

* Move client setup to setup()
2017-12-18 19:10:54 +01:00
Khole
05258ea4bf Hive Component Release Two (#11053)
* Add boost functionality to climate devices

* Update boost target temperature rounding

* Update with Colour Bulb Support

* colour bulb fix

* Requirements Update and colorsys import

* Add RGB Attribute - ATTR_RGB_COLOR

* Hive release-2

* add boost support for hive climate platform

* Colour Bulb - Varible update

* Boost - Tox error

* Convert colour to color

* Correct over indentation

* update version to 0.2.9 pyhiveapi

* Updated pyhiveapi to version 2.10 and altertered turn_n on fuction to 1 call

* Update climate doc string

* Update to is_aux_heat_on

* update to is_aux_heat_on
2017-12-18 18:15:41 +01:00
Thibault Cohen
8742ce035a Hydroquebec component use now asyncio (#10795)
* Hydroquebec component use now asyncio

* Add tests

* Improve coverage

* fix tests

* Remove useless try/except and associated tests
2017-12-17 22:11:48 +01:00
maxlaverse
0664bf31a2 Fix webdav calendar schema (#11185) 2017-12-17 20:53:40 +01:00
Fabian Affolter
0ec1ff642d Bump dev to 0.61.0.dev0 2017-12-17 16:29:36 +01:00
Fabian Affolter
cd44824233 Merge branch 'master' into dev 2017-12-17 16:24:52 +01:00
Fabian Affolter
79240a0d47 Merge pull request #11153 from home-assistant/release-0-60
0.60
2017-12-17 15:44:41 +01:00
Brad Dixon
c03d04d826 Revbump to SoCo 0.13 and add support for Night Sound and Speech Enhancement. (#10765)
Sonos Playbar and Playbase devices support Night Sound and Speech Enhancement
effects when playing from sources such as a TV. Adds a new service "sonos_set_option"
whichs accepts boolean options to control these audio features.
2017-12-17 13:09:48 +01:00
Brad Dixon
dfb8b5a3c1 Revbump to SoCo 0.13 and add support for Night Sound and Speech Enhancement. (#10765)
Sonos Playbar and Playbase devices support Night Sound and Speech Enhancement
effects when playing from sources such as a TV. Adds a new service "sonos_set_option"
whichs accepts boolean options to control these audio features.
2017-12-17 13:08:35 +01:00
Mike Megally
432304be82 Remove logging (#11173)
An error was being log that seems more like debug info
2017-12-17 13:07:23 +01:00
PhracturedBlue
294d8171a2 convert alarmdecoder interface from async to sync (#11168)
* convert alarmdecoder interface from async to sync

* Convert he rest of alarmdecoder rom async to sync

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py
2017-12-17 13:07:23 +01:00
Pascal Vizeli
a46ddcf6dd Add install mode to homematic (#11164) 2017-12-17 13:07:23 +01:00
Paulus Schoutsen
5ca006cc9c Don't connect to cloud if subscription expired (#11163)
* Final touch for cloud component

* Fix test
2017-12-17 13:07:23 +01:00
Adam Mills
564ed26aeb Perform logbook filtering on the worker thread (#11161) 2017-12-17 13:07:22 +01:00
Pascal Vizeli
5860267410 Resolve hostnames (#11160) 2017-12-17 13:07:22 +01:00
Pascal Vizeli
ec9638f4d1 Homematic next (#11156)
* Cleanup logic & New gen of HomeMatic

* fix lint

* cleanup

* fix coverage

* cleanup

* name consistenc

* fix lint

* Rename ip

* cleanup wrong property

* fix bug

* handle callback better

* fix lint

* Running now
2017-12-17 13:07:22 +01:00
Matthew Treinish
7db8bbf385 Fix X10 commands for mochad light turn on (#11146)
* Fix X10 commands for mochad light turn on

This commit attempts to address issues that a lot of people are having
with the x10 light component. Originally this was written to use the
xdim (extended dim) X10 command. However, not every X10 dimmer device
supports the xdim command. Additionally, it turns out the number of
dim/brighness levels the X10 device supports is device specific and
there is no way to detect this (given the mostly 1 way nature of X10)

To address these issues, this commit removes the usage of xdim and
instead relies on using the 'on' command and the 'dim' command. This
should work on all x10 light devices. In an attempt to address the
different dim/brightness levels supported by different devices this
commit also adds a new optional config value, 'brightness_levels', to
specify if it's either 32, 64, or 256. By default 32 levels are used
as this is the normal case and what is documented by mochad.

Fixes #8943

* make code more readable

* fix style

* fix lint

* fix tests
2017-12-17 13:07:22 +01:00
Przemek Więch
024f1d4882 Try multiple methods of getting data in asuswrt. (#11140)
* Try multiple methods of getting data in asuswrt.

Solves #11108 and potentially #8112.

* fix style

* fix lint
2017-12-17 12:46:47 +01:00
PhracturedBlue
3375261f51 convert alarmdecoder interface from async to sync (#11168)
* convert alarmdecoder interface from async to sync

* Convert he rest of alarmdecoder rom async to sync

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py

* Update alarmdecoder.py
2017-12-17 00:52:59 +01:00
Matthew Treinish
640d58f0a8 Fix X10 commands for mochad light turn on (#11146)
* Fix X10 commands for mochad light turn on

This commit attempts to address issues that a lot of people are having
with the x10 light component. Originally this was written to use the
xdim (extended dim) X10 command. However, not every X10 dimmer device
supports the xdim command. Additionally, it turns out the number of
dim/brighness levels the X10 device supports is device specific and
there is no way to detect this (given the mostly 1 way nature of X10)

To address these issues, this commit removes the usage of xdim and
instead relies on using the 'on' command and the 'dim' command. This
should work on all x10 light devices. In an attempt to address the
different dim/brightness levels supported by different devices this
commit also adds a new optional config value, 'brightness_levels', to
specify if it's either 32, 64, or 256. By default 32 levels are used
as this is the normal case and what is documented by mochad.

Fixes #8943

* make code more readable

* fix style

* fix lint

* fix tests
2017-12-17 00:52:40 +01:00
Mike Megally
793b8b8ad3 Remove logging (#11173)
An error was being log that seems more like debug info
2017-12-16 22:29:40 +01:00
Pascal Vizeli
39af43eb5c Add install mode to homematic (#11164) 2017-12-16 14:22:23 +01:00
Paulus Schoutsen
b56675a7bb Don't connect to cloud if subscription expired (#11163)
* Final touch for cloud component

* Fix test
2017-12-16 00:42:25 -08:00
Adam Mills
c4d71e934d Perform logbook filtering on the worker thread (#11161) 2017-12-16 00:04:27 -08:00
Paulus Schoutsen
f4d7bbd044 Update frontend 2017-12-15 23:36:04 -08:00
Paulus Schoutsen
3d5d90241f Update frontend 2017-12-15 23:35:37 -08:00
Pascal Vizeli
a7c8e202aa Resolve hostnames (#11160) 2017-12-15 22:54:54 +01:00
Pascal Vizeli
a63658d583 Homematic next (#11156)
* Cleanup logic & New gen of HomeMatic

* fix lint

* cleanup

* fix coverage

* cleanup

* name consistenc

* fix lint

* Rename ip

* cleanup wrong property

* fix bug

* handle callback better

* fix lint

* Running now
2017-12-15 21:22:36 +01:00
Fabian Affolter
ca81180e6d Bump release to 0.60.0 2017-12-15 10:06:06 +01:00
Daniel Perna
de4c8adca2 Upgrade Homematic (#11149)
* Update pyhomematic

* Update pyhomematic
2017-12-15 00:34:27 +01:00
Paulus Schoutsen
823e260c2a Disable html5 notify dependency (#11135) 2017-12-14 00:15:25 -08:00
Greg Laabs
1c8b5838cd ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events

This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds

* Move change event subscription to after entity is added to hass

The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.

* Overhaul binary sensors in ISY994 to be functional "out of the box"

We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.

This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.

* Parse the binary sensor device class from the ISY's device "type"

Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)

* Code review tweaks

The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.

* Handle cases where a sensor's state is unknown

When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.

* Clean up from code review

Fix coroutine await, remove unnecessary exception check, and return None when state is unknown

* Unknown value from PyISY is now -inf rather than -1

* Move heartbeat handling to a separate sensor

Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)

* Add support for Unknown state, which is being added in next PyISY 

PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.

* Change a couple try blocks to explicit None checks

* Bump PyISY to 1.1.0, now that it has been published!

* Remove -inf checking from base component

The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.

* Restrict negative-node and heartbeat support to known compatible types

Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.

* Use new style string formatting

* Add binary sensor detection for pre-5.x firmware

Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-13 20:14:56 -08:00
BryanJacobs
3473ef63af Allow using more than one keyboard remote (#11061)
* Allow using more than one keyboard remote

This sets up one thread per keyboard remote, listening for events.

* Remove enclosing block in keyboard_remote

* Remove unnecessary semantic check for keyboard_remote
2017-12-13 20:07:23 -08:00
Michael Pollett
2cced1dac3 set default utc offset to 0 (#11114) 2017-12-13 20:03:41 -08:00
Adam Mills
b5d3a4736b Add problem device class (#11130) 2017-12-13 20:02:24 -08:00
Andrea Campi
e627544479 Always consume the no_throttle keyword argument. (#11126)
The current code relies on the assumption that the first invocation will never specify no_throttle=True.
However that puts us in a pickle when writing unit tests: if we had a fictitious:

  def setup_platform():
    update()

  @Throttle(MIN_TIME_BETWEEN_SCANS)
  def update():
    pass

Then given multiple tests, the second and some of subsequent tests would be throttled (depending on timing).
But we also can't change that code to call `update(no_throttle=True)' because that's not currently accepted.

This diff shouldn't change the visibile behavior of any component, but allows this extra flexibility.
2017-12-13 20:01:59 -08:00
Andrea Campi
d547345f90 Skip HASS emulated Hue bridges from detection. (#11128)
When refactoring the Hue support we lost a check for HASS bridges; restore that.
2017-12-13 20:00:30 -08:00
Nolan Gilley
4ec3289f9c update pyripple (#11122) 2017-12-13 21:21:14 +01:00
Philipp Schmitt
638dd25aff Add media position properties (#10076)
* Add media progress information

* Remove unnecessary comments

* Remove datetime import

* Remove pysonyavr dependency

* Fix doc syntax (D205)

* Lint fix: no-else-return

* Don't attempt to set media progress info if program is None

* Fix Python 3.4 compatibility

* Explicitely depend on pyteleloisirs

* Only update remaining play time when it changed

* Fix floot state table
2017-12-13 10:58:49 +01:00
Ryan McLean
37efd5a5cd Fixed typo in automation.py (#11116) 2017-12-13 10:17:12 +01:00
Paulus Schoutsen
168065b9bc Update Warrant (#11101)
* Update Warrant

* Lint
2017-12-12 21:12:41 -08:00
Eitan Mosenkis
95cd2035b6 Fix incorrect comment. (#11111) 2017-12-13 00:04:42 +01:00
Dan Nixon
aeba81e193 Report availability for TP-Link smart bulbs (#10976) 2017-12-12 17:18:46 +01:00
Pierre Ståhl
c7e327ea87 Bump pyatv to 0.3.9 (#11104) 2017-12-12 16:52:39 +01:00
Fabian Affolter
ed06b8cead Use luftdaten module (#10970)
* Use luftdaten module

* Refactoring

* Check meta data

* Make name consistent

* Remove try block
2017-12-12 08:09:47 +01:00
Jan Almeroth
a79c7ee217 Bump pymusiccast to version 0.1.6 (#11091) 2017-12-11 22:29:52 +01:00
Pascal Vizeli
6bf23f9167 Update tellcore-net to 0.4 (#11087)
* Update tellcore-net to 0.4

* Update requirements_all.txt
2017-12-11 18:32:48 +01:00
Fabian Affolter
1b3963299d Upgrade shodan to 1.7.7 (#11084) 2017-12-11 16:44:14 +01:00
Fabian Affolter
c461a7c7e2 Upgrade youtube_dl to 2017.12.10 (#11080) 2017-12-11 13:53:01 +01:00
Fabian Affolter
0cfff13be1 Upgrade psutil to 5.4.2 (#11083) 2017-12-11 13:52:43 +01:00
Fabian Affolter
0245189670 Upgrade yarl to 0.16.0 (#11078) 2017-12-11 13:52:22 +01:00
Fabian Affolter
7777d5811f Upgrade aiohttp to 2.3.6 (#11079) 2017-12-11 13:50:55 +01:00
Pascal Vizeli
7259cc878e Allow tradfri to read the available state of the device (#11056)
* Allow tradfri to read the available state of the device

* Update tradfri.py
2017-12-11 11:34:48 +01:00
Erik Eriksson
a4214afddb Volvo on call: Optional use of Scandinavian miles. Also add average fuel consumption property (#11051) 2017-12-10 13:57:44 -08:00
uchagani
b078f6c342 add custom bypass status to total connect (#11042)
* add custom bypass status to total connect

* remove logger line
2017-12-10 16:02:12 -05:00
Andrea Campi
81974885ee Refactor hue to split bridge support from light platform (#10691)
* Introduce a new Hue component that knows how to talk to a Hue bridge, but doesn't actually set up lights.

* Refactor the hue lights platform to use the HueBridge class from the hue component.

* Reimplement support for multiple bridges

* Auto discover bridges.

* Provide some migration support by showing a persistent notification.

* Address most feedback from code review.

* Call load_platform from inside HueBridge.setup passing the bridge id.

Not only this looks nicer, but it also nicely solves additional bridges being added after initial setup (e.g. pairing a second bridge should work now, I believe it required a restart before).

* Add a unit test for hue_activate_scene

* Address feedback from code review.

* After feedback from @andrey-git I was able to find a way to not import phue in tests, yay!

* Inject a mock phue in a couple of places
2017-12-10 10:15:01 -08:00
Adde Lovein
b2c5a9f5fe Add GPS coords to meraki (#10998) 2017-12-10 09:47:14 -08:00
maxlaverse
04cb893d10 Add a caldav calendar component (#10842)
* Add caldav component

* Code review - 1

* Code review - 2

* Sort imports
2017-12-10 17:44:28 +01:00
perfalk
3b228c78c0 Added support for cover in tellstick (#10858)
* Added support for cover in tellstick

* Fixed comments from PR

* Fixed comments from PR

* Address comments
2017-12-10 17:35:10 +01:00
tringler
4e91e6d103 This change fixes the error OSError: [WinError 193] on Windows debuggers (i.e. PyCharm) (#11034) 2017-12-09 16:58:52 -08:00
Paulus Schoutsen
cb4e886a4f Make notify.html5 depend on config (#11052) 2017-12-09 13:14:16 -08:00
GreenTurtwig
bee80c5b79 Add support for Logitech UE Smart Radios. (#10077)
* Add support for Logitech UE Smart Radios.

* Removed full stops to please Hound's line limit.

* Updated with requested changes.

* Fix Pylint Flake8 problem.

* Updated with requested changes.
2017-12-09 20:01:23 +01:00
Andrey Kupreychik
4479761131 Added force_update for REST sensor (#11016)
* Added force_update for REST sensor

* Linting error
2017-12-09 08:18:45 +01:00
Andrey
20f1e1609f In dev mode expose only relevant sources (#11026) 2017-12-08 17:25:16 -08:00
Matthew Treinish
1f1115f631 Serialize mochad requests (#11029)
All mochad devices are sharing a single socket interface. When multiple
threads are issuing requests to the mochad daemon at the same time the
write read cycle might get crossed between the threads. This is normally
not an issue for 1-way X10 devices because as long as the request issued
successfully and data is read over the socket then we know as much as
mochad will tell us (since there is no ACK from the request for most
X10 devices). However, where it does matter is on the device __init__()
because we're relying on the mochad daemon's internal state to take an
educated guess at the device's state to intialize things with. When
there are multiple devices being initialized at the same time the wires
can get crossed between and the wrong device state may be read.

To address this potential issue this commit adds locking using a
semaphore around all pairs of send_cmd() and read_data() (which is what
pymochad.device.Device.get_status() does internally) calls to the mochad
controller to ensure we're only ever dealing with a single request at a
time.

Fixes mtreinish/pymochad#4
2017-12-08 09:18:52 -08:00
Andrey
f7c2ec19ef Change default js version to auto (#10999) 2017-12-08 09:16:26 -08:00
tschmidty69
fed7bd9473 Update snips to listen on new mqtt topic and utilize rawValue (#11020)
* Updated snips to listen on new mqtt topic and use rawValue if value not present in slot

* Too late at night

* Trying to make minor changes via web

* Update test_snips.py

* Update __init__.py

* Updated wrong branch cause I'm a monkey
2017-12-08 09:16:08 -08:00
Anders Melchiorsen
4d6070e33a Support LIFX Mini products (#10996)
* Support new LIFX products

* Remove lint
2017-12-08 08:43:37 -08:00
Anders Melchiorsen
0a7e6ac222 Ignore Sonos players with unknown hostnames (#11013) 2017-12-08 12:01:10 +01:00
Joe Lu
f892c3394b Add support for Canary component and platforms (#10306)
* Add Canary component

* Made some change to how canary data is updated and stored

* Updated to use py-canary:0.1.2

* Addressed flake8 warnings

* Import canary API locally

* Import canary API locally again

* Addressed pylint errors

* Updated requirements_all.txt

* Fixed incorrect unit of measurement for air quality sensor

* Added tests for Canary component and sensors

* Updated canary component to handle exception better when initializing

* Fixed tests

* Fixed tests again

* Addressed review comments

* Fixed houndci error

* Addressed comment about camera force update

* Addressed comment regarding timeout when fetching camera image

* Updated to use py-canary==0.2.2

* Increased update frequency to 30 seconds

* Added support for Canary alarm control panel

* Address review comments

* Fixed houndci error

* Fixed lint errors

* Updated test to only test setup component / platform

* Fixed flake error

* Fixed failing test

* Uptake py-canary:0.2.3

* canary.alarm_control_panel DISARM is now mapped to canary PRIVACY mode

* Fixed failing tests

* Removed unnecessary methods

* Removed polling in canary camera component and update camera info when getting camera image

* Added more tests to cover Canary sensors

* Address review comments

* Addressed review comment in tests

* Fixed pylint errors

* Excluded canary alarm_control_panel and camera from coverage calculation
2017-12-08 10:40:45 +01:00
Marcus Schmidt
929d49ed6f Shuffle support in Sonos (#10875)
* initial commit of shuffle option for sonos

* added test

* Small adjustments to adhere to review requests

* Removed unnessesary setting of variable. Use shuffle state from soco instead
2017-12-07 14:44:06 -05:00
Lewis Juggins
3c1f8cd882 Handle OSError when forcibly turning off media_player.samsungtv (#10997) 2017-12-07 16:30:51 +00:00
Jeroen ter Heerdt
f21da7cfdc Fix Egardia alarm status shown as unknown after restart (#11010) 2017-12-07 12:39:34 +01:00
Alan Fischer
39d33c97ff Added Vera scenes (#10424)
* Added Vera scenes

* Fixed flake8 issues

* Fixed comments

* Moved vera to use hass.data

* Made requested changes
2017-12-07 07:47:19 +01:00
Derek Brooks
3193e825d5 remove nuheat away functionality. :( 2017-12-06 22:34:13 -06:00
Derek Brooks
c262a387dc add supported_features functionality 2017-12-06 22:24:54 -06:00
Derek Brooks
36d5fff8e0 address feedback on log lines 2017-12-06 21:52:44 -06:00
Derek Brooks
72aa722c33 Merge branch 'dev' into nuheat 2017-12-06 21:49:13 -06:00
Richard Leurs
c952f2e18a Ensure Docker script files uses LF line endings to support Docker for Windows. (#10067) 2017-12-06 15:00:58 +01:00
Daniel Watkins
0fc7f37185 webostv: Ensure source exists before use (#10959)
In a case where either (a) an incorrect source name is used, or (b) the
TV isn't currently queryable (e.g. it's off), we get tracebacks because
we assume the source that we are being asked to select exists in
self._source_list.

This makes the lookup code a little more rugged, and adds in a warning
message (in place of the current exception).
2017-12-06 14:48:17 +01:00
Pascal Vizeli
9cff6c7e6a Update tradfri.py (#10991) 2017-12-06 12:44:41 +01:00
Mitko Masarliev
e66268dffe Meraki AP Device tracker (#10971)
* Device tracker for meraki AP

* styles fix

* fix again

* again

* and again :)

* fix hide if away

* docs and optimization

* tests and fixes

* styles

* styles

* styles

* styles

* styles fix. Hope last

* clear track new

* changes

* fix accuracy error and requested changes

* remove meraki from .coveragerc

* tests and minor changes

* remove location
2017-12-06 09:24:20 +01:00
Paulus Schoutsen
c13b510ba3 Update frontend to 20171206.0 2017-12-05 23:40:31 -08:00
Dan Nixon
5f4baa67dc Allow disabling the LEDs on TP-Link smart plugs (#10980) 2017-12-06 08:38:27 +01:00
Paulus Schoutsen
1db7e2c9d6 Merge branch 'master' into dev 2017-12-05 23:36:08 -08:00
Paulus Schoutsen
fa324dce9c Merge pull request #10990 from home-assistant/release-0-59-2
0.59.2
2017-12-05 22:17:27 -08:00
Paulus Schoutsen
56c694b477 Revert pychromecast update (#10989)
* Revert pychromecast update

* Update cast.py
2017-12-05 22:11:23 -08:00
Craig J. Ward
3f764f1981 Reload closest store on api menu request (#10962)
* reload closest store on api request

* revert change from debugging
2017-12-05 22:11:22 -08:00
William Scanlon
63d6734612 Allow chime to work for wink siren/chime (#10961)
* Allow Wink siren/chimes to work

* Updated requirements_all.txt
2017-12-05 22:11:22 -08:00
Erik Eriksson
f9743c29cd Upgrade tellduslive library version (closes https://github.com/home-assistant/home-assistant/issues/10922) (#10950) 2017-12-05 22:11:22 -08:00
Mateusz Drab
bdb7a29586 Fix linksys_ap.py by inheriting DeviceScanner (#10947)
As per issue #8638, the class wasn't inheriting from DeviceScanner, this commit patches it up.
2017-12-05 22:11:21 -08:00
Andrey
22c36f0ad3 Require FF43 for latest js (#10941)
* Require FF43 for latest js

`Array.prototype.includes` added in Firefox 43

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

* Update __init__.py
2017-12-05 22:11:20 -08:00
Paulus Schoutsen
fd6373c7aa Version bump to 0.59.2 2017-12-05 22:10:47 -08:00
Andrey
87fe674c70 Require FF43 for latest js (#10941)
* Require FF43 for latest js

`Array.prototype.includes` added in Firefox 43

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

* Update __init__.py
2017-12-05 22:09:41 -08:00
Paulus Schoutsen
ddec566e10 Revert pychromecast update (#10989)
* Revert pychromecast update

* Update cast.py
2017-12-05 22:08:09 -08:00
William Scanlon
454d8535f8 Allow chime to work for wink siren/chime (#10961)
* Allow Wink siren/chimes to work

* Updated requirements_all.txt
2017-12-05 22:07:59 -08:00
Mitko Masarliev
8e4942088e Add option to set default hide if away for new devices (#10762)
* Option to change hide_if_away

* tests fix

* change new device defaults

* tests and requested changes

* fix assert
2017-12-05 21:56:43 -08:00
Adam Mills
3af527b1b5 Use new build path for dev translations (#10937) 2017-12-05 09:13:09 -05:00
ziotibia81
69d5738e47 Generic thermostat initial_operation_mode (#10690)
* Generic thermostat restore operation mode

* Test restore operation mode

* Fix trailing whitespace

* Fix line too long

* Fix test duplicate entity_id

* Fix test

* async_added_to_hass modify modify internal state

* Test inital_operation_mode

* More restore state tests

* Fix whitespace

* fix test_custom_setup_param

* Test "None" target temp
2017-12-05 15:00:33 +01:00
Menno Blom
379c10985b Add Ziggo Mediabox XL media_player (#10514)
* Add Ziggo Mediabox XL media_player

* Using pypi module ziggo-mediabox-xl now.

* Code review changes
2017-12-05 14:22:27 +01:00
Roman
821cf7135d Gearbest sensor (#10556)
* Added Gearbest Sensor

* Updated required files

* Fixed houndci-bout findings

* Fix tox lint errors

* Changed code according to review
Implemented library version 1.0.5

* Fixed houndci-bot findings

* Fixed tox lint issues

* Updated item schema to use has_at_least_one_key
Added conf constants

* Remove CONF_ constants and import them from homeassistant.const

* Removed CurrencyConverter from hass
Fixed couple of issues found by MartinHjelmare
2017-12-05 12:32:43 +01:00
Craig J. Ward
d986bdab98 Reload closest store on api menu request (#10962)
* reload closest store on api request

* revert change from debugging
2017-12-05 10:47:48 +01:00
Stefan Lehmann
53d9fd18b7 Add ADS component (#10142)
* add ads hub, light and switch

* add binary sensor prototype

* switch: use adsvar for connection

* fix some issues with binary sensor

* fix binary sensor

* fix all platforms

* use latest pyads

* fixed error with multiple binary sensors

* add sensor

* add ads sensor

* clean up after shutdown

* ads component with platforms switch, binary_sensor, light, sensor

add locking

poll sensors at startup

update state of ads switch and light

update ads requirements

remove update() from constructors on ads platforms

omit ads coverage

ads catch read error when polling

* add ads service

* add default settings for use_notify and poll_interval

* fix too long line

* Fix style issues

* no pydocstyle errors

* Send and receive native brightness data to ADS device to prevent issues with math.floor reducing brightness -1 at every switch

* Enable non dimmable lights

* remove setting of self._state in switch

* remove polling

* Revert "remove polling"

This reverts commit 7da420f823.

* add service schema, add links to documentation

* fix naming, cleanup

* re-remove polling

* use async_added_to_hass for setup of callbacks

* fix comment.

* add callbacks for changed values

* use async_add_job for creating device notifications

* set should_poll to False for all platforms

* change should_poll to property

* add service description to services.yaml

* add for brigthness not being None

* put ads component in package

* Remove whitespace

* omit ads package
2017-12-05 09:44:22 +01:00
Mateusz Drab
38a1f06d14 Fix linksys_ap.py by inheriting DeviceScanner (#10947)
As per issue #8638, the class wasn't inheriting from DeviceScanner, this commit patches it up.
2017-12-04 18:58:52 +01:00
Paul Annekov
2e2d0f48fb don't ignore voltage data if sensor data changed (#10925) 2017-12-04 17:26:41 +01:00
Erik Eriksson
4652b8aea1 Upgrade tellduslive library version (closes https://github.com/home-assistant/home-assistant/issues/10922) (#10950) 2017-12-04 17:26:07 +01:00
dasos
ef1cbd3aea Tado ignore invalid devices (#10927)
* Ignore devices without temperatures

* Typo

* Linting

* Removing return false

* Another typo. :(

* Spelling received correctly
2017-12-04 14:55:04 +01:00
drop table USERS; --
31cedf83c7 Export climate status and target temperature to Prometheus (#10919)
* Export climate metrics to Prometheus.

This adds climate_state and temperature_c metrics for each climate
device.

* Add more climate states to state_as_number
2017-12-04 13:39:26 +01:00
Nicolas Bougues
19a97580fc Set percent unit for battery level so that history displays properly; edited variable name for consistency (#10932) 2017-12-04 08:34:42 +01:00
Dan Nixon
17f3cf0389 Report availability of TP-Link smart sockets (#10933)
* Report availability of TP-Link smart sockets

* Changes according to our style guide
2017-12-04 08:33:22 +01:00
Paulus Schoutsen
4e02300cbc Merge remote-tracking branch 'origin/master' into dev 2017-12-03 20:15:34 -08:00
Paulus Schoutsen
015cdd155c Merge pull request #10936 from home-assistant/release-0-59-1
0.59.1
2017-12-03 20:08:04 -08:00
Craig J. Ward
d4e603cc6a Dominos no order fix (#10935)
* check for none

* fix error from no store being set

* typo

* Lint

* fix default as per notes. Lint fix and make closest store None not False

* update default
2017-12-03 19:36:49 -08:00
Paulus Schoutsen
7ae374e11f Update frontend to 20171204.0 (#10934) 2017-12-03 19:36:49 -08:00
Will Boyce
292b403dc3 fix ios component config generation (#10923)
Fixes: #19020
2017-12-03 19:36:49 -08:00
Daniel Perna
b815898ddb Fix Notifications for Android TV (#10798)
* Fixed icon path, added dynamic icon

* Addressing requested changes

* Using DEFAULT_ICON

* Using CONF_ICON from const

* Getting hass_frontend path via import

* Lint

* Using embedded 1px transparent icon

* woof -.-

* Lint
2017-12-03 19:36:48 -08:00
Paulus Schoutsen
b1855f1d1d Version bump to 0.59.1 2017-12-03 19:36:41 -08:00
Craig J. Ward
bd6a17a3a5 Dominos no order fix (#10935)
* check for none

* fix error from no store being set

* typo

* Lint

* fix default as per notes. Lint fix and make closest store None not False

* update default
2017-12-03 19:34:58 -08:00
Paulus Schoutsen
29fad3fa3c Update frontend to 20171204.0 (#10934) 2017-12-03 17:59:58 -08:00
Paulus Schoutsen
0c43466225 Update coveragerc (#10931)
* Sort coveragerc

* Add climate.honeywell and vacuum.xiaomi_miio to coveragerc
2017-12-03 16:42:18 -08:00
Daniel Perna
0d6c95ac44 Fix Notifications for Android TV (#10798)
* Fixed icon path, added dynamic icon

* Addressing requested changes

* Using DEFAULT_ICON

* Using CONF_ICON from const

* Getting hass_frontend path via import

* Lint

* Using embedded 1px transparent icon

* woof -.-

* Lint
2017-12-03 15:08:10 -08:00
Will Boyce
6776e942d7 fix ios component config generation (#10923)
Fixes: #19020
2017-12-03 14:59:22 -08:00
Brent Hughes
879e32f670 Add Min and Event Count Metrics To Prometheus (#10530)
* Added min and Events sensor types to prometheus

* Updated prometheus client and fixed invalid swith state

* Added metric to count number of times an automation is triggered

* Removed assumption that may not apply to everybody

* Fixed tests

* Updated requirements_test_all

* Fixed unit tests
2017-12-03 23:39:54 +01:00
Oliver
3a246df544 Don't repeat getting receiver name on each update / pushed to denonavr 0.5.5 (#10915) 2017-12-03 21:51:32 +01:00
Fabian Affolter
9577525b0b Add Alpha Vantage sensor (#10873)
* Add Alpha Vantage sensor

* Remove data object

* Remove unused vars and change return

* Fix typo
2017-12-03 21:34:59 +01:00
Touliloup
6b410d8076 Correction of Samsung Power OFF behaviour (#10907)
* Correction of Samsung Power OFF behaviour

Addition of a delay after powering OFF a Samsung TV, this avoid status
update from powering the TV back ON.
Deletion of update() return statement, return value not used.

* Rename self._end_of_power_off_command into self._end_of_power_off

* Removal of unused line break in Samsung TV component
2017-12-03 18:34:45 +01:00
Ludovico de Nittis
9e82433a3e Add iAlarm support (#10878)
* Add iAlarm support

* Minor fixes to iAlarm

* Rename ialarmpanel to ialarm and add a check for the host value

* corrections in the value validation of ialarm

* add a missing period on ialarm
2017-12-03 16:48:12 +01:00
Erik Eriksson
8ceaa72ba3 Update eliqonline.py (#10914)
Channel id is now required (change in API)
2017-12-03 16:48:07 +01:00
Fabian Affolter
fce994ea76 Bump dev to 0.60.0.dev0 (#10912) 2017-12-03 16:47:21 +01:00
Nicko van Someren
4390fed168 Unpacking RESTful sensor JSON results into attributes. (#10753)
* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added requirement that RESTful JSON results used as attributes must be
objects, not lists.

* Expanded test coverage to test REFTful JSON attributes with and
without a value template.

* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added requirement that RESTful JSON results used as attributes must be
objects, not lists.

* Expanded test coverage to test REFTful JSON attributes with and
without a value template.

* sensor.envirophat: add missing requirement (#7451)

Adding requirements that is not explicitly pulled in by the library
that manages the Enviro pHAT.

* PyPI Openzwave (#7415)

* Remove default zwave config path

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

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

* Install python-openzwave from PyPI

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

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

* Add python-openzwave deps to .travis.yml

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

Thanks to @MartinHjelmare for this fix.

* Update docker build for PyPI openzwave

Now that PYOZW can be install from PyPI, the docker image build process
can be simplified to remove the explicit compilation of PYOZW.

* Add datadog component (#7158)

* Add datadog component

* Improve test_invalid_config datadog test

* Use assert_setup_component for test setup

* Fix object type for default KNX port

#7429 describes a TypeError that is raised if the port is omitted in the config for the KNX component (integer is required (got type str)). This commit changes the default port from a string to an integer. I expect this will resolve that issue...

* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added requirement that RESTful JSON results used as attributes must be
objects, not lists.

* Expanded test coverage to test REFTful JSON attributes with and
without a value template.

* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added requirement that RESTful JSON results used as attributes must be
objects, not lists.

* Expanded test coverage to test REFTful JSON attributes with and
without a value template.

* Added support for extracting JSON attributes from RESTful values

Setting the json_attributes configuration option to true on the
RESTful sensor will cause the result of the REST request to be parsed
as a JSON string and if successful the resulting dictionary will be
used for the attributes of the sensor.

* Added requirement that RESTful JSON results used as attributes must be
objects, not lists.

* Expanded test coverage to test REFTful JSON attributes with and
without a value template.

* Fixed breaks cause by manual upstream merge.

* Added one extra blank line to make PyLint happy.

* Switched json_attributes to be a list of keys rather than a boolean.

The value of json_attributes can now be either a comma sepaated list
of key names or a YAML list of key names. Only matching keys in a
retuned JSON dictionary will be mapped to sensor attributes.

Updated test cases to handle json_attributes being a list.

Also fixed two minor issues arrising from manual merge with 0.58 master.

* Added an explicit default value to the json_attributes config entry.

* Removed self.update() from __init__() body.

* Expended unit tests for error cases of json_attributes processing.

* Align quotes
2017-12-03 16:30:25 +01:00
Paolo Bonzini
0f8e48c26d More declarative timeout syntax for manual alarm control panel. (#10738)
More declarative timeout syntax for manual alarm control panel
2017-12-03 13:52:31 +01:00
Fabian Affolter
2d556486bf Merge branch 'master' into dev 2017-12-03 13:28:25 +01:00
Fabian Affolter
850a20a626 Merge pull request #10898 from home-assistant/release-0-59
0.59
2017-12-03 13:24:22 +01:00
John Arild Berentsen
68dc0d4d99 Bugfix #10902 (#10904) 2017-12-03 10:09:44 +01:00
PhracturedBlue
58e66c947b Fix issues from review of ecobee weather component (#10903)
* Fix issues from review

* Don't use STATE_UNKNOWN
2017-12-03 10:09:40 +01:00
John Arild Berentsen
29f47d58bc Bugfix #10902 (#10904) 2017-12-03 00:15:57 +01:00
PhracturedBlue
8947052405 Fix issues from review of ecobee weather component (#10903)
* Fix issues from review

* Don't use STATE_UNKNOWN
2017-12-02 22:44:55 +01:00
raymccarthy
475b7896e2 Pybotvac multi (#10843)
* Update requirements_all.txt

* Update neato.py
2017-12-02 15:44:24 +01:00
PhracturedBlue
b2a2cb3fd8 Update ecobee version to fix stack-trace issue (#10894) 2017-12-02 07:56:35 +02:00
Fabian Affolter
462a438f89 Version bump to 0.59.0 2017-12-02 01:09:43 +01:00
Andrey
4ebc52ab52 Reload groups after saving a change via config API (#10877) 2017-12-01 12:53:46 -08:00
Andrey
8afeef2f36 Serve latest extra_html in dev mode (#10863) 2017-12-01 12:53:15 -08:00
Adam Cooper
c2525782aa Refactored WHOIS sensor to resolve assumed key errors (#10662)
* Refactored WHOIS sensor to resolve assumed key errors

Altered it to now set an attribute key and value only if the attribute
is present in the WHOIS response. This prevents assumed keys (registrar)
from raising a KeyError on WHOIS lookups that don't contain registrar
information (onet.pl, wp.pl, for example).

* Removed non-used self._data

* WHOIS sensor now creates a new local attributes dict and overrides

* Corrected typos, refactored error cases to clear state adn attributes

* Resolved double return and refactored error logging
2017-12-01 11:36:15 -05:00
Pascal Vizeli
bc4de4e769 Fix tests (#10891) 2017-12-01 15:49:56 +01:00
Fabian Affolter
9f324205cb Upgrade yarl to 0.15.0 (#10888) 2017-12-01 13:37:14 +01:00
Fabian Affolter
fff85ab392 Upgrade youtube_dl to 2017.11.26 (#10890) 2017-12-01 12:38:46 +01:00
Jeroen ter Heerdt
29f4b73230 Microsoft Text-to-speech: Fixing missing en-gb support bug (#10429)
* Fixing missing en-gb support bug

* Microsoft TTS adding support for rate, volume, pitch and contour.

* Microsoft TTS fixing support for jp-jp.

* Fixing linting error on line 67

* make impossible things possible 🎉
2017-12-01 12:38:20 +01:00
PhracturedBlue
606fa34792 Create ecobee weather platform (#10869)
* Create ecobee weather component

* Update requirements_all for ecobee

* Fix missed lint issue
2017-12-01 12:30:45 +01:00
Teemu R
7b452208b6 Xiaomi Vacuum: remove deprecated calls (#10839)
* vacuum.xiaomi_miio: read dnd status properly instead of using imprecise dnd flag from vacuum_state

* vacuum.xiaomi_miio: use miio package instead of mirobo

* check only that wanted calls have taken place, ignore order of calls

* Fix linting issues

* Remove empty line after docstring
2017-12-01 12:28:59 +01:00
Fabian Affolter
493de295ac Upgrade schiene to 0.19 (#10887) 2017-12-01 12:26:15 +01:00
Fabian Affolter
d2106c40e1 Upgrade fastdotcom to 0.0.3 (#10886) 2017-12-01 12:25:54 +01:00
Fabian Affolter
9a0a5b7867 Upgrade aiohttp to 2.3.5 (#10889) 2017-12-01 12:22:28 +01:00
Paulus Schoutsen
d8003c4d87 Update frontend to 20171130.0 2017-11-30 20:46:21 -08:00
Pascal Vizeli
f7380dc927 tellstick fix DEPENDENCIES and update tellcore-net (#10859)
* Update requirements_all.txt

* Update tellstick.py

* Fix DEPENDENCIES

* Update requirements_all.txt

* fix format

* fix lint

* fix lint

* Update tellstick.py

* update tellcore-net

* update tellcore-net

* besser validate
2017-11-30 21:13:18 +01:00
Pascal Vizeli
ea6ca9252c Bugfix trigger state with multible entities (#10857)
* Bugfix trigger state with multible entities

* Fix numeric state

* fix lint

* fix dict

* fix unsub

* fix logic

* fix name

* fix new logic

* add test for state

* add numeric state test for unsub

* add test for multible entities

* Update numeric_state.py

* Update numeric_state.py

* Update state.py

* Fix logic for triple match

* Add clear to numeric state

* clear for state trigger
2017-11-30 21:03:52 +01:00
Fabian Affolter
bfc61c268a Upgrade distro to 1.1.0 (#10850) 2017-11-30 15:58:50 +01:00
Julius Mittenzwei
1c227bc0d9 Revert "KNX: Added config option for broadcasting current time to KNX bus. (#10654)" (#10874)
This reverts commit cadd797200.

As discussed within #10708 we should chose a different implementation. Therefore we should revert this change to avoid a breaking change.
2017-11-30 15:52:57 +01:00
Aaron Bach
bb870a688d Updated codeowner for Tile device tracker (#10861) 2017-11-29 19:13:31 +01:00
Fabian Affolter
40a98d56fa Upgrade mutagen to 1.39 (#10851) 2017-11-29 11:04:28 +01:00
Lukas Barth
373508693a Climate component: add supported_features (#10658)
* Implement supported_features for the climate component

* Test supported features

* Convert generic thermostat to supported features

* Max / min temperature are not features

* Fix lint

* Min / max humidity are not features

* Linting

* Remove current temperature / humidity

* Move c-hacker-style constants to boring integers. Booo!

* Refactor all the climate platforms to use the new supported_features

* Force all climate platforms to implement supported_features

* Fix mistakes

* Adapt hive platform

* Move flags into a constant

* Calm the hound
2017-11-29 11:01:28 +01:00
Daniel Perna
59fa4f18e4 Upgrade HomeMatic, add devices (#10845) 2017-11-28 23:16:47 -08:00
Per Osbäck
253d5aea6e add support for multiple execution per execute request (#10844) 2017-11-28 23:16:29 -08:00
Andrey
99ea2c17a1 Add useragent-based detection of JS version (#10776)
* Add useragent-based detection of JS version

* Keep es5 as default meanwhile

* Update test
2017-11-28 22:53:12 -08:00
Erik Eriksson
7ab15c0e79 Tellduslive: Use magic constants for battery level. Also, the previous formula for battery level was wrong. (#10788) 2017-11-28 15:32:36 +01:00
Matt Schmitt
4e4d4365a0 Add device class for low battery (#10829) 2017-11-28 15:25:32 +01:00
Cameron Bulock
1f82bb033d Ecobee set humidity level (#10780)
* Add the ability to set humidity levels on ecobee thermostats

* use the latest version of python-ecobee-api

* Lint fixes
2017-11-28 10:39:30 +01:00
Julius Mittenzwei
cadd797200 KNX: Added config option for broadcasting current time to KNX bus. (#10654) 2017-11-27 23:15:57 -08:00
Fredrik Erlandsson
6df5e712f7 Tellduslive update with support for auto config and Local api (#10435)
* Add support for local api connection found in TellStick Znet Lite/Pro.
Added auto discovery support for all TelldusLive devices,
changed authentication method. Breaking change!
Upgraded tellduslive dependency
Update CODEOWNERS.

* Close any open configurator when configuration done

* Add support for Telldus Local API via config (#2)

* Updated dependency (addresses issue raised by @rasmusbe in https://github.com/home-assistant/home-assistant/pull/10435#issuecomment-344719714)

* Fix requested changes
2017-11-27 23:13:30 -08:00
Zach
282e37ef14 Changing handling for google_assistant groups to treat them as lights. (#10111)
* Fixed aliases warning message

* Fixed test cases

* Changing handling for google_assistant groups to treat them as lights - amending to include user info.

* Enable brightness, RGB, etc for groups in Google Assistant

* Revert color/hue/temp settings

* Change servce from light to homeassistant

* Fixed config_units

* Convert from light to switch

* Change group to switch

* Update tests to switch instead of light for group
2017-11-27 21:43:01 -08:00
Odin Ugedal
0668fba7bd Add support for logarithm in templates (#10824)
* Add support for logarithm in templates

This adds a 'log' filter that takes the logarithm of the given value,
with an optional base number. The base defaults to 'e' - the natural
logarithm

* Remove usage of log10 in template filter 'log'

* Add logarithm as a global

This makes it possible to write:
'{{ log(4, 2) }}'
2017-11-27 21:29:01 -08:00
Dan Ferrante
27270b49b4 upgrade somecomfort to 0.5.0 (#10834)
* upgrading somecomfort to 0.5.0

* upgrade somecomfort to 0.5.0 in requirements files
2017-11-27 21:09:04 -08:00
Andrey
8c5d6ee9c3 Fix for Sensibo with missing temperature (#10801)
* Fix for sensibo woth missing temperature

* Use new temperatureUnit API field
2017-11-27 21:05:43 -08:00
chocomega
934c19445d Fixed Yeelight's color temperature conversion to RGB (#10831) 2017-11-27 20:54:56 -08:00
Stephen Yeargin
72251e0375 Fix "recently pair device" (#10832)
Noticed a minor grammar mistake.
2017-11-27 20:54:18 -08:00
Diogo Gomes
b1e2275b47 Add debug (#10828) 2017-11-27 21:25:00 +01:00
Maciej Sokołowski
af1bde6619 Single LEDs in Blinkt support (#10581)
* Single LEDs in Blinkt support

* Review remarks
2017-11-27 21:14:03 +01:00
Per Osbäck
2daea92379 make RGB values consistent as int. fixes #10766 (#10782)
* make RGB consitant as int. fixes #10766

* fix rounding and only change for hex convertion
2017-11-27 11:31:35 +01:00
zhujisheng
6cd9ca018a Add tts.baidu platform (#10724)
* Add tts.baidu platform

* Update baidu.py

* changed to sync

get_engine and get_tts_audio changed to sync.
2017-11-27 10:13:25 +01:00
Rasmus
eb282b3bb3 Added sensor types from telldus server src (#10787)
Added from https://github.com/telldus/tellstick-server/blob/master/telldus/src/telldus/Device.py
2017-11-27 10:11:00 +01:00
Paulus Schoutsen
fe0a9529ed Cloud cognito switch (#10823)
* Allow email based cognito instance

* Fix quitting Home Assistant while reconnecting

* Lint
2017-11-27 10:09:17 +01:00
Paulus Schoutsen
1b7a64412d Bump frontend to 20171127.0 2017-11-26 17:48:11 -08:00
Fabian Affolter
a187bd5455 Add missing docstring (#10812)
* Add missing docstring

* Revert isort change
2017-11-26 21:12:47 +01:00
Paulus Schoutsen
3e962808e6 Bump frontend to 20171126.0 2017-11-25 21:54:51 -08:00
bcl1713
3d5a9b5e91 Add away_mode_name to arlo alarm control panel (#10796)
* Update arlo.py

Include variables for custom away mode specification

* fixed line too long style problem

* fix trailing white space

* fix sending away mode command
2017-11-26 01:13:14 +01:00
Milan V
ba43218a73 Fix WUnderground error handling, rework entity methods (#10295)
* WUnderground sensor error handling and sensor class rework

* WUnderground error handling, avoid long state, tests

* Wunderground - add handling ValueError exception on parsing

* Changes to address review comments - part 1

* Tests lint

* Changes to address review comments - part 2
2017-11-25 15:19:52 -05:00
Andrey
d8bf15a2f5 system_log improvements (#10709)
* system_log improvements

* Don't use ModuleNotFoundError which is 3.6+

* Don't use FrameSummary which was added in 3.5

* Don't trace stack for exception logs

* Handle test error in Python 3.4
2017-11-25 16:22:41 +02:00
Marcelo Moreira de Mello
dbbbe1ceef Load Ring camera only with Ring Protect plan activated (#10739)
* Added ability to only load Ring camera
if the Ring Protect plan is activated.

* Fixed notification for all invalid cameras

* Fixed attribute name

* Using asyncio for persistent notifications
2017-11-25 12:15:12 +01:00
uchagani
2817f03378 Fixes #10773: Demo Alarm Broken (#10777)
* Fixes #10773: Demo Alarm Broken

* Added test for platform setup

* Remove unused import

* Lint fix

* Rework assert to work with python 3.5
2017-11-24 20:30:57 -08:00
Paulus Schoutsen
fcc164c31e Fix scene description formatting. (#10785) 2017-11-24 15:52:59 -08:00
uchagani
65d5b64d8d Bump total-connect-client version (#10769) 2017-11-25 00:21:31 +01:00
Rendili
f6547ec157 Update CODEOWNERS with hive Component / Platforms (#10775) 2017-11-24 16:31:37 +01:00
Nathan Henrie
61cddaa441 Make shell_command async (#10741)
* Make shell_command async

Use `asyncio.subprocess` instead of `subprocess` to make the
`shell_command` component async.

Was able to migrate over existing component and tests without too many
drastic changes.

Retrieving stdout and stderr paves the way for possibly using these in
future feature enhancements.

* Remove trailing comma

* Fix lint errors

* Try to get rid of syntaxerror

* Ignore spurious pylint error
2017-11-23 17:28:31 -08:00
Bart S
b03c024f74 Fix name collision when using multiple Hue bridges (#10486)
* Fix name collision when using multiple Hue bridges

See https://github.com/home-assistant/home-assistant/issues/9393

* Use new style of string formatting

* Removed creating of "All Hue Lights" group
2017-11-23 17:26:36 -08:00
Craig J. Ward
1a7522a594 Add Dominos Pizza platform (#10379)
* add dominos service

* change require

* dump to log

* component fixes

* clean-up use updated library

* remove unnecessary import

* fix hound errors

* more lint fixes

* Coverage rc

* update requirements

* cleanup as per notes

* missing message

* linting...

* schema validation and reducing requests

* fixlint

* spacing

* unused variable

* fix docstrings

* update req

* notes updates, pypi package, front-end panel

* stale import

* fix constant name

* docstrings

* fix library import

* lint fixes

* pylint bug

* remove built-in panel

* Make synchronous

* unused import and use throttle

* Handle exceptions properly and update client

* Import exceptions properly

* unused import

* remove bloat from start-up, readability fixes from notes, retrieve menu on request, not on startup

* whitespace on blank line
2017-11-23 17:21:24 -08:00
Jan Losinski
d0b9f08bf2 InfluxDB send retry after IOError (#10263)
* Implement data write retry for InfluxDB

This adds an optional max_retries parameter to the InfluxDB component
to specify if and how often the component should try to send the data
if the connection failed due to an IOError.

The sending will be scheduled for a retry in 20 seconds as often as the
user specified. This can be handy for flaky getwork connections between
the DB and Homeassistant or outages like daily DSL reconnects.

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

* Add unittest for influx write retries

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

* Add RetryOnError as helper decorator in util

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

* Add unittests for RetryOnError

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

* Use RetryOnError decorator in InfluxDB

This replaces the scheduling logic in the InfluxDB component with the
RetryOnError decorator from homeassistant.util

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

* Make the linters happy

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

* Implement a queue limit for the retry decorator.

This adds a queue limit to the RetryOnError handler. It limits the
number of calls waiting for be retried. If this number is exceeded,
every new call will discard the oldest one in the queue.

* influxdb: Add the retry queue limit option.

* Make the linter happy.

* Make pylint happy

* Log exception of dropped retry

* Move RetryOnError decorator to influxdb component.

* Fix bug in logging usage

* Fix imports

* Add newlines at the end of files.

* Remove blank line

* Remove blank line
2017-11-23 16:58:18 -08:00
Marcelo Moreira de Mello
3dd49b2b95 Protect sensitive information for Amcrest cameras (#10569)
*  Creates a AmcresHub object to protect some private attributes on the logs

* Uses hass.data to pass AmcrestHub to components

* Prefer constants

* Removed serializer since it's using hass.data and simplified camera entity constructor

* small cleanup
2017-11-23 16:38:53 -08:00
braddparker
3ef9c99003 Google assistant climate mode fix (#10726)
* Changed supported climate modes lookup to be case insensitive by forcing to lower-case

* Fixed style errors. (Blank line and line too long)
2017-11-23 12:57:30 -08:00
cgtobi
47183ce02e Temporarily fix yahoo weather API issue and add unit test. (#10737)
* Temporarily fix yahoo weather API issue and add unit test.

* Add test data.
2017-11-23 12:45:56 -08:00
Rendili
f2dea4615f New Hive Component / Platforms (#9804)
* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms

* Changes

* Changes

* Changes

* changes

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Sensor code updates

* Sensor code updates

* Move sensors to binary sensors

* Quack

* Updates - Removed climate related sensors

* sensor fix

* binary_sensor updates

* New Hive Component / Platforms

* New Hive Component / Platforms

* New Hive Component / Platforms
2017-11-23 13:10:23 +01:00
Ted Drain
b4635db5ac Add fan and reduce I/O calls in radiotherm (#10437)
* Added fan support.  Reduced number of calls to the thermostat to a minimum

* Move temp rounding to config schema

* Fixed pep8 errors

* Fix for review comments.

* removed unneeded if block

* Added missing precision attr back

* Fixed pylint errors

* Code review fixes.  Fan support by model number.

* Defined circulate state
2017-11-22 21:59:49 +01:00
Andy Castille
b668b19543 Use new DoorBirdPy (v0.1.0) (#10734)
* Use new DoorBirdPy (v0.1.0)

* Update requirements_all for DoorBirdPy 0.1.0
2017-11-22 11:40:15 +01:00
Lewis Juggins
cfb1853bbd Update pytradfri to 4.1.0 (#10521) 2017-11-22 09:37:20 +00:00
Sven-Hendrik Haase
b784d80973 Add transmission rate (#10740)
* Add transmission rate

* Rename transmission rate attributes to shorter names
2017-11-22 08:39:45 +01:00
Guillaume Rischard
2084ad2164 Optimised images. Saved 80 KB out of 656 KB. 12.3% overall (up to 32.1% per file) (#10735) 2017-11-21 21:19:13 -08:00
Bryan York
9c77f5f5a9 Fix unit conversion for Sensibo A/C units (#10692)
* Fix unit conversion for Sensibo A/C units

When the Sensibo component was released, there was a provision to not convert the temperature units unless "nativeTemperatureUnit" was returned with the API. I'm not sure if the API changed on Sensibo's side, but I do not get this key passed back with API requests.

This causes my current temperature to be returned in CELSIUS instead of FAHRENHEIT.

Removing this fixes it, and I can confirm the units are shown properly now.

* Update adding comment showing temperature is always in C
2017-11-21 20:48:36 +02:00
Otto Winter
8a750eba68 Bump pychromecast to 1.0.2 (#10728)
Fixes home-assistant/home-assistant#9965
2017-11-21 09:04:44 -08:00
bigwoof
5dbd554a10 Adding Queue count sensor (#10723)
Adding another sensor to output the numeber of items in the SABnabd queue.  This is an alternative to displaying filesize, just a preference thing, as it is more meaningfull for me the way I use SABnzdb. 

Note this is my first time coding on github and I have no idea if I am doing things right, I assume that all I needed to do is add a couple of lines to the sensors available and also another line as to what to extract from the SABnzdb API, in this case I have called the sensor "queue_count" and it gets the value from the noofslots_total which as I understand - each slot is a separate download item. 

hope I did this correctly - also I don't have a separate instance of home assistant running for testing so I have no way to test this code (and I don't know how I would switch to the dev channel either).  As I said I am a newb!
2017-11-21 15:35:23 +01:00
Sebastian Muszynski
d0296561f6 python-miio version bumped for improved device support. (#10720) 2017-11-21 09:23:39 +01:00
Paulus Schoutsen
db212cfb00 Fix tests 2017-11-20 22:38:12 -08:00
Paulus Schoutsen
6db5afe597 Update frontend to 20171121.1 2017-11-20 22:00:48 -08:00
Alok Saboo
2ba83655bb Add presence device_class (#10705) 2017-11-20 21:45:00 -08:00
Paulus Schoutsen
6e27e73474 Shopping list: add item HTTP API (#10674)
* Shopping list: add item HTTP API

* Fix order of decorators
2017-11-20 21:44:22 -08:00
Paulus Schoutsen
f0fe8cb2fe Merge branch 'master' into dev 2017-11-20 21:36:30 -08:00
Paulus Schoutsen
3d9f03d4f1 Merge pull request #10716 from home-assistant/release-0-58-1
0.58.1
2017-11-20 21:32:12 -08:00
Egor Tsinko
235707d31c Fix for time_date sensor (#10694)
* fix to time_date sensor

* cleaned up the code and added unit tests

* fixed lint errors
2017-11-20 20:58:28 -08:00
Thibault Cohen
8cb87d5e64 Handle the new version of HydroQuebec website (#10682)
* Handle the new version of HydroQuebec website

* Update requirements_all.txt
2017-11-20 20:58:27 -08:00
Anders Melchiorsen
4cb0e4b3c2 Properly initialize Harmony remote (#10665)
The delay_secs variable was not initialized if discovery was active and no
matching configuration block existed (i.e. override was None).
2017-11-20 20:58:27 -08:00
Lukas Barth
2ba5f1f45e Fix yweather (#10661) 2017-11-20 20:58:26 -08:00
Paulus Schoutsen
d7f9be9640 Version bump to 0.58.1 2017-11-20 20:50:12 -08:00
Paulus Schoutsen
34f06e8eef Bump frontend to 20171121.0 2017-11-20 20:49:58 -08:00
Paulus Schoutsen
efd45549e4 Bump frontend to 20171121.0 2017-11-20 20:48:52 -08:00
Paulus Schoutsen
34a4db57db Fix conversation (#10686)
* Fix conversation

* Lint
2017-11-20 20:26:36 -08:00
uchagani
e62ef067cc Add Arm Custom Bypass to alarm_control_panel (#10697) 2017-11-20 18:34:21 +01:00
Thibault Cohen
df37cb11fa Handle the new version of HydroQuebec website (#10682)
* Handle the new version of HydroQuebec website

* Update requirements_all.txt
2017-11-20 18:02:05 +01:00
Paulus Schoutsen
857d6b5b49 index.html improvements (#10696) 2017-11-20 15:16:36 +01:00
Paulus Schoutsen
62a740ba22 Convert configurator to use markdown (#10668) 2017-11-20 14:11:55 +01:00
Markus Nigbur
a83e741dc7 Refactored to new global json saving and loading (#10677)
* Refactored to new global json saving and loading

* Fixed emulated_hue tests

* Removed unnecassary error handling

* Added missing newline

* Remove unused imports

* Fixed linting error

* Moved _load_json wrapper out of the config class
2017-11-19 19:47:55 -08:00
Egor Tsinko
7695ca2c8b Fix for time_date sensor (#10694)
* fix to time_date sensor

* cleaned up the code and added unit tests

* fixed lint errors
2017-11-19 19:41:30 -08:00
Paulus Schoutsen
3f5c748560 Reorganize lint travis builds (#10670)
* tox cleanup

* 1 tox step

* Revert pytest sugar changes

* Tox: make pylint its own task

* Bump Travis to 30 minutes timeout
2017-11-19 17:39:24 -08:00
PeteBa
fb32cc39e1 Populate measurement state field for HA states like home/not_home (#9833) 2017-11-19 23:49:49 +01:00
Philip Kleimeyer
b548116f9b Tahoma platform for Somfy Covers and Sensors (#10652)
Tahoma platform for Somfy Covers and Sensors
2017-11-19 21:35:13 +01:00
PeteBa
2031b2803f Include unit_of_measurement as InfluxDb field (#9790) 2017-11-19 21:30:47 +01:00
Fabian Affolter
50775ce509 Bump dev to 0.59.0.dev0 (#10675) 2017-11-19 14:37:07 +01:00
Anders Melchiorsen
709df1e844 Properly initialize Harmony remote (#10665)
The delay_secs variable was not initialized if discovery was active and no
matching configuration block existed (i.e. override was None).
2017-11-19 05:20:31 +01:00
Giel Janssens
09d826edf4 Netatmo httperror403 fix (#10659)
* Update lnetatmo

* updated zip

* updated zip
2017-11-18 14:36:01 -08:00
Lukas Barth
086f64b06c Fix yweather (#10661) 2017-11-18 14:33:18 -08:00
frittes
6ad62a2ccb Added cycles config option to LaMetric notifications (#10656)
* Added cycles config option to lametric.py

Added cycles config option, changed display_time to lifetime, code cleanup

* Update lametric.py

* Update lametric.py
2017-11-18 21:12:16 +01:00
Charles Garwood
92fe9aadc8 Change some warnings to info (#10386) 2017-11-18 20:04:09 +01:00
Derek Brooks
a9feafd571 add nuheat to coverage reports 2017-11-18 10:26:36 -06:00
Paulus Schoutsen
d9a6d9ee73 Merge pull request #10630 from home-assistant/release-0-58
0.58
2017-11-17 22:27:35 -08:00
Robbie Trencheny
425c027085 Implement entity and domain exclude/include for Alexa (#10647)
* Implement entity and domain exclude/include for Alexa

* Switch to using generate_filter

* Use proper domain for turn on/off calls except for groups where we must use the generic homeassistant.turn_on/off

* travis fixes

* Untangle

* Lint
2017-11-17 22:00:11 -08:00
Pierre Ståhl
35699273da Bump pyatv to 0.3.8 (#10643)
Fixes AirPlay issues on newer versions of tvOS.
2017-11-17 22:00:11 -08:00
Andrey
b86110a15d Print entity type in "too slow" warnings (#10641)
* Update entity.py

* Update entity.py
2017-11-17 22:00:10 -08:00
Robbie Trencheny
e449ceeeff Alexa improvements (#10632)
* Initial scene support

* Initial fan support

* ordering

* Initial lock support

* Scenes cant be deactivated; Correct the scene display category

* Initial input_boolean support

* Support customization of Alexa discovered entities

* Initial media player support

* Add input_boolean to tests

* Add play/pause/stop/next/previous to media player

* Add missing functions and pylint

* Set manufacturerName to Home Assistant since the value is displayed in app

* Add scene test

* Add fan tests

* Add lock test

* Fix volume logic

* Add volume tests

* settup -> setup

* Remove unused variable

* Set required scene description as per docs

* Allow setting scene category (ACTIVITY_TRIGGER/SCENE_TRIGGER)

* Add alert, automation and group support/tests

* Change display categories to match docs

* simplify down the display category props into a single prop which can be used on any entity

* Fix tests to expect proper display categories

* Add cover support

* sort things

* Use generic homeassistant domain for turn on/off
2017-11-17 22:00:10 -08:00
Cezar Sá Espinola
bf8e2bd77e Make MQTT reconnection logic more resilient and fix race condition (#10133) 2017-11-17 22:00:10 -08:00
Aaron Bach
0202e966ea Fixes AirVisual bug regarding incorrect location data (#10054)
* Fixes AirVisual bug regarding incorrect location data

* Owner-requested changes
2017-11-17 22:00:09 -08:00
Derek Brooks
e1d1cf76ca Add Facebook Notification tests (#10642)
* test the facebook notification component

* respond to hound feedback

* remove unnecessary line breaks

* parse_qs not needed with requests_mock

* remove facebook notifier from .coveragerc
2017-11-17 21:12:36 -08:00
Robbie Trencheny
1317297191 Implement entity and domain exclude/include for Alexa (#10647)
* Implement entity and domain exclude/include for Alexa

* Switch to using generate_filter

* Use proper domain for turn on/off calls except for groups where we must use the generic homeassistant.turn_on/off

* travis fixes

* Untangle

* Lint
2017-11-17 21:10:24 -08:00
Paulus Schoutsen
b3d66e5881 Update frontend to 20171118.0 2017-11-17 19:09:47 -08:00
Pierre Ståhl
64a393b377 Bump pyatv to 0.3.8 (#10643)
Fixes AirPlay issues on newer versions of tvOS.
2017-11-18 00:00:15 +01:00
Aaron Bach
3ad64b0a66 Fixes AirVisual bug regarding incorrect location data (#10054)
* Fixes AirVisual bug regarding incorrect location data

* Owner-requested changes
2017-11-17 21:11:05 +01:00
William Scanlon
2664ca498e Support for Unifi direct access device tracker (No unifi controller software) (#10097) 2017-11-17 14:47:40 -05:00
Giel Janssens
5b44e83c0f Update lnetatmo (#10631) 2017-11-17 10:31:08 -08:00
Cezar Sá Espinola
b8b4e32758 Make MQTT reconnection logic more resilient and fix race condition (#10133) 2017-11-17 10:29:23 -08:00
Robbie Trencheny
2b60fca08d Alexa improvements (#10632)
* Initial scene support

* Initial fan support

* ordering

* Initial lock support

* Scenes cant be deactivated; Correct the scene display category

* Initial input_boolean support

* Support customization of Alexa discovered entities

* Initial media player support

* Add input_boolean to tests

* Add play/pause/stop/next/previous to media player

* Add missing functions and pylint

* Set manufacturerName to Home Assistant since the value is displayed in app

* Add scene test

* Add fan tests

* Add lock test

* Fix volume logic

* Add volume tests

* settup -> setup

* Remove unused variable

* Set required scene description as per docs

* Allow setting scene category (ACTIVITY_TRIGGER/SCENE_TRIGGER)

* Add alert, automation and group support/tests

* Change display categories to match docs

* simplify down the display category props into a single prop which can be used on any entity

* Fix tests to expect proper display categories

* Add cover support

* sort things

* Use generic homeassistant domain for turn on/off
2017-11-17 09:14:22 -08:00
Andrey
f43092c563 Print entity type in "too slow" warnings (#10641)
* Update entity.py

* Update entity.py
2017-11-17 17:36:18 +01:00
Lukas Barth
68d2076b56 Restore target temperature for generic thermostat (#10635)
* Restore target temp for generic thermostat

* Fix lint
2017-11-17 17:32:58 +01:00
cgtobi
be5f0fb3ac Add hddtemp sensor device even if unreachable. (#10623)
* Add hddtemp sensor device even if unreachable.

* Removed old commented code.

* Move unit detection logic into update.
2017-11-17 09:21:27 -05:00
Milan V
e9b691173a Change generic thermostat - any toggle device as heater switch (#10597)
* Change generic thermostat - any toggle device as heater

* Heater switch state method

* Tests

* Debug log, lint

* Debug code remove, cleanup

* Change generic thermostat to control heating on mode change Off -> Auto

* Fix typo

* Review fixes, tests

* Merge and fix tests
2017-11-17 12:47:54 +01:00
Alok Saboo
2a77883146 Added unit_of_measurement to Currencylayer (#10598)
* Added unit_of_measurement to Currencylayer

* Updated based on comments

* Remove quote from name
2017-11-17 08:58:46 +01:00
Paulus Schoutsen
eb8a8f6d0b Version bump to 0.58.0 2017-11-16 22:10:40 -08:00
Paulus Schoutsen
62c8843956 Update frontend to 20171117.1 2017-11-16 22:08:19 -08:00
John Arild Berentsen
1bb37aff0c Add loglinefetch for frontend API call (#10579)
* Add loglinefetch for frontend API call

* Too many blank lines

* Review changes

* review changes

* Only return a text

* Use aiohttp

* Don't do I/O in event loop

* Move lines to query and default to 0

* Small fixes
2017-11-16 22:07:08 -08:00
Egor Tsinko
f052a0926b Added sorted() to python_script (#10621)
* added sorted() to python_script

* fixed lint errors
2017-11-16 22:06:02 -08:00
Anders Melchiorsen
24aeea5ca3 Adjust logging in downloader component (#10622) 2017-11-16 22:05:08 -08:00
Paulus Schoutsen
5c20cc32b5 Update frontend to 20171117.0 2017-11-16 22:03:31 -08:00
Corey Pauley
6cf2e758a8 Alexa slot synonym fix (#10614)
* Added logic to the alexa component for handling slot synonyms

* Moved note with long url to the top of the file

* Just made a tiny url instead of messing with Flake8

* Refactored to be more Pythonic

* Put trailing comma back
2017-11-16 21:09:00 -08:00
Adam Mills
aa6b37912a Fix async missing decorators (#10628) 2017-11-16 21:03:05 -08:00
Jan Losinski
693d32fa68 Snapcast: bump version and enable reconnect. (#10626)
This bumps the used snapcast version to 2.0.8 and enables the new
reconnect feature that causes the component to reconnect to a server if
the connection was lost.

This fixes the ned to restart Home Assstant after a snapcast reboot, as
described in issue #10264.

Signed-off-by: Jan Losinski <losinski@wh2.tu-dresden.de>
2017-11-16 20:32:26 -05:00
Andrey
072ed7ea13 Allow to pass YandexTTS options via sevice call (#10578) 2017-11-16 09:10:25 -08:00
Mitko Masarliev
bd5a16d70b update hbmqtt to 0.9.1 (#10611) 2017-11-16 07:47:37 -08:00
Milan V
eb7643e163 Improve WUnderground config validation (#10573)
* Fix WUnderground config validation

* Fix indentation
2017-11-16 10:26:23 -05:00
Milan V
79ca93f892 Change generic thermostat to control heating on mode change Off -> Auto (#10601)
* Change generic thermostat to control heating on mode change Off -> Auto

* Fix typo
2017-11-16 13:11:46 +01:00
Colin Dunn
3dbae5ca5b Correct input_datetime initial value parsing (#10417)
* Correct input_datetime initial value parsing

* Correct input_datetime initial value parsing
2017-11-15 23:16:22 -08:00
Pascal Vizeli
1719fa7008 Cleanup old stale restore feature (#10593)
* Cleanup old stale restore feature

* cleanup

* Update __init__.py

* Update test_demo.py

* Lint
2017-11-15 23:03:41 -08:00
Per Osbäck
d4bd4c114b add support for color temperature and color to Google Assistant (#10039)
* add support for color temperature and color; also add some extra deviceInfo attributes

* change so that default behaviour doesn't turn off device if the action isn't handled

* add tests

* fix lint

* more lint

* use attributes were applicable

* removed debug logging

* fix unassigned if only None returned

* report more data in QUERY

* better tests for color and temperature

* fixes after dev merge

* remove deviceInfo as not part of a device state (PR #10399)

* fix after merge
2017-11-15 23:00:43 -08:00
boltgolt
f494c32866 Small fix to be able to use mac and vendor in "device_tracker_new_device" event. (#10537)
* Small fix to be able to use mac and vendor in EVENT_NEW_DEVICE event

* Missed device_tracker test
2017-11-15 22:41:39 -08:00
Fabian Affolter
e20fd3b973 Upgrade mypy to 0.550 (#10591) 2017-11-15 22:35:18 -08:00
ziotibia81
270846c2f5 Modbus switch register support (#10563)
* Update modbus.py

* Fix blank linea and whitespaces

* Fix visual indent

* Fix visual indent

* fix multiple statements on one line

* Typo

* Disable pylint check

# pylint: disable=super-init-not-called

* Fix code style
2017-11-15 22:17:10 -08:00
Fabrizio Furnari
b2ab4443a7 New sensor viaggiatreno. (#10522)
* New sensor viaggiatreno.

I've messed up the previous PR so here it is in a new one.
Should include also all corrections from @pvizeli

* fixes from PR 10522

* fixed import order

* requested changes from MartinHjelmare
2017-11-15 22:07:16 -08:00
Craig J. Ward
17cd64966d bump client version (#10610) 2017-11-15 22:04:26 -08:00
Michael Chang
48181a9388 Support script execution for Alexa (#10517)
* Support script execution for Alexa

* Use PowerController for the script component
2017-11-15 21:44:27 -08:00
Andrey
d5cba0b716 Allow unicode when dumping yaml (#10607) 2017-11-15 18:24:08 -08:00
Alok Saboo
3a0c749a12 Fix Hikvision (motion) switch bug (#10608)
* Fix Hikvision switch bug

* Added comment about last working version
2017-11-16 01:15:45 +01:00
ziotibia81
d652d793f3 Fix ValueError exception (#10596)
* Fix ValueError exception

structure = '>{:c}'.format(data_types[register.get(CONF_DATA_TYPE)][register.get(CONF_COUNT)])
give:
ValueError: Unknown format code 'c' for object of type 'str'

* Minor typo
2017-11-15 18:17:17 -05:00
Pierre Ståhl
87995ad62c Do not add panel from system_log (#10600)
The frontend will not have this panel.
2017-11-15 23:45:08 +01:00
Jeremy Williams
c2d0c8fba4 Arlo - Fixes for updated library (#9892)
* Reduce update calls to API. Add signal strength monitor.

* Fix lint errors

* Fix indent

* Update pyarlo version and review fixes

* Fix lint errors

* Remove staticmethod

* Clean up attributes

* Update arlo.py
2017-11-15 23:33:50 +01:00
On Freund
c7b0f25eae Fix Yahoo Weather icons over SSL (#10602) 2017-11-15 22:27:26 +02:00
Fabian Affolter
d5b170f761 Upgrade youtube_dl to 2017.11.15 (#10592) 2017-11-15 12:41:25 +01:00
Paulus Schoutsen
ea7ffff0ca Cloud updates (#10567)
* Update cloud

* Fix tests

* Lint
2017-11-15 08:16:19 +01:00
Paulus Schoutsen
0cd3271dfa Update frontend to 20171115.0 2017-11-14 22:48:31 -08:00
Paulus Schoutsen
7920ddda9d Add panel build type (#10589) 2017-11-14 22:39:06 -08:00
NovapaX
1e493dcb8a Tradfri unique identities (#10414)
* Unique identity
Use unique ID for generating keys and store them in config. Fallback to
old id so existing installs will still work.

* Remove Timeouts
they don't really work. this should be fixed in pytradfri I think.

* import uuid only when necessary

* more selective import

* lint

* use load_json and save_json from util.json

* remove unnecessary imports

* use async configurator functions

* async configurator calls

* thou shalt not mixup the (a)syncs

* again: no asyncs in the syncs!
last warning...

* Update tradfri.py
2017-11-14 22:16:21 -08:00
Pierre Ståhl
8111e3944c Add basic backend support for a system log (#10492)
Everything logged with "warning" or "error" is stored and exposed via
the HTTP API, that can be used by the frontend.
2017-11-14 20:35:56 -08:00
NovapaX
8d91de877a turn service call handler into coroutine (#10576) 2017-11-14 20:32:48 -08:00
Eitan Mosenkis
0b4de54725 Google Assistant for climate entities: Support QUERY and respect system-wide unit_system setting. (#10346) 2017-11-14 20:19:42 -08:00
marthoc
309e493e76 Add code to enable discovery for mqtt cover (#10580)
* Add code to enable discovery for mqtt cover

* Fix pylint error
2017-11-14 20:19:15 -08:00
Marcelo Moreira de Mello
95c831d5bc Bump ring_doorbell to 0.1.7 (#10566) 2017-11-14 15:56:42 +01:00
Andreas Björshammar
061253fded Verisure: Added option to set installation giid (#10504)
* Added option to set installation giid

* Changed where giid config var is being checked

* Style fix

* Fix style
2017-11-14 15:53:26 +01:00
Eugenio Panadero
e947e6a143 Use a template for the Universal media player state (#10395)
* Implementation of `state_template` for the Universal media_player

* add tracking to entities in state template

* use normal config_validation

* fix tests, use defaults in platform schema, remove extra keys

* and test the new option `state_template`

* lint fixes

* no need to check attributes against None

* use `async_added_to_hass` and call `async_track_state_change` from `hass.helpers`
2017-11-14 11:41:19 +01:00
Martin Hjelmare
dc6e50c39d Fix lametric sound (#10562)
* Fix sound for lametric notify

* Remove not used method
2017-11-14 10:40:44 +01:00
Abílio Costa
637b058a7e webostv: Reduce default timeout to prevent log spamming (#10564)
With the default timeout of 10 seconds, the log gets filled up with "component is taking more than 10 seconds" errors.
This should probably be fixed in some other way, but for now this reduces the problem a bit.
2017-11-14 10:37:52 +01:00
Fabian Affolter
d25f676711 Move temperature display helper from components to helpers (#10555) 2017-11-14 10:36:18 +01:00
Steve Edson
b1afed9e52 pad packets to multiple of 4 characters (#10560)
* pad packets to multiple of 4 characters

This fixes sending commands, see #7669

* Update broadlink.py

* removed whitespace
2017-11-14 09:18:06 +01:00
Kenny Millington
7c24d77031 Don't use the 'id' field since it can be autogenerated (fixes #10551). (#10554) 2017-11-13 22:46:26 -08:00
Derek Brooks
a3c6211c04 python 3.5 seems to not like assert_called_once() 2017-11-13 23:20:16 -06:00
Derek Brooks
afcb0b8767 fix up some docstrings 2017-11-13 23:13:04 -06:00
Derek Brooks
c0c439c549 that int() casting was redundant 2017-11-13 22:45:29 -06:00
Derek Brooks
959f6386b4 shorten that long line 2017-11-13 22:43:11 -06:00
Derek Brooks
f1fe8e95ba clean up a couple away temperature settings 2017-11-13 22:40:18 -06:00
ziotibia81
e33451e2b9 Better support for int types (#10409)
* Better int types support

* type

* Added optional register order

* Fix white spaces

* Fix line length

* Fix line too long

* Fix trailing whitespace

* Stylistc code fixes
2017-11-13 23:27:15 +01:00
Ari Lotter
2dcde12d38 Support presence detection using Hitron Coda router (#9682)
* Support presence detection using Hitron Coda router

* at least 2 spaces before inline comment

* Update hitron_coda.py

* rewrote authentication code, it actually works now

* make line slightly shorter to comply with hound

* Removed hardcoded IP address

* Fix string formatting, add timeout, and use generator

* Update hitron_coda.py

* Update hitron_coda.py

* Update hitron_coda.py

* typo

* update .coveragerc

* Update stale URL
2017-11-13 23:10:39 +01:00
Martin Hjelmare
3c135deec8 Fix and clean lametric (#10391)
* Fix and clean lametric

* Add missing DEPENDENCIES in notify platform.
* Remove not needed method in component manager class.
* Don't overwrite notify DOMAIN.
* Return consistently depending on found devices in setup of component.

* Get new token if token expired

* Add debug log for getting new token

* Clean up
2017-11-13 21:12:15 +01:00
Derek Brooks
6892033556 remove that unused constant 2017-11-13 12:31:15 -06:00
Derek Brooks
766893253a make sure is_away_mode_on supports user-defined minimum away temps 2017-11-13 12:28:09 -06:00
Derek Brooks
ef5edb95ba Update home/auto hold mode to be consistent with current documentation 2017-11-13 11:38:08 -06:00
Fabian Affolter
6974f2366d Upgrade pysnmp to 4.4.2 (#10539) 2017-11-13 09:24:07 -08:00
Ruslan Sayfutdinov
a6d9c7a621 webostv: set current source correctly (#10548) 2017-11-13 09:23:42 -08:00
Anders Melchiorsen
46fe9ed200 Optimize concurrent access to media player image cache (#10345)
We now do locking to ensure that an image is only downloaded and added
once, even when requested by multiple media players at the same time.
2017-11-13 09:03:12 -08:00
Derek Brooks
f21b9988e9 allow for the configuring of a minimum away temperature 2017-11-13 11:00:33 -06:00
Derek Brooks
7859b76429 kill target_temperature_low and high. They don't make sense here 2017-11-13 10:35:48 -06:00
r4nd0mbr1ck
f6d511ac1a Google Assistant request sync service (#10165)
* Initial commit for request_sync functionality

* Fixes for Tox results

* Fixed all tox issues and tested locally with GA

* Review comments - api_key, conditional read descriptions

* Add test for service
2017-11-13 08:32:23 -08:00
Diogo Gomes
bc23799c71 Change to device state attributes (#10536)
* Following the suggestion of @MartinHjelmare
2017-11-12 15:25:44 +01:00
Per Osbäck
59e943b3c1 notify.html5: use new json save and load functions (#10416)
* update to use new save_json and load_json

* it is no longer possible to determine if the json file contains valid or empty data.

* fix lint
2017-11-11 15:57:11 -08:00
Paulus Schoutsen
c8648fbfb8 Pre-construct frontend index.html (#10520)
* Pre-construct frontend index.html

* Cache templates

* Update frontend to 20171111.0

* Fix iframe panel test
2017-11-11 15:22:05 -08:00
Vignesh Venkat
96e7944fa8 telegram_bot: Support for sending videos (#10470)
* telegram_bot: Support for sending videos

Telegram python library has a sendVideo function that can be used
similar to sending photos and documents.

* fix lint issue

* fix grammar
2017-11-12 00:13:35 +01:00
Derek Brooks
2c44e4fb12 address initial houndbot suggestions 2017-11-11 16:47:12 -06:00
Derek Brooks
9b373901fa add documentation links 2017-11-11 16:31:35 -06:00
Derek Brooks
37be81c20c add ability to resume program... and add in a forgotten test 2017-11-11 16:21:14 -06:00
Aaron Bach
79001fc361 Adds support for Tile® Bluetooth trackers (#10478)
* Initial work in place

* Added new attributes + client UUID storage

* Wrapped up

* Collaborator-requested changes
2017-11-11 23:21:03 +01:00
Paulus Schoutsen
2310b791f9 Merge branch 'master' into dev 2017-11-11 13:04:55 -08:00
Paulus Schoutsen
d814d40330 Merge pull request #10534 from home-assistant/release-0-57-3
0.57.3
2017-11-11 13:00:19 -08:00
William Scanlon
b6e098d1c2 Fixed Wink Quirky Aros bugs. (#10533)
* Fixed Wink Quirky Aros bugs.
2017-11-11 15:49:20 -05:00
Martin Berg
db56748d88 Add attribute to show who last un/set alarm (SPC) (#9906)
* Add attribute to show who last un/set alarm.

This allows showing the name of the SPC user who last
issued an arm/disarm command and also allows for
automations to depend on this value.

* Optimize

* Update spc.py

* Update spc.py

* fix

* Fix test.

* Fix for removed is_state_attr.
2017-11-11 12:36:03 -08:00
Kane610
68fb995c63 Update axis.py (#10412) 2017-11-11 12:30:18 -08:00
Andrey
4420f11d9d Fix import in tests (#10525) 2017-11-11 22:24:43 +02:00
Erik Eriksson
75836affbe Support configuration of region (no service url neccessary (#10513) 2017-11-11 12:21:25 -08:00
Lukas Barth
b284cc54df Pin yarl (#10528)
* Pin yarl

* Update requirements
2017-11-11 12:15:13 -08:00
Paulus Schoutsen
547e089185 Version bump to 0.57.3 2017-11-11 12:14:28 -08:00
Marcelo Moreira de Mello
fe2e0c44c8 Fixed update() method and removed ding feature from stickupcams/floodlight (#10428)
* Simplified URL expiration calculation and fixed refresh method

* Remove support from Ring from StickupCams or floodlight cameras

* Makes lint happy

* Removed unecessary attributes
2017-11-11 12:12:58 -08:00
Stefan Jonasson
30bd92c851 Tellstick Duo acync callback fix (#10384)
* Reverted commit 1c8f179690. This fixes issue: #10329

* convert callback to async

* fix lint

* cleanup

* cleanup

* cleanups

* optimize initial handling

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint

* Update tellstick.py

* Fixed code errors and lint problems.

* fix bug

* Reduce logic, migrate to dispatcher

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint
2017-11-11 12:12:57 -08:00
Paulus Schoutsen
78afbd4292 Pin YARL to 0.13 2017-11-11 12:12:31 -08:00
Hmmbob
f3a90d6994 Update nederlandse_spoorwegen.py to include platform information (#10494)
* Update nederlandse_spoorwegen.py

Make departure and arrival platforms available as state attributes

* Update nederlandse_spoorwegen.py

* Update nederlandse_spoorwegen.py
2017-11-11 11:51:26 -08:00
Lukas Barth
44506ce15f Adapt to new yarl API (#10527) 2017-11-11 08:36:37 -08:00
Derek Brooks
5fe2db228c bug fixes and linting 2017-11-11 10:18:32 -06:00
Andrey
5e92fa3404 Add an option to serve ES6 JS to clients (#10474)
* Add an option to serve ES6 JS to clients

* Rename es6 to latest

* Fixes

* Serve JS vrsions from separate dirs

* Revert websocket API change

* Update frontend to 20171110.0

* websocket: move request to constructor
2017-11-10 23:02:06 -08:00
Derek Brooks
c91d52a587 first stab at the nuheat components 2017-11-11 00:22:37 -06:00
Jan Almeroth
1c36e2f586 Introduce media progress for Yamaha Musiccast devices (#10256)
* Introduce update_hass()

* Introduce media_positions

* Version bump pymusiccast

* Fix: Unnecessary "else" after "return"

* FIX D400: First line should end with a period

* Version bump

Fixes https://github.com/home-assistant/home-assistant/issues/10411
2017-11-10 23:41:02 +01:00
Kenny Millington
16dd90ac78 Add support for Alexa intent slot synonyms. (#10469) 2017-11-10 09:35:57 -08:00
Eric Hagan
7d9d299d5a OwnTracks Message Handling (#10489)
* Improve handling and logging of unsupported owntracks message types

Added generic handlers for message types that are valid but not
supported by the HA component (lwt, beacon, etc.) and for
message types which are invalid. Valid but not supported
messages will now be logged as DEBUG. Invalid messages will
be logged as WARNING.

Supporting single "waypoint" messages in addition to the
roll-up "waypoints" messages.

Added tests around these features.

* Style fixes
2017-11-10 09:29:21 -08:00
Fabian Affolter
0490ca67d1 Bump dev to 0.58.0.dev0 (#10510) 2017-11-10 09:25:31 -08:00
Matthew Donoughe
e7dc96397c upgrade to new pylutron_caseta with TLS (#10286)
* upgrade to new pylutron with TLS

* rename configuration options

* change more methods to coroutines

* use async_add_devices
2017-11-10 12:17:25 +01:00
Diogo Gomes
9bfdff0be1 add JSON processing capabilities to sensor_serial (#10476)
* add JSON processing capabilities

* format fixes

* format fixes

* Fix according to @fabaff comment

* reverting last commit to a more sane approach

* docstring...

* still docstring...

* passed script/lint

* downgrade exception

JSONDecodeError was only introduced in Python3.5

Since we are still supporting 3.4 ValueError is the parent class of
JSONDecodeError
2017-11-10 10:49:30 +01:00
sander76
143d9492b2 Fix for telegram polling. (added pausing when error occurs) (#10214)
* Fix for telegram polling. (added pausing when error occurs)

* fix pylint error.
invalid variable name ( Exception as _e)). Don't understand why as
removing the underscore fails with my local pylint..

* fixing too short variable name.

* moved logic to `check_incoming`

* fix line too long error.

* Simplify
2017-11-09 21:17:23 +01:00
Fabian Affolter
8e1a73dd0f Upgrade youtube_dl to 2017.11.06 (#10491) 2017-11-09 20:18:29 +01:00
Fabian Affolter
8878eccb7b Upgrade psutil to 5.4.1 (#10490) 2017-11-09 20:17:31 +01:00
cgtobi
37eae7fb8a Improve error handling. (#10482)
* Improve error handling.

* Fix import of core requirements.

* cleanup
2017-11-09 20:17:01 +01:00
Anders Melchiorsen
dd16b7cac3 Remove lag from Harmony remote platform (#10218)
* Simplify kwargs handling

* Move Harmony remote to a persistent connection with push feedback

* Make default delay_secs configurable on the harmony platform

* Remove lint

* Fix delay_secs with discovery

* Temporary location for updated pyharmony

* Remove lint

* Update pyharmony to 1.0.17

* Remove lint

* Return an Optional marker

* Update pyharmony to 1.0.18
2017-11-09 17:57:41 +01:00
David Grant
68986e9143 Updated gc100 package requirement to 1.0.3a (#10484)
* Updated gc100 package requirement to 1.0.3a

* Update requirements_all.txt
2017-11-09 17:54:45 +01:00
Stefan Jonasson
62c1b542ed Tellstick Duo acync callback fix (#10384)
* Reverted commit 1c8f179690. This fixes issue: #10329

* convert callback to async

* fix lint

* cleanup

* cleanup

* cleanups

* optimize initial handling

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint

* Update tellstick.py

* Fixed code errors and lint problems.

* fix bug

* Reduce logic, migrate to dispatcher

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint
2017-11-09 15:03:35 +01:00
Julius Mittenzwei
ee265394a6 Improvement of KNX climate component (#10388)
* Added myself to codeowners

* Improved climate support with setpoint shift for KNX. (https://github.com/XKNX/xknx/issues/48)

* requirements_all.txt

* typo

* flake

* Changes requested by @pvizeli
2017-11-09 11:49:19 +01:00
TopdRob
9297a9cbb4 Upgrade apns2 to 0.3.0 (#10347) 2017-11-08 21:09:19 -08:00
Marcelo Moreira de Mello
2118ab2503 Fixed update() method and removed ding feature from stickupcams/floodlight (#10428)
* Simplified URL expiration calculation and fixed refresh method

* Remove support from Ring from StickupCams or floodlight cameras

* Makes lint happy

* Removed unecessary attributes
2017-11-09 01:01:20 +01:00
Pascal Vizeli
2fff065b2c Remove useless temp converting (#10465) 2017-11-09 00:46:33 +01:00
TopdRob
ed9abe3fa2 Upgrade pyatv to 0.3.6 (#10349)
Fix string conversion for idle state
2017-11-08 16:13:05 +01:00
TopdRob
f5ea7d3c9c Upgrade to 0.1.2 (#10348)
Fix an insecure request warning when not using verify=True. Contributed by @nalepae
2017-11-08 16:11:12 +01:00
Matt White
148a7ddda9 Add include/exclude filter to mqtt_statestream (#10354)
* Add publish filter to mqtt_statestream

* Add tests for include/excludes in mqtt_statestream
2017-11-08 15:54:12 +01:00
Milan V
2f0920e4fb Fix recorder stop on SQLite vacuuming error (#10405)
* Fix recorder stop on SQLite vacuuming error

* Move import to function
2017-11-08 14:43:15 +01:00
Paulus Schoutsen
2e5b1e76ef Fix slow WOL switch test (#10455) 2017-11-08 12:38:17 +01:00
Per Osbäck
db8510f110 update pywebpush==1.3.0 (#10374) 2017-11-08 12:02:28 +01:00
Daniel Høyer Iversen
e49278cc7d update tibber library (#10460) 2017-11-08 11:18:35 +01:00
Paulus Schoutsen
50f6790a27 Remove model info from state (#10399) 2017-11-07 21:28:11 -08:00
Diogo Gomes
a5aa111893 Add baudrate option to Serial sensor (#10439)
* Add baudrate option

Baudrate is essential!

* line too long

line too long (82 > 79 characters)

* trailing whitespace

* Rename const

* Fix the missing one
2017-11-07 22:06:19 +01:00
Robin
119fb08198 Fixes issue #10425 (#10426)
Fixes an error reported resulting from Hammersmith no longer supplying
data.
2017-11-07 18:19:54 +01:00
Adam Mills
11ecc2c171 Remove extra info from zwave entity states (#10413)
* Remove extra info from zwave entity states

* Show initializing for nodes that haven't completed queries
2017-11-07 10:13:39 -05:00
Mister Wil
07f073361f Bump to 0.12.2 to fix urllib3 dependency (#10420) 2017-11-07 01:39:13 +01:00
John Arild Berentsen
5410700708 Zwave save cache to file now. (#10381)
* Add save config

* Add API to save Z-Wave cache to file immediatley.

* lint

* remove none assignment

* docstring
2017-11-06 15:15:52 +01:00
Sebastian Muszynski
131af1fece Device model identification of the Xiaomi Philips Ceiling Lamp fixed. (#10401) 2017-11-06 09:20:31 +01:00
Pascal Vizeli
a9a3e24bde Update aiohttp to 2.3.1 (#10139)
* Update aiohttp to 2.3.1

* set timeout 10sec

* fix freeze with new middleware handling

* Convert middleware auth

* Convert mittleware ipban

* convert middleware static

* fix lint

* Update ban.py

* Update auth.py

* fix lint

* Fix tests
2017-11-05 18:42:31 -08:00
Paulus Schoutsen
39de557c4c Update frontend 2017-11-05 18:26:16 -08:00
Paulus Schoutsen
4742899369 Merge remote-tracking branch 'origin/master' into dev 2017-11-05 18:23:08 -08:00
Fabian Affolter
f3511d615e Upgrae simplepush to 1.1.4 (#10365) 2017-11-05 22:52:58 +01:00
Paulus Schoutsen
8f8772093d Merge pull request #10385 from home-assistant/release-0-57-2
0.57.2
2017-11-05 13:50:20 -08:00
Paulus Schoutsen
210bbc53a4 Update version to 0.57.2 2017-11-05 12:50:06 -08:00
Paulus Schoutsen
ce0537ef7f Update frontend to 20171105.0 2017-11-05 12:49:54 -08:00
Patrik
73cd902857 Fix tradfri problem with brightness (#10359)
* Fix problem with brightness

* Fix typo

* Typo
2017-11-05 12:49:24 -08:00
Simon
5d4514652d Addition of new binary sensor class 'plug' (#10336)
* Addition of new binary sensor class 'plug'

* use term "unplugged"

* add the entry to the right place
2017-11-05 10:25:44 -08:00
John Arild Berentsen
c07e651013 Add heal_node and test_node services. (#10369)
* Add heal_node and test_node services.

* lint
2017-11-05 09:19:19 -08:00
Patrik
bc51bd93f4 Fix tradfri problem with brightness (#10359)
* Fix problem with brightness

* Fix typo

* Typo
2017-11-05 17:43:45 +01:00
Adam Cooper
72ce9ec321 Add platform and sensors for Vultr VPS (#9928)
* Initial commit of Vultr components

Have a working Vultr hub and binary sensor which pulls down the
following attributes of your VPS:
 - Date created
 - Subscription id (server id)
 - Cost per month (in US$)
 - Operating System installed
 - IPv4 address
 - label (human readable name)
 - region
 - number of vcpus
 - which storage package chosen
 - IPV6 address (if applicable)
 - RAM amount

Working next on sensor and then testing / coverage.

* Added Vultr sensor for pending charges and current bandwidth. Refactored binary_sensor and hub too

* Corrected is_on bases

* Added basic tests for Vultr binary & platform

* Updated require files

* Changing test fixture to highlight different cases

* Written basic test for sensor.vultr

* Resolved linting errors and broken test

* Increase test coverage and corrected docs

* Resolved hound issues

* Revert back negative binary test

* Another hound resolve

* Refactoring and adding is switch, moving over to vultr branch

* Made Vultr components more resiliant to invalid configs

* Added negetive test for vultr binary sensor

* Added better testing of vultr sensor

* Resolved vultr platform test affecting subsequent vultr tests

* Moving VULTR components to single use design

* Added in sensor name config

* Added missing sensors var

* Resolved init data setting of sensors, added in name conf to switch

* Made the Vultr component more resiliant to startup failure with better alerting

* Various Vultr component changes

- Refactored sensor, binary_sensor, and switch to reference one subscription
- Renamed CURRENT_BANDWIDTH_GB monitored condition to CURRENT_BANDWIDTH_USED
- Improved test coverage

* Resolved local tox linting issue

* Added more testing for Vultr switch

* Improved test coverage for Vultr components

* Made PR comment changes to vultr binary sensor

* Made PR comment changes to Vultr sensor

* resolved PR comments for Vultr Switch

* Resolved vultr sensor name and improved tests

* Improved Vultr switch testing (default name formatting)

* Removed vultr hub failure checking
2017-11-05 14:10:14 +01:00
Fabian Affolter
a5d5f3f727 Move counter component (#10332)
* Fix docstring

* Add comment

* Move counter to folder

* Fix missing parts

* Commit it when file is saved
2017-11-05 13:51:52 +01:00
Fabian Affolter
5be6f8ff36 Upgrade sqlalchemy to 1.1.15 (#10330) 2017-11-05 13:51:03 +01:00
Per Osbäck
28ef564974 fix a import in test causing vs code to fail to discover (#10358)
* fix a import in test causing vs code to fail to discover

* Change style
2017-11-05 13:50:46 +01:00
Paulus Schoutsen
aae9697d9a Merge pull request #10351 from home-assistant/release-0-57-1
0.57.1
2017-11-04 12:56:38 -07:00
Paulus Schoutsen
af3d9d8245 Update frontend to 20171104.0 2017-11-04 12:53:09 -07:00
Paulus Schoutsen
640729f312 Version bump to 0.57.1 2017-11-04 12:52:39 -07:00
Fabian Affolter
de9d19d6f4 Use constants for HTTP headers (#10313)
* Use constants for HTTP headers

* Fix ordering

* Move 'no-cache' to platform
2017-11-04 12:04:05 -07:00
marconfus
e64803e701 Fix for API change of new enocean package (#10328)
* Fix API change of new enocean package

* Fix lint issue
2017-11-04 12:58:02 +01:00
Paulus Schoutsen
37bb626dd2 Merge pull request #10293 from home-assistant/release-0-57
0.57
2017-11-03 21:59:03 -07:00
Pascal Vizeli
21273de6a1 Move timer into correct folder (#10324)
* Move timer into correct folder

* Rename tests/components/test_timer.py to tests/components/timer/test_timer.py

* create init for test component

* Fix services.yaml loading
2017-11-03 21:18:36 -07:00
Craig J. Ward
fe271749c2 Tc update (#10322)
* use updated client

* update requirements
2017-11-03 21:18:35 -07:00
Paulus Schoutsen
af0253b2eb Fix formatting invalid config text (#10319) 2017-11-03 21:18:35 -07:00
Pascal Vizeli
986bcfef21 Fix recorder purge (#10318)
* Fix recorder purge

* Fix lint

* fix utc convert
2017-11-03 21:18:35 -07:00
William Scanlon
96f19c7205 Strip white space from configurator input (#10317) 2017-11-03 21:18:34 -07:00
Pascal Vizeli
cdc2df012c TellStick / Remove async flavor / add hassio (#10315)
* Remove unused async flavor

* Add tellcore-net support

* Update tellstick.py

* Update requirements_all.txt

* fix lint
2017-11-03 21:18:34 -07:00
Fabian Affolter
8dd790e745 Upgrade credstash to 1.14.0 (#10310) 2017-11-03 21:18:33 -07:00
Sebastian Muszynski
e90e94b667 Allow an empty MAC address at the Xiaomi Aqara Gateway configuration. (#10307) 2017-11-03 21:18:33 -07:00
Paulus Schoutsen
52f40b3370 Cloud: Authenticate with id token (#10304) 2017-11-03 21:18:32 -07:00
Paulus Schoutsen
8ed75217e1 Fix panel_custom (#10303)
* Fix panel_custom

* lint
2017-11-03 21:18:32 -07:00
Paulus Schoutsen
1e92417804 Cleanup Xiaomi Aqara (#10302) 2017-11-03 21:18:31 -07:00
NovapaX
be9cdf51d9 update mask-icon to a working mask-icon.svg (#10290)
* update mask-icon to favicon.svg

* change name of icon to mask-icon.svg
2017-11-03 21:18:31 -07:00
PeteBa
0e1a3c0665 Maintain recorder purge schedule (#10279)
* Maintain automated purge schedule

* Updates from review feedback
2017-11-03 21:18:31 -07:00
Pascal Vizeli
0f7a4b1d6f Move timer into correct folder (#10324)
* Move timer into correct folder

* Rename tests/components/test_timer.py to tests/components/timer/test_timer.py

* create init for test component

* Fix services.yaml loading
2017-11-03 21:10:08 -07:00
Craig J. Ward
acfee385fb Tc update (#10322)
* use updated client

* update requirements
2017-11-03 20:46:40 -07:00
Alok Saboo
96657841c8 Add option to overwrite file to the downloader component (#10298)
* Add option to overwrite file to the downloader component

* Cleanup

* Address Paulus's comments
2017-11-03 13:02:38 -07:00
Pascal Vizeli
a4dec0b6d2 Fix recorder purge (#10318)
* Fix recorder purge

* Fix lint

* fix utc convert
2017-11-03 12:55:00 -07:00
Pascal Vizeli
06d3d8b827 TellStick / Remove async flavor / add hassio (#10315)
* Remove unused async flavor

* Add tellcore-net support

* Update tellstick.py

* Update requirements_all.txt

* fix lint
2017-11-03 20:31:48 +01:00
Paulus Schoutsen
0877ea07b3 Fix formatting invalid config text (#10319) 2017-11-03 18:12:45 +01:00
William Scanlon
31b89f602a Strip white space from configurator input (#10317) 2017-11-03 08:58:03 -07:00
PeteBa
1ffccfc91c Maintain recorder purge schedule (#10279)
* Maintain automated purge schedule

* Updates from review feedback
2017-11-03 08:28:16 -07:00
Fabian Affolter
81324806d5 Move constants to setup.py (#10312)
* Remove unused import

* Move setup relevant consts to 'setup.py'

* remove blank line

* Set source
2017-11-03 07:43:30 -07:00
Sebastian Muszynski
a43f99a71c Allow an empty MAC address at the Xiaomi Aqara Gateway configuration. (#10307) 2017-11-03 07:38:15 -07:00
Hugo Dupras
1347c3191f Refactor Neato botvac components as a vacuum (#9946)
* Refactor Neato botvac components as a vacuum

A switch is still use to enable/disable the schedule

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* CI Hound fixes

* Fix lint errors

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* [Neato vacumm] Add sensor attributes to vacuum

Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>

* Remove line breaks and fix docstring

* PR fixes
2017-11-03 14:25:26 +01:00
Paulus Schoutsen
4e8e04fe66 Clean up core (#10305)
* Clean up core

* Lint

* Fix tests

* Address comment

* Update entity.py

* romve test for forward update to async_update

* fix lint
2017-11-03 14:19:36 +01:00
Fabian Affolter
9b8c64c8b6 Upgrade credstash to 1.14.0 (#10310) 2017-11-03 13:51:17 +01:00
Paulus Schoutsen
a943b207ba Fix panel_custom (#10303)
* Fix panel_custom

* lint
2017-11-03 10:28:31 +01:00
Heiko Thiery
23809bff64 Add LaCrosse sensor platform (#10195)
* Initial commit of LaCrosse sensor component

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* fix review comments from houndci-bot

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* fix review comments from houndci-bot

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* add pylacrosse version to REQUIREMENTS

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* add lacrosse to .coveragerc

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* import 3rd party libraries inside methods

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* add pylacrosse to requirements_all.txt

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* add missing docstring

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* fix pylint warning

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* fix pylint warning

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* fix pylint warnings

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* remove too many blank lines

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* some minor cleanup

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* change to single quote

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* incorporate review comments

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* remove type check as validation only allows TYPES

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>

* Adjust log level and update ordering
2017-11-03 08:59:11 +01:00
Paulus Schoutsen
a4f7828363 Cloud: Authenticate with id token (#10304) 2017-11-03 07:30:05 +01:00
Paulus Schoutsen
2598770b49 Update frontend 2017-11-02 22:52:19 -07:00
Paulus Schoutsen
b77df372d6 Update frontend 2017-11-02 22:51:53 -07:00
Paulus Schoutsen
8f774e9c53 Cleanup Xiaomi Aqara (#10302) 2017-11-02 22:18:10 -07:00
NovapaX
47d9403e3a update mask-icon to a working mask-icon.svg (#10290)
* update mask-icon to favicon.svg

* change name of icon to mask-icon.svg
2017-11-02 20:55:09 -07:00
Markus
4d19092722 pyLoad download sensor (#10089)
* Create pyload.py

* tabs and whitespaces removed

* code style fix

* code style fixes

* code style fix

* fixed standard import order

* classname fixed

* Added homeassistant/components/sensor/pyload.py

* code formatting

* implemented @fabaff recommendations

* Update pyload.py

* Use string formatting

* Make host optional
2017-11-02 22:17:44 +01:00
Sebastian Muszynski
f2a38677fc Bump python-miio for improved device support (#10294)
* Bump python-miio for improved device support.

* Requirements defines updated.
2017-11-02 21:38:18 +01:00
Paulus Schoutsen
8c525b3087 Version bump to 0.57 2017-11-02 09:31:30 -07:00
Paulus Schoutsen
5359001c04 Merge remote-tracking branch 'origin/master' into dev 2017-11-02 09:27:56 -07:00
Paulus Schoutsen
2481cd2012 Update frontend 2017-11-02 09:18:06 -07:00
Paulus Schoutsen
e4ddb00086 Update frontend 2017-11-02 08:59:26 -07:00
vatir
f9a019ea82 Follow-up: Replace emulated_hue: with emulated_hue_hidden (#9894)
When lights in the hue component are used with the emulated hue component ATTR_EMULATED_HUE is still being used, which was deprecated by #9382. This updates ATTR_EMULATED_HUE to ATTR_EMULATED_HUE_HIDDEN to improve consistency and stop the deprecation warnings.
2017-11-02 12:45:02 +01:00
Joaquín
417240ee3e Better scene handling (#10213) 2017-11-02 12:02:03 +01:00
cgtobi
ffc2541ba5 Improve unit tests for season sensor component. (#10288)
* Add setup platform tests for the season sensor component.

* Improve tests to cover 97% of the code.

* Improve tests to cover 97% of the code.
2017-11-02 11:08:17 +01:00
lichtteil
d74dbc35f2 Luftdaten sensor (#10274)
* Add sensor platform for luftdaten.info

* Add monitored conditions to platform schema

* Make monitored conditions config obligatory

* Improve inline documentation

* Import first standard libs then 3rd party libs

* Combine resource url using format

* Remove unnecessary try..except block

* Use state “None” instead of STATE_UNKNOWN

* Minor changes incl. removal of unused vars

* Add missing spaces
2017-11-02 10:32:06 +01:00
Maciej Sokołowski
1c2224cc5c Fixed Tradfri whitebulbs handling after #9703 (#10040) 2017-11-02 08:18:20 +00:00
dominikandreas
56c66a19f0 Update plant for dealing with float values (#10246)
Value parsing in plant component throws an ValueError if values are given as floats. This commit changes int(value) to int(float(value)) to avoid this error.
2017-11-02 09:17:26 +01:00
Lewis Juggins
79da44a6b3 Support new tradfri individual DTLS identification method (#10282) 2017-11-02 06:23:06 +00:00
William Scanlon
d9805160bc Removed username/password auth and moved to new save/load json (#10277) 2017-11-01 21:58:46 +01:00
Pascal Vizeli
f463f4d8c6 WIP: Cleanup async stuff on templates (#10275)
Cleanup async stuff on templates
2017-11-01 15:48:09 +01:00
Eitan Mosenkis
4da8ec0a05 Add Google Assistant support for setting climate temperature and operation mode. (#10174)
Fixes #10025.
2017-11-01 07:44:59 -07:00
Pascal Vizeli
fb34f94d9c Update template.py 2017-11-01 14:20:29 +01:00
cgtobi
513c2b03c9 Add setup platform tests for the season sensor component. (#10270) 2017-11-01 14:06:43 +01:00
Paulus Schoutsen
4dc9ac820f Remove http.development (#10267)
* Remove http.development

* Remove development

* Remove development from tests

* Remove constant
2017-11-01 13:07:16 +01:00
Pascal Vizeli
619d329a16 Add xy support to Alexa HomeAPI v3 (#10268)
* Add xy support to Alexa HomeAPI v3

* Update smart_home.py

* Update smart_home.py

* fix lint

* fix copy paste

* Update smart_home.py

* simplify

* Add test for xy/rgb

* Update test_smart_home.py

* Update smart_home.py

* add test
2017-11-01 12:16:05 +01:00
Fabian Affolter
e2c6f538a8 Add link to docs (#10272) 2017-11-01 12:11:32 +01:00
cgtobi
8739991676 Add unit test for wake on lan component. (#10262)
* Add unit test for wake on lan component.

* Remove unneccessary imports and print calls.

* Clean up lint complaints.
2017-11-01 11:15:24 +01:00
Paulus Schoutsen
26b097b860 Change introduction card to use persistent notification (#10265)
* Change introduction card to use persistent notification

* Lint
2017-11-01 09:10:36 +01:00
Erik Eriksson
4e8723f345 Extracted json saving and loading (#10216) 2017-11-01 09:08:28 +01:00
Paulus Schoutsen
85f30b893e Update netdisco to 1.2.3 (#10266) 2017-11-01 09:05:55 +01:00
Anders Melchiorsen
6cadb796bc Avoid Sonos error during startup (#10146)
* Add Sonos coordinators before slaves
2017-11-01 08:28:13 +01:00
Matt White
9eaa057739 Add EntityFilter helper (#10221)
* Add EntityFilter helper

* Changes in entityfilter after code review

* Convert recorder to use EntityFilter

* Fix flake/lint errors in recorder

* Update entity filter helper to return function

* Update recorder to use updated entity filter

* Better docstrings in entityfilter

* Update entityfilter.py
2017-10-31 21:54:50 -07:00
Per Osbäck
b6324b511c Google Assistant: make it possible to set a custom domain/type (#10188)
* Google Assistant: make it possible to set a custom domain/type

* add test for switch platform

* update custom type test
2017-10-31 20:38:34 -07:00
ChristianKuehnel
80a9539f97 integration with Remember The Milk. (#9803)
* MVP integration with Remember The Milk.

This version offers a service allowing you to create new issues in Remember The Milk.

* fixed pylint issue with import path

* - added files to .coveragerc as the server inerface is hard to test
- added tests for config file handling

* fixed lint error

* added missing docstrings

* removed stray edit

* fixed minor issues reported by @fabaff

* changed naming of the service, so that serveral accounts can be used

* added disclaimer

* moved service description to services.yaml

* fixed blank lines

* fixed structure of configuration

* added comment about httplib2

* renamed internal config file

* improved logging statements

* moved entry in services.yaml into separate folder.

Had to move the component itself as well.

* fixed static analysis findings

* mocked first test case

* fixed bug in config handling, fixed unit tests

* mocked second test case

* fixed line length

* fixed static analysis findings and failing test case

* also renamed file in .coveragerc

* control flow changes as requested by  @balloob
2017-10-31 20:33:47 -07:00
Pascal Vizeli
8c266f9266 Alexa SmartHome API extend (#10251)
* Implement adjustment

* Add color support

* fix lint

* Fix lint & use only RGB

* fix HSB + Test

* Add tests & fix bugs

* add rgb test

* add setColorTemperature

* Add color light support + tests

* Fix color temp

* use kelvin for converting

* use correct calculation
2017-10-31 20:28:17 -07:00
Adam Mills
5043b85c58 Use translated panel names on frontend (#10197)
* Use translated panel names on frontend

* Revert hassio translation
2017-10-31 20:22:04 -07:00
Daniel Perna
890c11cc7c WIP: Timer component (#9889)
* Added timer component

* Reworked functionality a bit

* Fixed requested change

* Fixed state updates when finished

* Removing expired listeners, added events, changed services

* Added finish service

* Using timedelta parameters in start-service

* Cleanup

* Lint

* Updating state for remaining time

* Removed duration from cancel method

* Renamed service to fix disabled lint

* Some tests (incomplete)

* Relocated service descriptions

* Addressed requested changes

* Adjusted tests, added methods and events

* Added test for finish service, lint

* Code cleanp, using string states

* tzzz... one char...

* Proper usage of restore_state

* Some more cleanup
2017-10-31 20:20:56 -07:00
Ted Drain
253c8aee1f Mqtt light options to fix #9330 and #7810 (#9829)
* Added ability to control when the on command is sent.

* Changed to allow only brightness command

* Code cleanup

* Added test cases for on command mode.

* Added addition test

* Changed brightness options to lower case.

* Fixed case of default value

* Remove default
2017-10-31 23:18:49 +01:00
cdce8p
12e1602a81 input_text - Added service doc (#10238)
* * Added service doc

* * Deleted print statement
2017-10-31 14:51:12 +01:00
GTH
25a25dde7a Add support for 'Send Current Position' feature in Geofency 5.1 (#10012) 2017-10-31 14:17:14 +01:00
biggms
c0eaf0386c Changed single tolerance value to COLD and HOT tolerances. Allows on and off states to have different error bands. (#9843) 2017-10-31 14:06:34 +01:00
Audric Schiltknecht
6b96bc3859 Add support for odhcpd DHCP server (#9858)
This commit adds support for the odhcp DHCP server in addition to
dnsmasq. A new configuration option 'dhcp_software' has been added and
allows the user to set the server used (defaults to dnsmasq to not break
existing installations).
2017-10-31 13:40:12 +01:00
Fabian Affolter
6d94c121a7 Move constant to 'const.py' (#10249) 2017-10-31 13:31:12 +01:00
Andrey
ae34640a80 Use theme color in loading screen. (#10248) 2017-10-31 13:30:50 +01:00
Greg. A
8832de80bc Sytadin default value must be a list #10233 (#10234)
* Sytadin default value must be a list #10233

* Sytadin default value must be a list #10233 v2
2017-10-31 08:32:26 +01:00
Eric Hagan
ed3f7d1581 OwnTracks work. Beacon logic and testcases (#10183)
* OwnTracks work. Beacon logic and testcases

The existing test cases don't really make clear what is being
tested and the iBeacon / Region / Zone / Tracker thing is all
a bit confused.

I'm distinguishing a fixed-place beacon used to trigger entrance
into an HA zone (as a Region Beacon) from a beacon affixed to a
portable or mobile object (as a Mobile Beacon). The behaviors
and test cases for those usages should be different. A Region Beacon
will be named the same as a Home Assistant Zone and seeing an event
from that beacon should trigger a device tracker update related
to that zone. It would be appropriate, though unnecessary, to
configure the Region Beacon with the GPS coordinates of its static
physical location.

A Mobile Beacon is not named after any HA Zone and seeing the beacon
triggers an update in HA setting the location of the beacon to the
current device_tracker location. In this way, when my_phone sees
the beacon on my_keys, the location of my_keys is set to where
my_phone is. And when my_phone stops seeing my_keys, my_keys location
is the location of my_phone the last time it saw them.

A Mobile Beacon's GPS information should be ignored because it's
almost certain to be incorrect because the beacon moves. In fact,
beacons typcially come configured with lat/lon as 0.0/0.0 so using
the location of the beacon in an update has a nasty habit of
setting you and your keys on the bottom of the Atlantic Ocean.

Leave message handling is changed to treat mobile beacons
differently from region beacons and gps regions.

active beacons should be a set. you shouldn't end up
with multiple "active" entries for the same beacon. Let's
enforce that with the correct data structure.

Added test for real-world bug that is fixed.
A series of mobile beacon and region beacon
enter and leave events could cause a mobile
beacon to stick to the tracking device even
though it had tracked through a "leave" event.

Changed two tests to look at the size
of the 'mobile_beacons_active' structure rather
than at the object which will allow this test
to work with any sort of list, set, etc.

* Removing excess logging and unnecessary try catch.

From review on PR #10183 I've removed some info logging
that was unnecessary and I've made the suggested changes
to an if block and a try/catch.
2017-10-31 00:18:45 -07:00
Mark Jozefiak
062fb7ac4c Add priority attribute for hyperion (#10102)
* light.hyperion: Add priority attribute

Allows to set the priority of the hyperion remote instance.

* fix lint errors

* Remove whitespace
2017-10-30 21:48:42 +01:00
Fabian Affolter
cc293db5ab Update services.yaml files (#10229)
* Add period to the description

* Update abbreviation
2017-10-30 21:39:12 +01:00
Sean Dague
c9c102815a Clarify yamaha play_media parameters (#10228)
The play_media parameters are a little bit black magic for the yamaha
platform, this explains what they are in a code comment instead of
having to go look at the yamaha platform itself.

Fixes Bug #10180
2017-10-30 21:38:52 +01:00
Jan Almeroth
5d23afdc9e Introducing multi-zone support for yamaha_musiccast devices (#9968)
* Introduce zones to yamaha_musiccast
* Introducing new config 'interval_seconds'
* Version bump pymusiccast
* Removing name parameter
2017-10-30 13:50:20 -04:00
Sebastian Muszynski
e95b48ca44 Xiaomi Aqara: Remove/Add device service added (#10150)
* First draft of a remove device service. Fixes https://github.com/home-assistant/home-assistant/issues/9571.

* Add device service introduced. Enables the join permission of the gateway to pair a new sub device within the next 30 seconds.

* Schema validation added and some refactoring.

* A more precise validation of the gw_mac (ffffffffffff vs. ff:ff:ff:ff:ff:ff).

* Persistent notification added to provide some feedback.

* Pylint warning disabled. The methods are used indirectly.

* CODEOWNERS reference updated.
2017-10-30 17:33:16 +01:00
Aaron Bach
646c03eea1 Add entity_picture_template options to Template Cover, Template Light, Template Sensor, and Template Switch (#9854)
* Re-adding cover

* Re-adding light

* Re-adding sensor

* Re-adding switch

* Re-added tests

* Fixing missing imports in rebased test

* Fixing broken tests

* Owner-requested changes

* Owner-requested changes

* Fixed exception
2017-10-30 09:28:37 -07:00
Nicolae Vlădescu
05ece53ec2 Librouteros capsman fix (#10217)
* Catch MultiTrapError exceptions also

* Expect librouteros api exceptions on every call and also set fallbacks

* Do not re raise

* Fix lint issue
2017-10-30 08:41:37 +01:00
David Grant
b5214af762 Add gc100 platforms and component (#10159)
* Initial commit of gc100 platforms and component

* Fixed removed temporary code to set import path

* Fixed removed unused import

* Implementing changes requested by @fabaff

* Fixed linter errors

* Remove reference to binary sensor

* Move const

* Add const

* Fix pylint
2017-10-30 08:40:14 +01:00
Marcelo Moreira de Mello
3630dc7ff3 Raincloudy version bump (#10225) 2017-10-30 08:36:38 +01:00
Peter Epley
e7fc8a1890 Google Assistant Script Support (#10148)
* Add script support as scene

* Add script support as scene

* Corrected missing script definition
2017-10-30 00:11:23 -07:00
Fabian Affolter
2891b0cb2e Upgrade restrictedpython to 4.0b2 (#10179)
* Upgrade restrictedpython to 4.0b2

* Update test
2017-10-30 00:02:15 -07:00
Paulus Schoutsen
fc44a4ed99 Update frontend 2017-10-29 23:29:49 -07:00
Pascal Vizeli
444b7c5ee7 Add new service 'snapshot' for camera (#10207)
* Add new service 'snapshot' for camera

* Fix lint

* fix arguments

* Add test and fix bugs

* Fix lint

* Fix typo
2017-10-29 23:14:26 +01:00
Fabian Affolter
690760404b Move constant to 'const.py' and use already defined ones (#10211) 2017-10-29 17:28:07 +01:00
Greg Dowling
6a9968ccb9 Bump pyvera to 0.2.38. (#10206) 2017-10-29 13:50:21 +01:00
Fabian Affolter
e91ed1f2a4 Upgrade youtube_dl to 2017.10.29 (#10202) 2017-10-29 12:32:33 +01:00
Fabian Affolter
115c59d88c Move constant to 'const.py' and use already definied ones (#10204) 2017-10-29 12:32:02 +01:00
Fabian Affolter
97bb252d23 Upgrade pylast to 2.0.0 (#10200) 2017-10-29 11:54:11 +01:00
cgtobi
20a1a52bd5 Add unit test for hddtemp sensor. (#10154)
* Add unit test for hddtemp sensor.

* Remove sample fixture and include it in the code.

* Add test to raise coverage.

* Fix bug when host is not reachable.

* Minor code cleanups.

* More code cleanups.
2017-10-29 11:53:53 +01:00
Patrik
9e27e05a84 Update CODEOWNERS (#10198)
Adding myself to monitor tradfri
2017-10-29 11:19:34 +01:00
Fabian Affolter
67c48736a2 Add clickatell (#10199) 2017-10-29 11:19:04 +01:00
Fabian Affolter
35805e51a3 Add Random binary sensor (#10164) 2017-10-29 11:15:57 +01:00
TopdRob
6057b41151 update boto3 to 1.4.7 and botocore to 1.7.34 (#10121) 2017-10-29 10:24:56 +01:00
David Lloyd
2374659984 Added new Clickatell SMS messaging Notify Platform (#9775)
* Added new Clickatell SMS messaging Notify Platform

* Added new Clickatell SMS platform with layout corrections

* Added new Clickatell platform with additional  layout corrections

* Added new Clickatell platform with exception handling removed

* Added new Clickatell platform with poor reference removed

* Reversed changes to dev_docker file

* Minor changes
2017-10-29 09:14:40 +01:00
Egor Tsinko
2c3195522f media_title property now returns current source (#10120) 2017-10-29 02:26:55 +02:00
Greg. A
b3e88d1f8f Add Sytadin Traffic component (#9524)
* Add Sytadin Traffic component

* Add Sytadin Traffic component, update

* Add Sytadin Traffic component, update

* Add Sytadin Traffic component, update

* Add Sytadin Traffic component, update

* Add Sytadin Traffic component, update

* implements @fabaff comments

* Formatting

* formatting

* formatting

* split monitored condition to be independent

* formatting

* add version

* fix requirements part based on gen_requirements_all.py result

* formatting

* requirements stuff file

* formatting

* add missing file into .coveragerc

* add me for code review

* configuration management updated

* implements @fabaff and @pvizeli comments

* indentation

* indentation

* indentation

* indentation

* add const DEFAULT_UPDATE_INTERVAL

* use const DEFAULT_UPDATE_INTERVAL

* remove blank line

* fixes debug call

* clean for loop

* use BeautifulSoup for html parsing

* Update requirements

* Add throttle and fix remaining issues

* Don't add const
2017-10-29 01:34:55 +02:00
Florian Klien
dd7d8d56bb added Yesss SMS platform (#10177)
* added Yesss SMS component

requires YesssSMS==0.1.1b

* requirements_all

* coveragerc

* fix

* docstring fix

* added Exception handling

* requirements_all

* fixing lint error, version bump for YesssSMS
2017-10-29 01:05:56 +02:00
Sebastian Muszynski
df19172e56 Limits of the favorite level updated. Values between 0 and 16 will be accepted. (#10186) 2017-10-28 10:54:11 +02:00
Marcelo Moreira de Mello
f060dcc0aa Added capability to pass a filename to the downloader component (#10059)
* Added capability to pass the filename to the downloader component

* Simplified filename conditions
2017-10-27 22:50:02 +02:00
Menno Blom
5c168ab551 Nederlandse spoorwegen (#10136)
* Add Nederlandse Spoorwegen sensor

* Remove unused 'delay' option.

* Apply requested code reviewed changes

- use constants from const.py
- ensure the configuration is a list
- verify credentials upon platform setup
- verify station input
- add True as second param to add_device
- only call add_device if at least 1 sensor was setup
2017-10-27 22:19:12 +02:00
Sebastian Muszynski
fe9b45c964 Xiaomi MiIO Light: Philips Eyecare Lamp 2 support (#10007)
* Xiaomi Philips Eyecare Lamp 2 support introduced.

* Code clean-up.

* Make hound happy again (idents).

* Revert "Code clean-up."

This reverts commit ea637602ff.

* Unused platform constant removed.

* Nice compromise of the code clean-up implemented.
2017-10-27 22:04:48 +02:00
Fabian Affolter
38c189ecf4 Revert "Upgrade restrictedpython to 4.0b2"
This reverts commit 8e4f0ea5ae.
2017-10-27 21:52:44 +02:00
Fabian Affolter
8e4f0ea5ae Upgrade restrictedpython to 4.0b2 2017-10-27 21:50:22 +02:00
Lukas Barth
248d974ded Cast attribute values to string before publishing to MQTT (#9872)
* Cast attribute values to string before publishing to MQTT

* Simplify

* Use JSON serialization, add test
2017-10-27 08:55:04 -07:00
Ryan McLean
2d93285689 Linode (#9936)
* Fix: Last Played Media Title in plex would stay even when player was idle/off
     Primary Fix is in the "if self._device" portion.
     code in "if self._session" is a catch all but i'm not 100% if it is needed.

* Fixed lint issues with previous commit

* 1st Pass at refactoring plex refresh
Moved _media** into clearMedia() which is called in _init_ and
at start of refresh.

Removed redunant _media_* = None entries

Grouped TV Show and Music under single if rather than testing
seperately for now.

* Fixed invalid name for _clearMedia()
Removed another media_* = None entry

* Removed print() statements used for debug

* Removed unneeded "if" statement

* Added Base Support for Linode

* Removed unused Attr

* Corrected some Typos

* updated requirements & coveragerc

* added import to prevent var not declared errors in linter

* Fixed Typo

* Added Switch Component for Linode and corrected some errors if data was blank

* Updated api lib to hopefully remove dependancy on enum34

* Fixed Reference error

* fix pylint errors

* Update linode.py

* Update linode.py

* Update linode.py

* Update linode.py
2017-10-27 16:19:47 +02:00
bastshoes
68390373e5 Fix for issue #9240 (#10173)
Changes for respecting mqtt fan config. If fan speed and oscillation is not configured they wouldn't be displayed in UI.
2017-10-27 16:04:18 +02:00
randellhodges
c0f8e6c5c5 Updated denon component to play nice with the 3808CI that doesn't support NSFRN command (#10157) 2017-10-27 14:22:23 +02:00
Casper Weiss Bang
85d7377beb MPD now uses the filename if song doesn't have metadata (#10085)
* added support for filename

* used the getter instead - minor mistake

* changed how the filename is generated
2017-10-27 11:21:47 +02:00
Anders Melchiorsen
f17cf1d26b Avoid Sonos errors for tracks with no artist information (#10160)
Rather than adding guards to the string formatting, just remove this
unused attribute.
2017-10-27 10:50:03 +02:00
Anders Melchiorsen
e50b59a56c Reduce album art flickering in media player UI (#10163)
* Add HTTP cache header to proxied media player images

With the resource actually being cacheable, preemptively extend the cache
buster key to prevent hash collisions.

While at it, change the hash from md5 to sha256 for consistency with the
access_token method.

* Remove lint
2017-10-27 10:49:20 +02:00
Fabian Affolter
e43fefa8f6 Support for NO-IP (#10155)
* Support for NO-IP

* Update URL
2017-10-27 10:15:47 +02:00
Yannick POLLART
e819678e27 Rfxtrx binary sensor rewrite (#10152)
* Refactored Lighting4-specific code (issue #8907)

* Fixed lint problem and removed unneeded function call.

* fixed malformed if

* removed backslash

* fixed code for pylint

* removed useless assignment

* using hasattr() instead of try/catch
2017-10-27 10:01:46 +02:00
Florian Klien
9df7302603 New DTLSSocket version that fixes Cython dependency for Trådfri (#10123) 2017-10-27 07:53:36 +01:00
Fabian Affolter
afe88dfa0f Upgrade libnacl to 1.6.1 (#10161) 2017-10-27 08:01:32 +02:00
Fabian Affolter
027ce2f555 Upgrade python_opendata_transport to 0.0.3 (#10162) 2017-10-27 08:01:12 +02:00
Fabian Affolter
63a10233c5 Upgrade sendgrid to 5.3.0 (#10166) 2017-10-27 08:00:53 +02:00
Egor Tsinko
acbf45d5f8 added platform discovery code back into device_tracker (#10169) 2017-10-27 07:57:31 +02:00
Paulus Schoutsen
d13f3eca92 Update frontend to 20171027.1 2017-10-26 22:29:59 -07:00
Andrey
fc291dd5ab Don't use pypi package in dev mode. Allow non-fingerprinted mdi. (#10144) 2017-10-26 22:28:07 -07:00
Adam Mills
583e57042b Core POC support for polymer i18n (#6344)
* Core POC support for polymer i18n

* Add gulp to build_frontend

* Remove frontend build

* Updated translations format

* Eliminate translation namespace from panel names

* Only register translations path in dev mode
2017-10-26 21:46:21 -07:00
Sebastian Muszynski
9d0c2a8dae Xiaomi MiIO Switch: Support for different device types (#9836)
* Support for different device types of MIIO switches: Xiaomi Smart WiFi Socket (called Plug), Xiaomi Smart Power Strip and Xiaomi Chuang Mi Plug V1.

* Line too long fixed.

* Trailing whitespace removed.

* Changes based on review.

* Line too long fixed.

* No blank lines allowed after function docstring fixed.

* The underlying library is called python-miio now. Imports and requirements updated.

* TODO comment removed. Travis complains about.

* Blank line removed.

* Code clean-up.

* Revert "Code clean-up."

This reverts commit 96b191c7a6.

* Unused platform constant removed.
2017-10-26 23:37:30 +02:00
Richard Leurs
c2ef22bd08 Add display currency setting to CoinMarketCap sensor (#10093)
* Add support for different display currencies in CoinMarkerCap sensor.

* Add test for CoinMarketCap sensor.

* Add test dependency to gen_requirements_all.

* Fix review comments: use string formatting and less string case chanes.
2017-10-26 18:49:17 +02:00
Daniel Høyer Iversen
2561efe45d Add last action to xiaomi aqara button (#10131) 2017-10-26 18:46:29 +02:00
Rasmus
c191c13f3a Telldus Live: Device without methods is a binary sensor (#10106)
Telldus Live reports binary sensors as devices without methods.
2017-10-26 15:54:49 +02:00
Fabian Affolter
b1291e572e Upgrade pysnmp to 4.4.1 (#10138) 2017-10-26 10:38:27 +02:00
Jeroen ter Heerdt
d1416056cd Microsoft tts (#9973)
* Microsoft Cognitive Services text-to-speech

* Adding microsoft.py to .coveragerc

* Update microsoft.py

* Update microsoft.py

* Update requirements_all.txt

* fix order
2017-10-25 19:43:21 +02:00
milanvo
7987065ad7 Fix recorder crash for long state string - enforce at core level (#9696)
* Recorder exception catch for long state string

* Revert - Recorder exception catch for long state string

* Validate state length at core level

* Revert - this reverts commit 9d6bd017d9.

* Revert - Recorder exception catch for long state string

* Fix state TypeError

* Test for long state exception
2017-10-25 09:05:30 -07:00
Sam Birch
fc8940111d Binary sensor for detecting linear trends (#9808)
* Trend sensor now uses linear regression to calculate trend

* Added numpy to trend sensor test requirements

* Added trendline tests

* Trend sensor now has max_samples attribute

* Trend sensor uses utcnow from HA utils

* Trend sensor now completes setup in async_added_to_hass

* Fixed linter issues

* Fixed broken import

* Trend tests make use of max_samples

* Added @asyncio.coroutine decorator to trend update callback

* Update trend.py
2017-10-25 17:33:17 +02:00
Georgi Kirichkov
63c9d59d54 SNMP switch (#9840)
* Initial support for SNMP switch

Supports setting integer values

* Resolves styling issues

* Updates requirements

* Remove commented out import

* Changes default community to private. Fixes linting errors.

* More linter fixes and bugfix in #state()

* Refactors the code to make it simpler.
2017-10-25 16:09:29 +02:00
Anders Melchiorsen
61ccbb59ce Fire numeric_state action when first state change matches criteria (#10125)
* Fire numeric_state action when first state change matches criteria

* Remove lint

* Update numeric_state.py
2017-10-25 16:01:09 +02:00
Pascal Vizeli
5fabfced38 Fix lint google-domains (#10135)
* Fix lint google-domains

* Update google_domains.py
2017-10-25 15:43:02 +02:00
Daniel Perna
6c39e1ef19 Added increment + decrement to input_number (#9870)
* Added increment + decrement to input_number

* Lint

* Fix tests

* Another lint

* Additional testing

* Added service descriptions

* Consolidated service registration

* Shortened service registration

* Fixed service descriptions

* Fix Lint
2017-10-25 15:25:33 +02:00
Hydreliox
632466bb56 Add Deluge Sensor (#10117)
Add a sensor to provide upload and download speed of the Deluge Bittorrent Client
2017-10-25 15:13:11 +02:00
Trevor
7784c40f12 Add Google Domains component (#9996)
* Add Google Domains component

* Fixes for hound

* Add Google Domains tests

* Fixes for hound

* Clean up Google Domains

* Add timeout to Google Domains

* Remove whitespace from blank lines

* Update google_domains.py

* Update google_domains.py
2017-10-25 11:42:53 +02:00
Ryan McLean
41fa8cc8f2 Plex refactor Part 1 - Update plexapi to 3.0.3 (#9988)
* Fix: Last Played Media Title in plex would stay even when player was idle/off
     Primary Fix is in the "if self._device" portion.
     code in "if self._session" is a catch all but i'm not 100% if it is needed.

* Fixed lint issues with previous commit

* 1st Pass at refactoring plex refresh
Moved _media** into clearMedia() which is called in _init_ and
at start of refresh.

Removed redunant _media_* = None entries

Grouped TV Show and Music under single if rather than testing
seperately for now.

* Fixed invalid name for _clearMedia()
Removed another media_* = None entry

* Removed print() statements used for debug

* Removed unneeded "if" statement

* Changes
* Updated Requests to 2.18.4
* Updated plexapi to 3.0.3
* Removed function to convert_NA_to_None
* Removed function get_thumb_url

Type changes
* _session.player is now a list players
* na_type deprecated and to be removed
* plexapi has change na to None

Known Issues:
* Player controls currently broken
* Last location (library) stays while player idle

* Username is now Usernames and a list

* Fix for broken controls

* Removed errant print statement

* Removed depecrated na_type

* Updated Plex Sensor to use plexapi 3.0.3
Added support for Token to be used in sensor

Known Issues:
Username and Password broken for Plex Sensor use Token instead for now

* removed TODOs

* Fixes for private access violations

* Removed need for _local_client_fix

* Removed unused import and fixed parens
2017-10-25 11:42:13 +02:00
Trevor
2a2a106e62 Fix Sonarr and Radarr divide by zero (#10101)
* Fix Sonarr and Radarr divide by zero

* Fixes for hound

* Clean up Radarr diskspace
2017-10-25 11:37:08 +02:00
Anders Melchiorsen
45e140149b Allow folder selection for IMAP unread sensor (#10126) 2017-10-25 11:36:00 +02:00
Kane610
34368a6b69 WIP: Refactor Axis component removing external dependencies (#9791)
* Async rewrite

* Device and events now working with async core

* More async than before

* Methods have moved

* Remove check if parameter serial number is available since library handles reconnection anyway and we can expect user to set up configuration correctly

* Async rewrite

* Device and events now working with async core

* More async than before

* Methods have moved

* Remove check if parameter serial number is available since library handles reconnection anyway and we can expect user to set up configuration correctly

* Serial number is the only unique identifier, checks are still needed

* No async for setup_device

* Bump axis to 13 in preparation of friday the 13th

* Fix review comments

* Removed async after some discussions with @armills.
It wasn't possible to get past that aiohttp doesnt support digest auth, which makes it impossible to convert the full library at this point in time.
2017-10-25 00:04:30 -07:00
Sebastian Muszynski
e8f5445acc Xiaomi MiIO Fan: Xiaomi Air Purifier 2 integration (#9837)
* Xiaomi Air Purifier 2 integration

* Flake8 errors fixed.
Changes based on review.

* Service domain ("fan") updated and services properly prefixed by xiaomi_miio.

* The underlying library is called python-miio now. Imports and requirements updated.

* Version bumped. The underlying library is called python-miio now.
2017-10-24 23:50:01 -07:00
Daniel Høyer Iversen
00b9297082 Rfxtrx fix (#10128)
Rfxtrx fix (#10128)
2017-10-25 08:41:02 +02:00
Paulus Schoutsen
2bdad5388b Consolidate frontend (#9915)
* Consolidate frontend

* Remove home-assistant-polymer submodule

* Convert to using a pypi package for frontend

* fix release script

* Lint

* Remove unused file

* Remove frontend related scripts

* Move hass_frontend to frontend REQUIREMENTS

* Fix tests

* lint

* Address comments

* Lint + fix tests in py34

* Fix py34 tests again

* fix typo
2017-10-24 19:36:27 -07:00
Sergey Isachenko
29fb65b224 Fixes #10030. Extented Network Exceptions handling. (#10116)
* Fixes #10030. Extented Network Exceptions handling.

* Remove unused import. Replace ex.reason to ex.message to use custom exception instead of HTTPError
2017-10-24 19:15:25 +02:00
Fabian Affolter
560a4ef5eb Fix PEP8 and PEP257 issues (#10108) 2017-10-24 18:36:08 +02:00
Hydreliox
186f8f6996 Add Deluge Switch Component (#9979)
* Add Deluge Switch Component

* Update deluge.py
2017-10-24 16:44:12 +02:00
Paulus Schoutsen
238884dfe2 Revert gactions in Docker (#10115) 2017-10-24 07:30:24 -07:00
Pascal Vizeli
6da08deabf Merge pull request #9118 from jbarrancos/dev
Rain Bird LNK WiFi Irrigation Implementation
2017-10-24 15:01:15 +02:00
Markus
e970edbf20 fixed typo (#10110) 2017-10-24 14:44:38 +02:00
Pascal Vizeli
7c69941f13 cleanup 2017-10-24 12:25:12 +02:00
Pascal Vizeli
179655b6b0 Merge pull request #10079 from home-assistant/scrape-auth
Add support for HTTP Basic/Digest authentication
2017-10-24 12:21:44 +02:00
Pascal Vizeli
6ebff3cda4 Merge pull request #10105 from home-assistant/bayesian-sensor
Use constants and update docstrings
2017-10-24 12:18:02 +02:00
Daniel Høyer Iversen
70eaa5f10e Update CODEOWNERS (#10103) 2017-10-24 09:34:42 +02:00
Adam Cooper
485e81db79 whois domain lookup sensor (#10000)
* Init commit of new whois sensor

* Updated requirements

* Resolved updated showing expired, added expired attr

* Added missing attribute in init, added whois to coveragerc

* Various PR comment changes

- Now more resiliant to invalid hostnames
- Removed various assumed STATE_UNKOWN setting
- Upfront check for valid hostname preventing the sensor starting with dud
- Resolved unit of measurement Day, Days, None issue
- Datetime formatting now done to iso 8601 standard
- Removed all expired usage, not really that useful
- Unused hass assignment

* More PR comment resolutions

- Resolved the dilemma with hosts / single host per sensor. Now running
single domain per sensor.
- Renamed host(s) to domain

* Moved coveragerc sensor location

* Re-phrased the expiration_date warning

* Resolved assumed updated_date existence

* Resolved missing indent

* Resolved discover_info typo

* Update whois.py
2017-10-24 09:34:06 +02:00
Fabian Affolter
fc2f41fe8a Use constants and update docstrings 2017-10-24 09:12:01 +02:00
Pascal Vizeli
a4b0e8f897 Merge pull request #10082 from arsaboo/uptimefix
Add minutes to Uptime sensor
2017-10-24 08:32:46 +02:00
Pascal Vizeli
3cf99e29be Merge pull request #10100 from home-assistant/additional-event-tests
Additional event data tests to cover recent bugs
2017-10-24 08:28:09 +02:00
Adam Mills
5f8eb08cd9 Additional event data tests to cover recent bugs 2017-10-23 19:44:07 -04:00
pezinek
d1424714c7 Support for Entity.available in sensor/rest (#10073) 2017-10-23 23:29:41 +02:00
Fabian Affolter
74e93e5853 Upgrade Sphinx to 1.6.5 (#10090) 2017-10-23 23:15:36 +02:00
arsaboo
bd72f45788 Added minutes to uptime sensor 2017-10-23 14:38:16 -04:00
arsaboo
845fd532f0 Reverse tests 2017-10-23 14:08:38 -04:00
arsaboo
46404a84ec Update tests 2017-10-23 13:45:20 -04:00
arsaboo
ebce666264 Fix decimals in uptime sensor 2017-10-23 13:05:20 -04:00
Fabian Affolter
15cf34f45f Add support for HTTP Basic/Digest authentication 2017-10-23 17:48:51 +02:00
Pascal Vizeli
e620479cc8 Merge pull request #10069 from home-assistant/release-0-56-2
0.56.2
2017-10-23 17:40:58 +02:00
Pascal Vizeli
b292a4af3f EntityComponent: revert warning (#10078)
* Add warning back

* fix lint
2017-10-23 17:39:50 +02:00
Fabian Affolter
79d71c6727 Version bump to 0.56.2 2017-10-23 16:09:26 +02:00
Thom Troy
0b850b555f add eph ember controls (#9721)
* add eph ember controls

* updates based on review

* remove unused import

* update to new version of pyephember

* added myself to codeowners as requested

* make codeowners alphabetical

* run fixed gen_requirements_all

* Update ephember.py
2017-10-23 15:52:39 +02:00
R1chardTM
176c99f0cd Change deprecated use of maintainer tag in Dockerfile. (#10068) 2017-10-23 15:25:55 +02:00
Fabian Affolter
42e59b465e Make host optional (#10063)
* Make host optional

* Update test to reflect code changes
2017-10-23 15:24:04 +02:00
Daniel Høyer Iversen
e8a701ffd0 update library for xiaomi_aqara, change from pyCrypto to cryptography (#10066) 2017-10-23 14:55:36 +02:00
Fabian Affolter
32f58baa85 Fix merge conflict 2017-10-23 13:49:45 +02:00
Pascal Vizeli
9794336113 Remove warning 2017-10-23 13:48:25 +02:00
Teemu R
ed82f23da3 switch.tplink: fix overlooked issue with statically defined names (#10053) 2017-10-23 13:46:00 +02:00
Maciej Bieniek
48c86e07fa fix gateway illumination sensor value (#10045) 2017-10-23 13:45:34 +02:00
Fabian Affolter
76a0763cbc Remove STATE_UNKNOWN (#10064) 2017-10-23 13:12:14 +02:00
Fabian Affolter
f4f36a3662 Add link to docs and update ordering (#10062) 2017-10-23 12:18:23 +02:00
Philipp Schmitt
e201bcad14 Show current program thumbnail as media_image (#10033)
* Do not include program data in media_title if program data is undefined (None)

* Show thumbnail of currently playing program

* async setup

* Update requirements
2017-10-23 12:04:23 +02:00
Fabian Affolter
5182f76aea Merge pull request #10060 from home-assistant/some-cleanups
Remove warning component / Update event trigger for UI created
2017-10-23 11:54:50 +02:00
Fabian Affolter
53b1c75d81 Merge pull request #10061 from cgtobi/glances_icons
Add icons according to sensor types.
2017-10-23 11:12:17 +02:00
Tobias Sauerwein
fdc769abf7 Add icons according to sensor types. 2017-10-23 07:54:57 +00:00
Teemu R
f57e307c7a switch.tplink: fix overlooked issue with statically defined names (#10053) 2017-10-23 09:41:47 +02:00
Kevin Fronczak
f9d89a016e Add fail2ban sensor (#9975)
* Initial revision of fail2ban sensor

* Verified working, added tests

* Re-factored code so that log reading isn't called for each sensor

* Lint fixes

* Removed errant reset of last ban, added test to verify bans persist through update

* Removed for loop in read_log and replaced with regex per review request

* Refactored update to use current ban array for last ban state

- also was missing return False in timer for default behavior

* Removed CONF_SCAN_INTERVAL from PLATFORM_SCHEMA.extend

- renamed DEFAULT_SCAN_INTERVAL to SCAN_INTERVAL

* SCAN_INTERVAL changed to timedelta

* Force travis rebuild (last build timed out)

* Using compiled regex now
2017-10-23 09:20:45 +02:00
Pascal Vizeli
205f24c070 Trigger also with orderdict 2017-10-23 09:01:59 +02:00
Pascal Vizeli
4bf1972393 Remove warning 2017-10-23 08:58:02 +02:00
sander76
4fa0119245 fixing a typo in the old library which broke hub gen2 compatibility (#9990)
* fixing a typo in the old library.
Should now work with both version 1 and version 2 hub

* version bump

* fix Scene shadowing

* fix requirements. (not sure whether I should commit the other generated files as well ?)
2017-10-22 23:34:50 -07:00
Maciej Bieniek
ccde371a9d fix gateway illumination sensor value (#10045) 2017-10-23 08:02:20 +02:00
Adam Mills
4e7cc110d9 Fix no data event triggers (#10049)
* Test including extra data on a no data trigger

* Match any dicts for default schema for event data

* Fix indentation

* Only check schema if one was configured
2017-10-22 20:20:38 -04:00
Adam Mills
05ba78d886 Aioautomatic bump and scope update (#10043)
* Bump aioautomatic version

* Include vehicle:events scope for automatic

* Sort scopes
2017-10-22 17:11:35 -04:00
Adam Mills
ee56e33193 Add regression test for entity ID update bug (#10037) 2017-10-22 14:23:20 -04:00
Paulus Schoutsen
106bf467f8 0.56.1 (#10035)
* Version bump to 0.56.1

* Fix device update / entity_id with names (#10029)

* Fix device update

* Add tests

* add test for disabled warning

* fix temperature/humidity sensors valid values (#10024)
2017-10-22 19:56:20 +02:00
Pascal Vizeli
56cbfb5f2a Fix device update / entity_id with names (#10029)
* Fix device update

* Add tests

* add test for disabled warning
2017-10-22 08:40:00 -07:00
Maciej Sokołowski
193188b965 RGB Tradfri simple support (#9703) 2017-10-22 16:22:51 +01:00
Thom Troy
4197c9ee85 add irish rail transport sensor (#9883)
* add irish rail transport sensor

* Add True as last device

* Update irish_rail_transport.py
2017-10-22 14:18:34 +02:00
Klaas Hoekema
9418c61b25 Use feed name assigned in EmonCMS if there is one (#10021)
The default names for the feeds created by the EmonCMS component are
like 'emoncms1_feedid_10', but
- This is the display name. The ID should be lowercase and underscored,
  but the display name should be readable. The ID gets derived from it
  and comes out formatted correctly.
- EmonCMS lets you assign names to feeds, so it makes sense to use those
  if they exist, rather than feed IDs. The ID is pretty meaningless and
  basically means you have to override every name to make it readable.
- Including the ID identifying the EmonCMS instance (i.e. the '1') makes
  the name clunkier and would only be useful for people with multiple
  EmonCMS instances, which is likely to be an extremely small group since
  one hub can run as many feeds as you need it to.

This changes the default behavior but still uses configured 'name' if
it's set, so it won't break the configuration of people who have
customized their feed names in HA config.
2017-10-22 12:12:36 +02:00
Maciej Bieniek
62caea6bfb fix temperature/humidity sensors valid values (#10024) 2017-10-22 11:59:24 +02:00
Abílio Costa
80053ef21b switch.flux: add interval and transition attributes (#9700) 2017-10-22 11:27:04 +02:00
Fabian Affolter
bd4304e838 Upgrade youtube_dl to 2017.10.20 (#10014) 2017-10-22 11:24:07 +02:00
Sebastian Muszynski
c08c8c7996 Xiaomi Aqara: New xiaomi wireless button (sensor_switch.aq3) introduced (#10008)
* New xiaomi wireless button (sensor_switch.aq3) introduced.

* The next version of PyXiaomiGateway (0.5.3) is needed.
2017-10-22 11:02:01 +02:00
Paulus Schoutsen
9d39a5ced3 Call correct script [skip ci] 2017-10-22 01:04:10 -07:00
Fabian Affolter
816b69c807 Upgrade mypy to 0.540 (#10013) 2017-10-22 00:45:40 -07:00
Alan Fischer
9f62d5e3cf Json api fix (#10017)
* Dont let json parsing errors result in a 500

* Fixed error description
2017-10-22 00:44:46 -07:00
Chris Kacerguis
796a3ff49d Added gaction script to support the new Google Assistant component in Docker (#10019)
* added gactions install script

* added gaction setup step

* added ability to not install gaction

* updated dev docker file
2017-10-22 08:09:49 +02:00
Hydreliox
089e1ab6f4 Add xy attribute to Yeelight (#9957)
Allows using light profiles with yeelight bulbs
2017-10-22 02:59:55 +02:00
Fabian Affolter
5ad715507b Merge branch 'master' into dev 2017-10-22 00:46:47 +02:00
Fabian Affolter
2ab14bbabc Bump dev to 0.57.0.dev0 (#10010) 2017-10-21 13:12:57 -07:00
Fabian Affolter
28b7a3da32 Renaming API.AI to Dialogflow (#10006)
* Rename API.AI to Dialogflow

* Rename API.AI to Dialogflow
2017-10-21 13:12:23 -07:00
milanvo
bf26b75d27 Change persistent notification to avoid long text in entity state (#9967)
* Change persistent notification to avoid long text in entity state

* Tests for changed persistent notification

* Persistent notification state

* Test for component state
2017-10-21 21:59:05 +02:00
Adam
3ea4691fce Fix spelling error (#10009) 2017-10-21 21:56:19 +02:00
Gerardo Castillo
5df985a510 Update the Russound RNET component to use enhanced Russound.py (#9739)
* Updated RussoundRNETDevice.update() to call and enhanced function that reduces network traffic

Refer to issue #6 on the Russound project

* Updated RussoundRNETDevice.update() to invoke an enhanced function to reduce network traffic

PLease see issue #6 on the russound project

* Updated REQUIREMENTS to use version 0.1.9 of the Russound component

Please refer to issue #6 on the Russound rnet project

* Corrected some minor style details to satisfy Houndbot

* Update requirements_all.txt
2017-10-21 14:56:37 -04:00
cgtobi
789929d445 Add support for multiple disks to be monitored. (#9977)
* Add support for multiple disks.

* Fix LINT error.

* Make disk config optional to not break existing installations.

* Change state handling as per request by @fabaff.
2017-10-21 20:45:53 +02:00
Marcelo Moreira de Mello
51a65ee8e9 Introducing Ring Door Bell Camera (including StickUp cameras) and WiFi sensors (#9962)
*   Extended Ring DoorBell to support camera playback and wifi sensors

   * Bump python-ringdoorbell to version 0.1.6
   * Support to camera playback via ffmpeg
   * Extended ringdoorbell sensors to report WiFi attributes
   * Extended unittests

* Makes lint happy

* Added support to stickup cameras and fixed logic

* Fixed unittests for stickup cameras

* Makes lint happy

* Refactored attributions and removed extra refresh method.
2017-10-21 16:08:40 +02:00
Martin Eberhardt
222cc4c393 Add optional attribute option to scrape sensor (#10001)
* Add optional attribute option to scrape sensor

* Rename attribute variable to attr
2017-10-21 16:03:29 +02:00
Pascal Vizeli
ce1a2cc2a6 Remove async_update (#9997) 2017-10-21 09:45:05 +02:00
Fabian Affolter
aab7442cc5 Upgrade gitterpy to 0.1.6 (#9983) 2017-10-20 20:28:34 +02:00
Fabian Affolter
4f1eab138c Upgrade speedtest-cli to 1.0.7 (#9984) 2017-10-20 20:28:11 +02:00
Kamil Warguła
53df3fadd7 Update screenshot-components.png file. (#9987) 2017-10-20 20:26:34 +02:00
William Scanlon
41c2bdb4fb Moved siren to Wink from switch (#9879) 2017-10-20 10:18:32 -04:00
Lewis Juggins
d16c5f9046 [tradfri] Update pytradfri, simplify dependencies. (#9875)
* Update pytradfri

* Process dep links

* Process dep links

* Process dep links

* Install all deps

* Update requirements

* Exclude aiocoap

* Install cython

* Remove cython

* Exclude DTLSSocket

* Add cython
2017-10-19 23:20:33 -07:00
TopdRob
29d4dca56a Update requests requirement (#9876)
* Update request requirement

Update request requirement from version v2.14.2 to v2.18.4

* Fix dependency vizio integration

3rd patry packages were removed from requests. Changed dependency from requests to urllib3

* forgot =

forgot = when adding the requirement

* re-run script/gen_requirements_all.py

re-run script/gen_requirements_all.py

* Unvendoring urllib3 from requests

In v2.16.0 and newer of requests they unverdored urllib3.

* undefined name 'InsecureRequestWarning'

* Removed requirement to 'urllib3==1.22

* removed import requests

* removed urllib3.exceptions.InsecureRequestWarning

removed urllib3.exceptions.InsecureRequestWarning travis lint
2017-10-19 19:24:49 -07:00
jbarrancos
a1d5daee53 Merge pull request #7 from jbarrancos/rainbird
Fixed comments from @fabaff
2017-10-18 12:58:04 +02:00
J.J.Barrancos
a628112e4c lint ws 2017-10-18 11:17:29 +02:00
J.J.Barrancos
778761ebce lint error 2017-10-18 10:40:38 +02:00
J.J.Barrancos
76a3a4892d Fix req 2017-10-18 10:18:37 +02:00
J.J.Barrancos
02f8779de8 Fixed comments from @fabaff
Fxied issues raides
2017-10-18 09:58:32 +02:00
jbarrancos
38e02a057d Merge pull request #6 from jbarrancos/rainbird
Removed requirement
2017-10-12 10:43:35 +02:00
J.J.Barrancos
fad9e607c3 Removed requirement 2017-10-12 10:22:22 +02:00
jbarrancos
47d8601f30 Merge pull request #5 from jbarrancos/rainbird
Rainbird
2017-10-11 17:24:07 +02:00
J.J.Barrancos
bddb424b0d Requirements updated 2017-10-11 17:01:14 +02:00
J.J.Barrancos
8db4b4f303 typo 2017-10-11 16:54:08 +02:00
J.J.Barrancos
cc4ec228b5 Removed requirement 2017-10-11 16:12:01 +02:00
jbarrancos
c6e6496000 Merge pull request #4 from jbarrancos/dev
Merge in latest
2017-10-11 16:05:54 +02:00
jbarrancos
2c9010d661 Merge pull request #3 from home-assistant/dev
Get latest
2017-10-11 16:04:37 +02:00
J.J.Barrancos
24826c2770 Revert "Dependency breaks build"
This reverts commit c1aaed250a.
2017-10-11 16:02:32 +02:00
J.J.Barrancos
c1aaed250a Dependency breaks build 2017-10-11 16:01:25 +02:00
J.J.Barrancos
59fcef39ff Split requirements per line 2017-10-11 15:56:18 +02:00
J.J.Barrancos
d0ff45500b Fixed dependency version 2017-10-11 15:45:07 +02:00
J.J.Barrancos
0ace832166 Requirements updated 2017-10-11 15:35:58 +02:00
J.J.Barrancos
7f97d166bf Added dependency on pycrypto
Crypto Dependency missing found on virtualenv install. Added to dependecy rainbird.py
2017-10-11 15:15:50 +02:00
jbarrancos
e9f36a7e45 Merge pull request #2 from jbarrancos/rainbird
Limited to switch on/off
2017-10-06 15:38:37 +02:00
J.J.Barrancos
f036bf9353 Limited to switch on/off
Limited to switch on/off
Lowered loglevel
2017-10-06 15:22:22 +02:00
jbarrancos
c95c8a04ef Merge pull request #1 from home-assistant/dev
Update Fork
2017-09-27 16:17:28 +02:00
J.J.Barrancos
d2d28fd419 Moved all code into the switch component
Per request moved all the code inside the switch
2017-08-30 16:11:40 +02:00
J.J.Barrancos
67007aed40 Updated requirements_all.txt 2017-08-29 14:08:55 +02:00
J.J.Barrancos
df1c3dfb67 Hound issue "whitespace" 2017-08-29 13:47:47 +02:00
J.J.Barrancos
689484216d Using latest module and fixed state issue
- pyrainbird 0.0.9 allows the override (if ever needed) connection retry/sleep
- Forces state towards the Entity when switching the switches. Gives better UI experience.
2017-08-29 13:45:18 +02:00
J.J.Barrancos
51c6029fe5 Fixed issue with missing key 2017-08-28 21:07:40 +02:00
J.J.Barrancos
0eee544d17 Changed component to use entity and switch
Changed component to use entity and switch.
2017-08-28 17:57:45 +02:00
J.J.Barrancos
21cca21124 Recommit corrected
Recommit corrected
2017-08-25 17:04:38 +02:00
J.J.Barrancos
f837451633 St*pid mistake.. wrong place 2017-08-25 16:59:37 +02:00
J.J.Barrancos
cce4a569e4 Changed doc and added default schema 2017-08-25 16:06:49 +02:00
J.J.Barrancos
4feea9d7ec pydocstyle fixes
pydocstyle fixes
2017-08-25 13:26:38 +02:00
J.J.Barrancos
7aff588bf0 pylint, coverage and requirement fix
pylint, coverage and requirement fix
2017-08-25 12:01:32 +02:00
jbarrancos
41a046a69d Typo 2017-08-25 00:08:40 +02:00
jbarrancos
e548bd5312 PIP8 Fixes
PIP8 Fixes
2017-08-24 22:49:54 +02:00
jbarrancos
88098283c7 Markup fixes
Markup fixes
2017-08-24 22:40:45 +02:00
jbarrancos
6c6ed29329 Fixed some markup and removed not yet needed class
Fixed issues during hound run (markup) and a class which is obsolete
2017-08-24 22:35:30 +02:00
jbarrancos
c4f4e492e5 Removed some whitelines
Removed some whitelines
2017-08-24 18:41:25 +02:00
jbarrancos
c286e2c434 Rainbird WiFi LNK Irrigation Implementation
This is a component which adds support for the Rainbird WiFi LNK Irragation system.
2017-08-24 18:30:36 +02:00
930 changed files with 40458 additions and 15968 deletions

View File

@@ -11,6 +11,9 @@ omit =
homeassistant/components/abode.py
homeassistant/components/*/abode.py
homeassistant/components/ads/__init__.py
homeassistant/components/*/ads.py
homeassistant/components/alarmdecoder.py
homeassistant/components/*/alarmdecoder.py
@@ -47,12 +50,20 @@ omit =
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py
homeassistant/components/coinbase.py
homeassistant/components/sensor/coinbase.py
homeassistant/components/comfoconnect.py
homeassistant/components/*/comfoconnect.py
homeassistant/components/deconz/*
homeassistant/components/*/deconz.py
homeassistant/components/digital_ocean.py
homeassistant/components/*/digital_ocean.py
homeassistant/components/dominos.py
homeassistant/components/doorbird.py
homeassistant/components/*/doorbird.py
@@ -71,13 +82,19 @@ omit =
homeassistant/components/envisalink.py
homeassistant/components/*/envisalink.py
homeassistant/components/gc100.py
homeassistant/components/*/gc100.py
homeassistant/components/google.py
homeassistant/components/*/google.py
homeassistant/components/hdmi_cec.py
homeassistant/components/*/hdmi_cec.py
homeassistant/components/homematic.py
homeassistant/components/hive.py
homeassistant/components/*/hive.py
homeassistant/components/homematic/__init__.py
homeassistant/components/*/homematic.py
homeassistant/components/insteon_local.py
@@ -107,6 +124,9 @@ omit =
homeassistant/components/lametric.py
homeassistant/components/*/lametric.py
homeassistant/components/linode.py
homeassistant/components/*/linode.py
homeassistant/components/lutron.py
homeassistant/components/*/lutron.py
@@ -176,6 +196,9 @@ omit =
homeassistant/components/tado.py
homeassistant/components/*/tado.py
homeassistant/components/tahoma.py
homeassistant/components/*/tahoma.py
homeassistant/components/tellduslive.py
homeassistant/components/*/tellduslive.py
@@ -246,9 +269,14 @@ omit =
homeassistant/components/zoneminder.py
homeassistant/components/*/zoneminder.py
homeassistant/components/daikin.py
homeassistant/components/*/daikin.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/canary.py
homeassistant/components/alarm_control_panel/concord232.py
homeassistant/components/alarm_control_panel/egardia.py
homeassistant/components/alarm_control_panel/ialarm.py
homeassistant/components/alarm_control_panel/manual_mqtt.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/alarm_control_panel/simplisafe.py
@@ -265,24 +293,31 @@ omit =
homeassistant/components/binary_sensor/rest.py
homeassistant/components/binary_sensor/tapsaff.py
homeassistant/components/browser.py
homeassistant/components/calendar/caldav.py
homeassistant/components/calendar/todoist.py
homeassistant/components/camera/bloomsky.py
homeassistant/components/camera/canary.py
homeassistant/components/camera/ffmpeg.py
homeassistant/components/camera/foscam.py
homeassistant/components/camera/mjpeg.py
homeassistant/components/camera/rpi_camera.py
homeassistant/components/camera/onvif.py
homeassistant/components/camera/ring.py
homeassistant/components/camera/rpi_camera.py
homeassistant/components/camera/synology.py
homeassistant/components/camera/yi.py
homeassistant/components/climate/econet.py
homeassistant/components/climate/ephember.py
homeassistant/components/climate/eq3btsmart.py
homeassistant/components/climate/flexit.py
homeassistant/components/climate/heatmiser.py
homeassistant/components/climate/homematic.py
homeassistant/components/climate/honeywell.py
homeassistant/components/climate/knx.py
homeassistant/components/climate/oem.py
homeassistant/components/climate/proliphix.py
homeassistant/components/climate/radiotherm.py
homeassistant/components/climate/sensibo.py
homeassistant/components/climate/touchline.py
homeassistant/components/cover/garadget.py
homeassistant/components/cover/homematic.py
homeassistant/components/cover/knx.py
@@ -301,6 +336,7 @@ omit =
homeassistant/components/device_tracker/cisco_ios.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/gpslogger.py
homeassistant/components/device_tracker/hitron_coda.py
homeassistant/components/device_tracker/huawei_router.py
homeassistant/components/device_tracker/icloud.py
homeassistant/components/device_tracker/keenetic_ndms2.py
@@ -314,9 +350,10 @@ omit =
homeassistant/components/device_tracker/sky_hub.py
homeassistant/components/device_tracker/snmp.py
homeassistant/components/device_tracker/swisscom.py
homeassistant/components/device_tracker/thomson.py
homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/tado.py
homeassistant/components/device_tracker/thomson.py
homeassistant/components/device_tracker/tile.py
homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/tplink.py
homeassistant/components/device_tracker/trackr.py
homeassistant/components/device_tracker/ubus.py
@@ -324,6 +361,7 @@ omit =
homeassistant/components/emoncms_history.py
homeassistant/components/emulated_hue/upnp.py
homeassistant/components/fan/mqtt.py
homeassistant/components/fan/xiaomi_miio.py
homeassistant/components/feedreader.py
homeassistant/components/foursquare.py
homeassistant/components/ifttt.py
@@ -333,20 +371,22 @@ omit =
homeassistant/components/keyboard.py
homeassistant/components/keyboard_remote.py
homeassistant/components/light/avion.py
homeassistant/components/light/blinkt.py
homeassistant/components/light/blinksticklight.py
homeassistant/components/light/blinkt.py
homeassistant/components/light/decora.py
homeassistant/components/light/decora_wifi.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/greenwave.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
homeassistant/components/light/iglo.py
homeassistant/components/light/lifx.py
homeassistant/components/light/lifx_legacy.py
homeassistant/components/light/limitlessled.py
homeassistant/components/light/mystrom.py
homeassistant/components/light/osramlightify.py
homeassistant/components/light/rpi_gpio_pwm.py
homeassistant/components/light/piglow.py
homeassistant/components/light/rpi_gpio_pwm.py
homeassistant/components/light/sensehat.py
homeassistant/components/light/tikteck.py
homeassistant/components/light/tplink.py
@@ -357,9 +397,9 @@ omit =
homeassistant/components/light/yeelightsunflower.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/lockitron.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
@@ -401,20 +441,22 @@ omit =
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/spotify.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/ue_smart_radio.py
homeassistant/components/media_player/vizio.py
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/media_player/ziggo_mediabox_xl.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/clickatell.py
homeassistant/components/notify/clicksend.py
homeassistant/components/notify/clicksend_tts.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
@@ -444,13 +486,17 @@ omit =
homeassistant/components/notify/telstra.py
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/notify/yessssms.py
homeassistant/components/nuimo_controller.py
homeassistant/components/prometheus.py
homeassistant/components/rainbird.py
homeassistant/components/remember_the_milk/__init__.py
homeassistant/components/remote/harmony.py
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/alpha_vantage.py
homeassistant/components/sensor/arest.py
homeassistant/components/sensor/arwn.py
homeassistant/components/sensor/bbox.py
@@ -461,17 +507,18 @@ omit =
homeassistant/components/sensor/bom.py
homeassistant/components/sensor/broadlink.py
homeassistant/components/sensor/buienradar.py
homeassistant/components/sensor/citybikes.py
homeassistant/components/sensor/coinmarketcap.py
homeassistant/components/sensor/cert_expiry.py
homeassistant/components/sensor/citybikes.py
homeassistant/components/sensor/comed_hourly_pricing.py
homeassistant/components/sensor/cpuspeed.py
homeassistant/components/sensor/crimereports.py
homeassistant/components/sensor/cups.py
homeassistant/components/sensor/currencylayer.py
homeassistant/components/sensor/darksky.py
homeassistant/components/sensor/deluge.py
homeassistant/components/sensor/deutsche_bahn.py
homeassistant/components/sensor/dht.py
homeassistant/components/sensor/discogs.py
homeassistant/components/sensor/dnsip.py
homeassistant/components/sensor/dovado.py
homeassistant/components/sensor/dte_energy_bridge.py
@@ -485,11 +532,11 @@ omit =
homeassistant/components/sensor/etherscan.py
homeassistant/components/sensor/fastdotcom.py
homeassistant/components/sensor/fedex.py
homeassistant/components/sensor/fido.py
homeassistant/components/sensor/fitbit.py
homeassistant/components/sensor/fixer.py
homeassistant/components/sensor/fritzbox_callmonitor.py
homeassistant/components/sensor/fritzbox_netmonitor.py
homeassistant/components/sensor/gearbest.py
homeassistant/components/sensor/geizhals.py
homeassistant/components/sensor/gitter.py
homeassistant/components/sensor/glances.py
@@ -497,17 +544,18 @@ omit =
homeassistant/components/sensor/gpsd.py
homeassistant/components/sensor/gtfs.py
homeassistant/components/sensor/haveibeenpwned.py
homeassistant/components/sensor/hddtemp.py
homeassistant/components/sensor/hp_ilo.py
homeassistant/components/sensor/htu21d.py
homeassistant/components/sensor/hydroquebec.py
homeassistant/components/sensor/imap.py
homeassistant/components/sensor/imap_email_content.py
homeassistant/components/sensor/influxdb.py
homeassistant/components/sensor/irish_rail_transport.py
homeassistant/components/sensor/kwb.py
homeassistant/components/sensor/lacrosse.py
homeassistant/components/sensor/lastfm.py
homeassistant/components/sensor/linux_battery.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/luftdaten.py
homeassistant/components/sensor/lyft.py
homeassistant/components/sensor/metoffice.py
homeassistant/components/sensor/miflora.py
@@ -515,6 +563,7 @@ omit =
homeassistant/components/sensor/mopar.py
homeassistant/components/sensor/mqtt_room.py
homeassistant/components/sensor/mvglive.py
homeassistant/components/sensor/nederlandse_spoorwegen.py
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nut.py
@@ -531,8 +580,10 @@ omit =
homeassistant/components/sensor/pocketcasts.py
homeassistant/components/sensor/pushbullet.py
homeassistant/components/sensor/pvoutput.py
homeassistant/components/sensor/pyload.py
homeassistant/components/sensor/qnap.py
homeassistant/components/sensor/radarr.py
homeassistant/components/sensor/rainbird.py
homeassistant/components/sensor/ripple.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/sensor/scrape.py
@@ -543,6 +594,7 @@ omit =
homeassistant/components/sensor/skybeacon.py
homeassistant/components/sensor/sma.py
homeassistant/components/sensor/snmp.py
homeassistant/components/sensor/sochain.py
homeassistant/components/sensor/sonarr.py
homeassistant/components/sensor/speedtest.py
homeassistant/components/sensor/steam_online.py
@@ -551,6 +603,7 @@ omit =
homeassistant/components/sensor/swiss_public_transport.py
homeassistant/components/sensor/synologydsm.py
homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/sytadin.py
homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/temper.py
@@ -564,11 +617,12 @@ omit =
homeassistant/components/sensor/upnp.py
homeassistant/components/sensor/ups.py
homeassistant/components/sensor/vasttrafik.py
homeassistant/components/sensor/viaggiatreno.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/whois.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
homeassistant/components/shiftr.py
homeassistant/components/spc.py
@@ -576,6 +630,7 @@ omit =
homeassistant/components/switch/anel_pwrctrl.py
homeassistant/components/switch/arest.py
homeassistant/components/switch/broadlink.py
homeassistant/components/switch/deluge.py
homeassistant/components/switch/digitalloggers.py
homeassistant/components/switch/dlink.py
homeassistant/components/switch/edimax.py
@@ -588,29 +643,32 @@ omit =
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pilight.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rainbird.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/snmp.py
homeassistant/components/switch/telnet.py
homeassistant/components/switch/tplink.py
homeassistant/components/switch/transmission.py
homeassistant/components/switch/wake_on_lan.py
homeassistant/components/switch/xiaomi_miio.py
homeassistant/components/telegram_bot/*
homeassistant/components/thingspeak.py
homeassistant/components/tts/amazon_polly.py
homeassistant/components/tts/baidu.py
homeassistant/components/tts/microsoft.py
homeassistant/components/tts/picotts.py
homeassistant/components/vacuum/mqtt.py
homeassistant/components/vacuum/roomba.py
homeassistant/components/vacuum/xiaomi_miio.py
homeassistant/components/weather/bom.py
homeassistant/components/weather/buienradar.py
homeassistant/components/weather/darksky.py
homeassistant/components/weather/metoffice.py
homeassistant/components/weather/openweathermap.py
homeassistant/components/weather/yweather.py
homeassistant/components/weather/zamg.py
homeassistant/components/zeroconf.py
homeassistant/components/zwave/util.py
homeassistant/components/vacuum/mqtt.py
[report]
# Regexes for lines to exclude from consideration

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
# Ensure Docker script files uses LF to support Docker for Windows.
setup_docker_prereqs eol=lf
/virtualization/Docker/scripts/* eol=lf

View File

@@ -11,6 +11,7 @@
```
## Checklist:
- [ ] The code change is tested and works locally.
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [home-assistant.github.io](https://github.com/home-assistant/home-assistant.github.io)

3
.gitignore vendored
View File

@@ -74,6 +74,7 @@ pip-selfcheck.json
venv
.venv
Pipfile*
share/*
# vimmy stuff
*.swp
@@ -96,4 +97,4 @@ docs/build
desktop.ini
/home-assistant.pyproj
/home-assistant.sln
/.vs/home-assistant/v14
/.vs/*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "homeassistant/components/frontend/www_static/home-assistant-polymer"]
path = homeassistant/components/frontend/www_static/home-assistant-polymer
url = https://github.com/home-assistant/home-assistant-polymer.git

View File

@@ -8,18 +8,18 @@ matrix:
include:
- python: "3.4.2"
env: TOXENV=lint
- python: "3.4.2"
env: TOXENV=pylint
- python: "3.4.2"
env: TOXENV=py34
# - python: "3.5"
# env: TOXENV=typing
- python: "3.5"
- python: "3.5.3"
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
# - python: "3.6-dev"
# env: TOXENV=py36
- python: "3.4.2"
env: TOXENV=requirements
# allow_failures:
# - python: "3.5"
# env: TOXENV=typing
@@ -29,5 +29,5 @@ cache:
- $HOME/.cache/pip
install: pip install -U tox coveralls
language: python
script: travis_wait tox
script: travis_wait 30 tox --develop
after_success: coveralls

View File

@@ -41,26 +41,46 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave
# Indiviudal components
homeassistant/components/alarm_control_panel/egardia.py @jeroenterheerdt
homeassistant/components/camera/yi.py @bachya
homeassistant/components/climate/ephember.py @ttroy50
homeassistant/components/climate/eq3btsmart.py @rytilahti
homeassistant/components/climate/sensibo.py @andrey-git
homeassistant/components/cover/template.py @PhracturedBlue
homeassistant/components/device_tracker/automatic.py @armills
homeassistant/components/device_tracker/tile.py @bachya
homeassistant/components/history_graph.py @andrey-git
homeassistant/components/light/tplink.py @rytilahti
homeassistant/components/light/yeelight.py @rytilahti
homeassistant/components/media_player/kodi.py @armills
homeassistant/components/media_player/monoprice.py @etsinko
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
homeassistant/components/plant.py @ChristianKuehnel
homeassistant/components/sensor/airvisual.py @bachya
homeassistant/components/sensor/miflora.py @danielhiversen
homeassistant/components/sensor/gearbest.py @HerrHofrat
homeassistant/components/sensor/irish_rail_transport.py @ttroy50
homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel
homeassistant/components/sensor/sytadin.py @gautric
homeassistant/components/sensor/tibber.py @danielhiversen
homeassistant/components/sensor/waqi.py @andrey-git
homeassistant/components/switch/rainmachine.py @bachya
homeassistant/components/switch/tplink.py @rytilahti
homeassistant/components/xiaomi_aqara.py @danielhiversen @syssi
homeassistant/components/*/axis.py @Kane610
homeassistant/components/*/axis.py @kane610
homeassistant/components/*/broadlink.py @danielhiversen
homeassistant/components/hive.py @Rendili @KJonline
homeassistant/components/*/hive.py @Rendili @KJonline
homeassistant/components/*/deconz.py @kane610
homeassistant/components/*/rfxtrx.py @danielhiversen
homeassistant/components/velux.py @Julius2342
homeassistant/components/*/velux.py @Julius2342
homeassistant/components/knx.py @Julius2342
homeassistant/components/*/knx.py @Julius2342
homeassistant/components/tahoma.py @philklei
homeassistant/components/*/tahoma.py @philklei
homeassistant/components/tesla.py @zabuldon
homeassistant/components/*/tesla.py @zabuldon
homeassistant/components/*/xiaomi_aqara.py @danielhiversen
homeassistant/components/*/xiaomi_miio.py @rytilahti
homeassistant/components/tellduslive.py @molobrakos @fredrike
homeassistant/components/*/tellduslive.py @molobrakos @fredrike
homeassistant/components/*/tradfri.py @ggravlingen
homeassistant/components/*/xiaomi_aqara.py @danielhiversen @syssi
homeassistant/components/*/xiaomi_miio.py @rytilahti @syssi

View File

@@ -3,7 +3,7 @@
# This way, the development image and the production image are kept in sync.
FROM python:3.6
MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
LABEL maintainer="Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>"
# Uncomment any of the following lines to disable the installation.
#ENV INSTALL_TELLSTICK no

View File

@@ -1,5 +1,4 @@
include README.rst
include LICENSE.md
graft homeassistant
prune homeassistant/components/frontend/www_static/home-assistant-polymer
recursive-exclude * *.py[co]

BIN
docs/screenshot-components.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -19,15 +19,13 @@
#
import sys
import os
from os.path import relpath
import inspect
from homeassistant.const import (__version__, __short_version__, PROJECT_NAME,
PROJECT_LONG_DESCRIPTION,
PROJECT_COPYRIGHT, PROJECT_AUTHOR,
PROJECT_GITHUB_USERNAME,
PROJECT_GITHUB_REPOSITORY,
GITHUB_PATH, GITHUB_URL)
from homeassistant.const import __version__, __short_version__
from setup import (
PROJECT_NAME, PROJECT_LONG_DESCRIPTION, PROJECT_COPYRIGHT, PROJECT_AUTHOR,
PROJECT_GITHUB_USERNAME, PROJECT_GITHUB_REPOSITORY, GITHUB_PATH,
GITHUB_URL)
sys.path.insert(0, os.path.abspath('_ext'))
sys.path.insert(0, os.path.abspath('../homeassistant'))
@@ -87,9 +85,7 @@ edit_on_github_src_path = 'docs/source/'
def linkcode_resolve(domain, info):
"""
Determine the URL corresponding to Python object
"""
"""Determine the URL corresponding to Python object."""
if domain != 'py':
return None
modname = info['module']

View File

@@ -230,7 +230,7 @@ def closefds_osx(min_fd: int, max_fd: int) -> None:
def cmdline() -> List[str]:
"""Collect path and arguments to re-execute the current hass instance."""
if sys.argv[0].endswith(os.path.sep + '__main__.py'):
if os.path.basename(sys.argv[0]) == '__main__.py':
modulepath = os.path.dirname(sys.argv[0])
os.environ['PYTHONPATH'] = os.path.dirname(modulepath)
return [sys.executable] + [arg for arg in sys.argv if

View File

@@ -30,8 +30,8 @@ ERROR_LOG_FILENAME = 'home-assistant.log'
DATA_LOGGING = 'logging'
FIRST_INIT_COMPONENT = set((
'recorder', 'mqtt', 'mqtt_eventstream', 'logger', 'introduction',
'frontend', 'history'))
'system_log', 'recorder', 'mqtt', 'mqtt_eventstream', 'logger',
'introduction', 'frontend', 'history'))
def from_config_dict(config: Dict[str, Any],
@@ -88,7 +88,7 @@ def async_from_config_dict(config: Dict[str, Any],
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 '
'the beginning of 2018. Please upgrade Python or your operating '
'system. More info: https://home-assistant.io/blog/2017/10/06/'
'deprecating-python-3.4-support/'
)

View File

@@ -10,7 +10,6 @@ Component design guidelines:
import asyncio
import itertools as it
import logging
import os
import homeassistant.core as ha
import homeassistant.config as conf_util
@@ -111,11 +110,6 @@ def async_reload_core_config(hass):
@asyncio.coroutine
def async_setup(hass, config):
"""Set up general services related to Home Assistant."""
descriptions = yield from hass.async_add_job(
conf_util.load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml')
)
@asyncio.coroutine
def async_handle_turn_service(service):
"""Handle calls to homeassistant.turn_on/off."""
@@ -155,14 +149,11 @@ def async_setup(hass, config):
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service,
descriptions[ha.DOMAIN][SERVICE_TURN_OFF])
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service,
descriptions[ha.DOMAIN][SERVICE_TURN_ON])
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service,
descriptions[ha.DOMAIN][SERVICE_TOGGLE])
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service)
@asyncio.coroutine
def async_handle_core_service(call):
@@ -187,14 +178,11 @@ def async_setup(hass, config):
hass.async_add_job(hass.async_stop(RESTART_EXIT_CODE))
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service,
descriptions[ha.DOMAIN][SERVICE_HOMEASSISTANT_STOP])
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service,
descriptions[ha.DOMAIN][SERVICE_HOMEASSISTANT_RESTART])
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service,
descriptions[ha.DOMAIN][SERVICE_CHECK_CONFIG])
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service)
@asyncio.coroutine
def async_handle_reload_config(call):
@@ -209,7 +197,6 @@ def async_setup(hass, config):
hass, conf.get(ha.DOMAIN) or {})
hass.services.async_register(
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config,
descriptions[ha.DOMAIN][SERVICE_RELOAD_CORE_CONFIG])
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config)
return True

View File

@@ -7,11 +7,9 @@ https://home-assistant.io/components/abode/
import asyncio
import logging
from functools import partial
from os import path
import voluptuous as vol
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, CONF_LIGHTS,
@@ -21,7 +19,7 @@ from homeassistant.helpers import discovery
from homeassistant.helpers.entity import Entity
from requests.exceptions import HTTPError, ConnectTimeout
REQUIREMENTS = ['abodepy==0.12.1']
REQUIREMENTS = ['abodepy==0.12.2']
_LOGGER = logging.getLogger(__name__)
@@ -188,22 +186,16 @@ def setup_hass_services(hass):
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)

View File

@@ -0,0 +1,210 @@
"""
ADS Component.
For more details about this component, please refer to the documentation.
https://home-assistant.io/components/ads/
"""
import threading
import struct
import logging
import ctypes
from collections import namedtuple
import voluptuous as vol
from homeassistant.const import CONF_DEVICE, CONF_PORT, CONF_IP_ADDRESS, \
EVENT_HOMEASSISTANT_STOP
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyads==2.2.6']
_LOGGER = logging.getLogger(__name__)
DATA_ADS = 'data_ads'
# Supported Types
ADSTYPE_INT = 'int'
ADSTYPE_UINT = 'uint'
ADSTYPE_BYTE = 'byte'
ADSTYPE_BOOL = 'bool'
DOMAIN = 'ads'
# config variable names
CONF_ADS_VAR = 'adsvar'
CONF_ADS_VAR_BRIGHTNESS = 'adsvar_brightness'
CONF_ADS_TYPE = 'adstype'
CONF_ADS_FACTOR = 'factor'
CONF_ADS_VALUE = 'value'
SERVICE_WRITE_DATA_BY_NAME = 'write_data_by_name'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_DEVICE): cv.string,
vol.Required(CONF_PORT): cv.port,
vol.Optional(CONF_IP_ADDRESS): cv.string,
})
}, extra=vol.ALLOW_EXTRA)
SCHEMA_SERVICE_WRITE_DATA_BY_NAME = vol.Schema({
vol.Required(CONF_ADS_VAR): cv.string,
vol.Required(CONF_ADS_TYPE): vol.In([ADSTYPE_INT, ADSTYPE_UINT,
ADSTYPE_BYTE]),
vol.Required(CONF_ADS_VALUE): cv.match_all
})
def setup(hass, config):
"""Set up the ADS component."""
import pyads
conf = config[DOMAIN]
# get ads connection parameters from config
net_id = conf.get(CONF_DEVICE)
ip_address = conf.get(CONF_IP_ADDRESS)
port = conf.get(CONF_PORT)
# create a new ads connection
client = pyads.Connection(net_id, port, ip_address)
# add some constants to AdsHub
AdsHub.ADS_TYPEMAP = {
ADSTYPE_BOOL: pyads.PLCTYPE_BOOL,
ADSTYPE_BYTE: pyads.PLCTYPE_BYTE,
ADSTYPE_INT: pyads.PLCTYPE_INT,
ADSTYPE_UINT: pyads.PLCTYPE_UINT,
}
AdsHub.PLCTYPE_BOOL = pyads.PLCTYPE_BOOL
AdsHub.PLCTYPE_BYTE = pyads.PLCTYPE_BYTE
AdsHub.PLCTYPE_INT = pyads.PLCTYPE_INT
AdsHub.PLCTYPE_UINT = pyads.PLCTYPE_UINT
AdsHub.ADSError = pyads.ADSError
# connect to ads client and try to connect
try:
ads = AdsHub(client)
except pyads.pyads.ADSError:
_LOGGER.error(
'Could not connect to ADS host (netid=%s, port=%s)', net_id, port
)
return False
# add ads hub to hass data collection, listen to shutdown
hass.data[DATA_ADS] = ads
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, ads.shutdown)
def handle_write_data_by_name(call):
"""Write a value to the connected ADS device."""
ads_var = call.data.get(CONF_ADS_VAR)
ads_type = call.data.get(CONF_ADS_TYPE)
value = call.data.get(CONF_ADS_VALUE)
try:
ads.write_by_name(ads_var, value, ads.ADS_TYPEMAP[ads_type])
except pyads.ADSError as err:
_LOGGER.error(err)
hass.services.register(
DOMAIN, SERVICE_WRITE_DATA_BY_NAME, handle_write_data_by_name,
schema=SCHEMA_SERVICE_WRITE_DATA_BY_NAME
)
return True
# tuple to hold data needed for notification
NotificationItem = namedtuple(
'NotificationItem', 'hnotify huser name plc_datatype callback'
)
class AdsHub:
"""Representation of a PyADS connection."""
def __init__(self, ads_client):
"""Initialize the ADS Hub."""
self._client = ads_client
self._client.open()
# all ADS devices are registered here
self._devices = []
self._notification_items = {}
self._lock = threading.Lock()
def shutdown(self, *args, **kwargs):
"""Shutdown ADS connection."""
_LOGGER.debug('Shutting down ADS')
for notification_item in self._notification_items.values():
self._client.del_device_notification(
notification_item.hnotify,
notification_item.huser
)
_LOGGER.debug(
'Deleting device notification %d, %d',
notification_item.hnotify, notification_item.huser
)
self._client.close()
def register_device(self, device):
"""Register a new device."""
self._devices.append(device)
def write_by_name(self, name, value, plc_datatype):
"""Write a value to the device."""
with self._lock:
return self._client.write_by_name(name, value, plc_datatype)
def read_by_name(self, name, plc_datatype):
"""Read a value from the device."""
with self._lock:
return self._client.read_by_name(name, plc_datatype)
def add_device_notification(self, name, plc_datatype, callback):
"""Add a notification to the ADS devices."""
from pyads import NotificationAttrib
attr = NotificationAttrib(ctypes.sizeof(plc_datatype))
with self._lock:
hnotify, huser = self._client.add_device_notification(
name, attr, self._device_notification_callback
)
hnotify = int(hnotify)
_LOGGER.debug(
'Added Device Notification %d for variable %s', hnotify, name
)
self._notification_items[hnotify] = NotificationItem(
hnotify, huser, name, plc_datatype, callback
)
def _device_notification_callback(self, addr, notification, huser):
"""Handle device notifications."""
contents = notification.contents
hnotify = int(contents.hNotification)
_LOGGER.debug('Received Notification %d', hnotify)
data = contents.data
try:
notification_item = self._notification_items[hnotify]
except KeyError:
_LOGGER.debug('Unknown Device Notification handle: %d', hnotify)
return
# parse data to desired datatype
if notification_item.plc_datatype == self.PLCTYPE_BOOL:
value = bool(struct.unpack('<?', bytearray(data)[:1])[0])
elif notification_item.plc_datatype == self.PLCTYPE_INT:
value = struct.unpack('<h', bytearray(data)[:2])[0]
elif notification_item.plc_datatype == self.PLCTYPE_BYTE:
value = struct.unpack('<B', bytearray(data)[:1])[0]
elif notification_item.plc_datatype == self.PLCTYPE_UINT:
value = struct.unpack('<H', bytearray(data)[:2])[0]
else:
value = bytearray(data)
_LOGGER.warning('No callback available for this datatype.')
# execute callback
notification_item.callback(notification_item.name, value)

View File

@@ -0,0 +1,15 @@
# Describes the format for available ADS services
write_data_by_name:
description: Write a value to the connected ADS device.
fields:
adsvar:
description: The name of the variable to write to.
example: '.global_var'
adstype:
description: The data type of the variable to write to.
example: 'int'
value:
description: The value to write to the variable.
example: 1

View File

@@ -7,15 +7,13 @@ https://home-assistant.io/components/alarm_control_panel/
import asyncio
from datetime import timedelta
import logging
import os
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_ARM_NIGHT)
from homeassistant.config import load_yaml_config_file
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_CUSTOM_BYPASS)
from homeassistant.loader import bind_hass
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
@@ -33,6 +31,7 @@ SERVICE_TO_METHOD = {
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
SERVICE_ALARM_ARM_NIGHT: 'alarm_arm_night',
SERVICE_ALARM_ARM_CUSTOM_BYPASS: 'alarm_arm_custom_bypass',
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
}
@@ -107,6 +106,18 @@ def alarm_trigger(hass, code=None, entity_id=None):
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
@bind_hass
def alarm_arm_custom_bypass(hass, code=None, entity_id=None):
"""Send the alarm the command for arm custom bypass."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_CUSTOM_BYPASS, data)
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for sensors."""
@@ -135,14 +146,10 @@ def async_setup(hass, config):
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
for service in SERVICE_TO_METHOD:
hass.services.async_register(
DOMAIN, service, async_alarm_service_handler,
descriptions.get(service), schema=ALARM_SERVICE_SCHEMA)
schema=ALARM_SERVICE_SCHEMA)
return True
@@ -216,6 +223,17 @@ class AlarmControlPanel(Entity):
"""
return self.hass.async_add_job(self.alarm_trigger, code)
def alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command."""
raise NotImplementedError()
def async_alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_arm_custom_bypass, code)
@property
def state_attributes(self):
"""Return the state attributes."""

View File

@@ -7,69 +7,86 @@ https://home-assistant.io/components/alarm_control_panel.alarmdecoder/
import asyncio
import logging
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarmdecoder import (DATA_AD,
SIGNAL_PANEL_MESSAGE)
import homeassistant.helpers.config_validation as cv
from homeassistant.components.alarmdecoder import (
DATA_AD, SIGNAL_PANEL_MESSAGE)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN, STATE_ALARM_TRIGGERED)
ATTR_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['alarmdecoder']
SERVICE_ALARM_TOGGLE_CHIME = 'alarmdecoder_alarm_toggle_chime'
ALARM_TOGGLE_CHIME_SCHEMA = vol.Schema({
vol.Required(ATTR_CODE): cv.string,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up for AlarmDecoder alarm panels."""
_LOGGER.debug("AlarmDecoderAlarmPanel: setup")
device = AlarmDecoderAlarmPanel()
add_devices([device])
device = AlarmDecoderAlarmPanel("Alarm Panel", hass)
def alarm_toggle_chime_handler(service):
"""Register toggle chime handler."""
code = service.data.get(ATTR_CODE)
device.alarm_toggle_chime(code)
async_add_devices([device])
return True
hass.services.register(
alarm.DOMAIN, SERVICE_ALARM_TOGGLE_CHIME, alarm_toggle_chime_handler,
schema=ALARM_TOGGLE_CHIME_SCHEMA)
class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
"""Representation of an AlarmDecoder-based alarm panel."""
def __init__(self, name, hass):
def __init__(self):
"""Initialize the alarm panel."""
self._display = ""
self._name = name
self._state = STATE_UNKNOWN
_LOGGER.debug("Setting up panel")
self._name = "Alarm Panel"
self._state = None
self._ac_power = None
self._backlight_on = None
self._battery_low = None
self._check_zone = None
self._chime = None
self._entry_delay_off = None
self._programming_mode = None
self._ready = None
self._zone_bypassed = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback)
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_PANEL_MESSAGE, self._message_callback)
@callback
def _message_callback(self, message):
if message.alarm_sounding or message.fire_alarm:
if self._state != STATE_ALARM_TRIGGERED:
self._state = STATE_ALARM_TRIGGERED
self.async_schedule_update_ha_state()
self._state = STATE_ALARM_TRIGGERED
elif message.armed_away:
if self._state != STATE_ALARM_ARMED_AWAY:
self._state = STATE_ALARM_ARMED_AWAY
self.async_schedule_update_ha_state()
self._state = STATE_ALARM_ARMED_AWAY
elif message.armed_home:
if self._state != STATE_ALARM_ARMED_HOME:
self._state = STATE_ALARM_ARMED_HOME
self.async_schedule_update_ha_state()
self._state = STATE_ALARM_ARMED_HOME
else:
if self._state != STATE_ALARM_DISARMED:
self._state = STATE_ALARM_DISARMED
self.async_schedule_update_ha_state()
self._state = STATE_ALARM_DISARMED
self._ac_power = message.ac_power
self._backlight_on = message.backlight_on
self._battery_low = message.battery_low
self._check_zone = message.check_zone
self._chime = message.chime_on
self._entry_delay_off = message.entry_delay_off
self._programming_mode = message.programming_mode
self._ready = message.ready
self._zone_bypassed = message.zone_bypassed
self.schedule_update_ha_state()
@property
def name(self):
@@ -91,26 +108,37 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
"""Return the state of the device."""
return self._state
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'ac_power': self._ac_power,
'backlight_on': self._backlight_on,
'battery_low': self._battery_low,
'check_zone': self._check_zone,
'chime': self._chime,
'entry_delay_off': self._entry_delay_off,
'programming_mode': self._programming_mode,
'ready': self._ready,
'zone_bypassed': self._zone_bypassed
}
def alarm_disarm(self, code=None):
"""Send disarm command."""
_LOGGER.debug("alarm_disarm: %s", code)
if code:
_LOGGER.debug("alarm_disarm: sending %s1", str(code))
self.hass.data[DATA_AD].send("{!s}1".format(code))
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
_LOGGER.debug("alarm_arm_away: %s", code)
if code:
_LOGGER.debug("alarm_arm_away: sending %s2", str(code))
self.hass.data[DATA_AD].send("{!s}2".format(code))
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
def alarm_arm_home(self, code=None):
"""Send arm home command."""
_LOGGER.debug("alarm_arm_home: %s", code)
if code:
_LOGGER.debug("alarm_arm_home: sending %s3", str(code))
self.hass.data[DATA_AD].send("{!s}3".format(code))
def alarm_toggle_chime(self, code=None):
"""Send toggle chime command."""
if code:
self.hass.data[DATA_AD].send("{!s}9".format(code))

View File

@@ -22,6 +22,7 @@ _LOGGER = logging.getLogger(__name__)
ARMED = 'armed'
CONF_HOME_MODE_NAME = 'home_mode_name'
CONF_AWAY_MODE_NAME = 'away_mode_name'
DEPENDENCIES = ['arlo']
@@ -31,6 +32,7 @@ ICON = 'mdi:security'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOME_MODE_NAME, default=ARMED): cv.string,
vol.Optional(CONF_AWAY_MODE_NAME, default=ARMED): cv.string,
})
@@ -43,19 +45,22 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
return
home_mode_name = config.get(CONF_HOME_MODE_NAME)
away_mode_name = config.get(CONF_AWAY_MODE_NAME)
base_stations = []
for base_station in data.base_stations:
base_stations.append(ArloBaseStation(base_station, home_mode_name))
base_stations.append(ArloBaseStation(base_station, home_mode_name,
away_mode_name))
async_add_devices(base_stations, True)
class ArloBaseStation(AlarmControlPanel):
"""Representation of an Arlo Alarm Control Panel."""
def __init__(self, data, home_mode_name):
def __init__(self, data, home_mode_name, away_mode_name):
"""Initialize the alarm control panel."""
self._base_station = data
self._home_mode_name = home_mode_name
self._away_mode_name = away_mode_name
self._state = None
@property
@@ -89,8 +94,8 @@ class ArloBaseStation(AlarmControlPanel):
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
self._base_station.mode = ARMED
"""Send arm away command. Uses custom mode."""
self._base_station.mode = self._away_mode_name
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
@@ -118,4 +123,6 @@ class ArloBaseStation(AlarmControlPanel):
return STATE_ALARM_DISARMED
elif mode == self._home_mode_name:
return STATE_ALARM_ARMED_HOME
elif mode == self._away_mode_name:
return STATE_ALARM_ARMED_AWAY
return None

View File

@@ -0,0 +1,92 @@
"""
Support for Canary alarm.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.canary/
"""
import logging
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.canary import DATA_CANARY
from homeassistant.const import STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, \
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_HOME
DEPENDENCIES = ['canary']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Canary alarms."""
data = hass.data[DATA_CANARY]
devices = []
for location in data.locations:
devices.append(CanaryAlarm(data, location.location_id))
add_devices(devices, True)
class CanaryAlarm(AlarmControlPanel):
"""Representation of a Canary alarm control panel."""
def __init__(self, data, location_id):
"""Initialize a Canary security camera."""
self._data = data
self._location_id = location_id
@property
def name(self):
"""Return the name of the alarm."""
location = self._data.get_location(self._location_id)
return location.name
@property
def state(self):
"""Return the state of the device."""
from canary.api import LOCATION_MODE_AWAY, LOCATION_MODE_HOME, \
LOCATION_MODE_NIGHT
location = self._data.get_location(self._location_id)
if location.is_private:
return STATE_ALARM_DISARMED
mode = location.mode
if mode.name == LOCATION_MODE_AWAY:
return STATE_ALARM_ARMED_AWAY
elif mode.name == LOCATION_MODE_HOME:
return STATE_ALARM_ARMED_HOME
elif mode.name == LOCATION_MODE_NIGHT:
return STATE_ALARM_ARMED_NIGHT
else:
return None
@property
def device_state_attributes(self):
"""Return the state attributes."""
location = self._data.get_location(self._location_id)
return {
'private': location.is_private
}
def alarm_disarm(self, code=None):
"""Send disarm command."""
location = self._data.get_location(self._location_id)
self._data.set_location_mode(self._location_id, location.mode.name,
True)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
from canary.api import LOCATION_MODE_HOME
self._data.set_location_mode(self._location_id, LOCATION_MODE_HOME)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
from canary.api import LOCATION_MODE_AWAY
self._data.set_location_mode(self._location_id, LOCATION_MODE_AWAY)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
from canary.api import LOCATION_MODE_NIGHT
self._data.set_location_mode(self._location_id, LOCATION_MODE_NIGHT)

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['concord232==0.14']
REQUIREMENTS = ['concord232==0.15']
_LOGGER = logging.getLogger(__name__)
@@ -121,4 +121,4 @@ class Concord232Alarm(alarm.AlarmControlPanel):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._alarm.arm('auto')
self._alarm.arm('away')

View File

@@ -4,27 +4,45 @@ Demo platform that has two fake alarm control panels.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/demo/
"""
import datetime
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)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, CONF_DELAY_TIME,
CONF_PENDING_TIME, CONF_TRIGGER_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', None, False, {
STATE_ALARM_ARMED_AWAY: {
CONF_PENDING_TIME: 5
CONF_DELAY_TIME: datetime.timedelta(seconds=0),
CONF_PENDING_TIME: datetime.timedelta(seconds=5),
CONF_TRIGGER_TIME: datetime.timedelta(seconds=10),
},
STATE_ALARM_ARMED_HOME: {
CONF_PENDING_TIME: 5
CONF_DELAY_TIME: datetime.timedelta(seconds=0),
CONF_PENDING_TIME: datetime.timedelta(seconds=5),
CONF_TRIGGER_TIME: datetime.timedelta(seconds=10),
},
STATE_ALARM_ARMED_NIGHT: {
CONF_PENDING_TIME: 5
CONF_DELAY_TIME: datetime.timedelta(seconds=0),
CONF_PENDING_TIME: datetime.timedelta(seconds=5),
CONF_TRIGGER_TIME: datetime.timedelta(seconds=10),
},
STATE_ALARM_DISARMED: {
CONF_DELAY_TIME: datetime.timedelta(seconds=0),
CONF_TRIGGER_TIME: datetime.timedelta(seconds=10),
},
STATE_ALARM_ARMED_CUSTOM_BYPASS: {
CONF_DELAY_TIME: datetime.timedelta(seconds=0),
CONF_PENDING_TIME: datetime.timedelta(seconds=5),
CONF_TRIGGER_TIME: datetime.timedelta(seconds=10),
},
STATE_ALARM_TRIGGERED: {
CONF_PENDING_TIME: 5
CONF_PENDING_TIME: datetime.timedelta(seconds=5),
},
}),
])

View File

@@ -16,9 +16,9 @@ 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)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED, EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['pythonegardia==1.0.22']
REQUIREMENTS = ['pythonegardia==1.0.26']
_LOGGER = logging.getLogger(__name__)
@@ -26,13 +26,15 @@ 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'
CONF_VERSION = 'version'
DEFAULT_NAME = 'Egardia'
DEFAULT_PORT = 80
DEFAULT_REPORT_SERVER_ENABLED = False
DEFAULT_REPORT_SERVER_PORT = 52010
DEFAULT_VERSION = 'GATE-01'
DOMAIN = 'egardia'
D_EGARDIASRV = 'egardiaserver'
NOTIFICATION_ID = 'egardia_notification'
NOTIFICATION_TITLE = 'Egardia'
@@ -49,6 +51,7 @@ 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_VERSION, default=DEFAULT_VERSION): 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),
@@ -62,6 +65,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Egardia platform."""
from pythonegardia import egardiadevice
from pythonegardia import egardiaserver
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
@@ -71,41 +75,62 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
rs_enabled = config.get(CONF_REPORT_SERVER_ENABLED)
rs_port = config.get(CONF_REPORT_SERVER_PORT)
rs_codes = config.get(CONF_REPORT_SERVER_CODES)
version = config.get(CONF_VERSION)
try:
egardiasystem = egardiadevice.EgardiaDevice(
host, port, username, password, '')
host, port, username, password, '', version)
except requests.exceptions.RequestException:
raise exc.PlatformNotReady()
except egardiadevice.UnauthorizedError:
_LOGGER.error("Unable to authorize. Wrong password or username")
return False
return
add_devices([EgardiaAlarm(
name, egardiasystem, hass, rs_enabled, rs_port, rs_codes)], True)
eg_dev = EgardiaAlarm(
name, egardiasystem, rs_enabled, rs_codes)
if rs_enabled:
# Set up the egardia server
_LOGGER.info("Setting up EgardiaServer")
try:
if D_EGARDIASRV not in hass.data:
server = egardiaserver.EgardiaServer('', rs_port)
bound = server.bind()
if not bound:
raise IOError("Binding error occurred while " +
"starting EgardiaServer")
hass.data[D_EGARDIASRV] = server
server.start()
except IOError:
return
hass.data[D_EGARDIASRV].register_callback(eg_dev.handle_status_event)
def handle_stop_event(event):
"""Callback function for HA stop event."""
hass.data[D_EGARDIASRV].stop()
# listen to home assistant stop event
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop_event)
# add egardia alarm device
add_devices([eg_dev], 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):
def __init__(self, name, egardiasystem,
rs_enabled=False, rs_codes=None):
"""Initialize object."""
self._name = name
self._egardiasystem = egardiasystem
self._status = STATE_UNKNOWN
self._status = None
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."""
@@ -116,17 +141,20 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
"""Return the state of the device."""
return self._status
def handle_system_status_event(self, event):
@property
def should_poll(self):
"""Poll if no report server is enabled."""
if not self._rs_enabled:
return True
return False
def handle_status_event(self, event):
"""Handle egardia_system_status_event."""
if event.data.get('status') is not None:
statuscode = event.data.get('status')
statuscode = event.get('status')
if statuscode is not None:
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)
self.schedule_update_ha_state()
def lookupstatusfromcode(self, statuscode):
"""Look at the rs_codes and returns the status from the code."""
@@ -161,9 +189,8 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
def update(self):
"""Update the alarm status."""
if not self._rs_enabled:
status = self._egardiasystem.getstate()
self.parsestatus(status)
status = self._egardiasystem.getstate()
self.parsestatus(status)
def alarm_disarm(self, code=None):
"""Send disarm command."""

View File

@@ -6,7 +6,6 @@ https://home-assistant.io/components/alarm_control_panel.envisalink/
"""
import asyncio
import logging
import os
import voluptuous as vol
@@ -14,7 +13,6 @@ from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.helpers.config_validation as cv
from homeassistant.config import load_yaml_config_file
from homeassistant.components.envisalink import (
DATA_EVL, EnvisalinkDevice, PARTITION_SCHEMA, CONF_CODE, CONF_PANIC,
CONF_PARTITIONNAME, SIGNAL_KEYPAD_UPDATE, SIGNAL_PARTITION_UPDATE)
@@ -69,14 +67,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
for device in target_devices:
device.async_alarm_keypress(keypress)
# Register Envisalink specific services
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
hass.services.async_register(
alarm.DOMAIN, SERVICE_ALARM_KEYPRESS, alarm_keypress_handler,
descriptions.get(SERVICE_ALARM_KEYPRESS), schema=ALARM_KEYPRESS_SCHEMA)
schema=ALARM_KEYPRESS_SCHEMA)
return True

View File

@@ -0,0 +1,107 @@
"""
Interfaces with iAlarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.ialarm/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, CONF_HOST, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, CONF_NAME)
REQUIREMENTS = ['pyialarm==0.2']
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'iAlarm'
def no_application_protocol(value):
"""Validate that value is without the application protocol."""
protocol_separator = "://"
if not value or protocol_separator in value:
raise vol.Invalid(
'Invalid host, {} is not allowed'.format(protocol_separator))
return value
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_HOST): vol.All(cv.string, no_application_protocol),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up an iAlarm control panel."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
host = config.get(CONF_HOST)
url = 'http://{}'.format(host)
ialarm = IAlarmPanel(name, username, password, url)
add_devices([ialarm], True)
class IAlarmPanel(alarm.AlarmControlPanel):
"""Represent an iAlarm status."""
def __init__(self, name, username, password, url):
"""Initialize the iAlarm status."""
from pyialarm import IAlarm
self._name = name
self._username = username
self._password = password
self._url = url
self._state = None
self._client = IAlarm(username, password, url)
@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._state
def update(self):
"""Return the state of the device."""
status = self._client.get_status()
_LOGGER.debug('iAlarm status: %s', status)
if status:
status = int(status)
if status == self._client.DISARMED:
state = STATE_ALARM_DISARMED
elif status == self._client.ARMED_AWAY:
state = STATE_ALARM_ARMED_AWAY
elif status == self._client.ARMED_STAY:
state = STATE_ALARM_ARMED_HOME
else:
state = None
self._state = state
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._client.disarm()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._client.arm_away()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._client.arm_stay()

View File

@@ -14,25 +14,42 @@ 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,
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_DISARMED, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED, CONF_PLATFORM, CONF_NAME, CONF_CODE,
CONF_DELAY_TIME, 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
CONF_CODE_TEMPLATE = 'code_template'
DEFAULT_ALARM_NAME = 'HA Alarm'
DEFAULT_PENDING_TIME = 60
DEFAULT_TRIGGER_TIME = 120
DEFAULT_DELAY_TIME = datetime.timedelta(seconds=0)
DEFAULT_PENDING_TIME = datetime.timedelta(seconds=60)
DEFAULT_TRIGGER_TIME = datetime.timedelta(seconds=120)
DEFAULT_DISARM_AFTER_TRIGGER = False
SUPPORTED_PENDING_STATES = [STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_TRIGGERED]
SUPPORTED_STATES = [STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_TRIGGERED]
SUPPORTED_PRETRIGGER_STATES = [state for state in SUPPORTED_STATES
if state != STATE_ALARM_TRIGGERED]
SUPPORTED_PENDING_STATES = [state for state in SUPPORTED_STATES
if state != STATE_ALARM_DISARMED]
ATTR_PRE_PENDING_STATE = 'pre_pending_state'
ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
config = copy.deepcopy(config)
for state in SUPPORTED_PRETRIGGER_STATES:
if CONF_DELAY_TIME not in config[state]:
config[state][CONF_DELAY_TIME] = config[CONF_DELAY_TIME]
if CONF_TRIGGER_TIME not in config[state]:
config[state][CONF_TRIGGER_TIME] = config[CONF_TRIGGER_TIME]
for state in SUPPORTED_PENDING_STATES:
if CONF_PENDING_TIME not in config[state]:
config[state][CONF_PENDING_TIME] = config[CONF_PENDING_TIME]
@@ -40,26 +57,44 @@ def _state_validator(config):
return config
STATE_SETTING_SCHEMA = vol.Schema({
vol.Optional(CONF_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0))
})
def _state_schema(state):
schema = {}
if state in SUPPORTED_PRETRIGGER_STATES:
schema[vol.Optional(CONF_DELAY_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
schema[vol.Optional(CONF_TRIGGER_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
if state in SUPPORTED_PENDING_STATES:
schema[vol.Optional(CONF_PENDING_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
return vol.Schema(schema)
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,
vol.Exclusive(CONF_CODE, 'code validation'): cv.string,
vol.Exclusive(CONF_CODE_TEMPLATE, 'code validation'): cv.template,
vol.Optional(CONF_DELAY_TIME, default=DEFAULT_DELAY_TIME):
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_PENDING_TIME, default=DEFAULT_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_TRIGGER_TIME, default=DEFAULT_TRIGGER_TIME):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.All(cv.time_period, cv.positive_timedelta),
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.Optional(STATE_ALARM_ARMED_AWAY, default={}):
_state_schema(STATE_ALARM_ARMED_AWAY),
vol.Optional(STATE_ALARM_ARMED_HOME, default={}):
_state_schema(STATE_ALARM_ARMED_HOME),
vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}):
_state_schema(STATE_ALARM_ARMED_NIGHT),
vol.Optional(STATE_ALARM_ARMED_CUSTOM_BYPASS, default={}):
_state_schema(STATE_ALARM_ARMED_CUSTOM_BYPASS),
vol.Optional(STATE_ALARM_DISARMED, default={}):
_state_schema(STATE_ALARM_DISARMED),
vol.Optional(STATE_ALARM_TRIGGERED, default={}):
_state_schema(STATE_ALARM_TRIGGERED),
}, _state_validator))
_LOGGER = logging.getLogger(__name__)
@@ -71,8 +106,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
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_CODE_TEMPLATE),
config.get(CONF_DISARM_AFTER_TRIGGER, DEFAULT_DISARM_AFTER_TRIGGER),
config
)])
@@ -83,27 +117,37 @@ class ManualAlarm(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.
When triggered, will be pending for the triggering state's 'delay_time'
plus the triggered state's 'pending_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.
A trigger_time of zero disables the alarm_trigger service.
"""
def __init__(self, hass, name, code, pending_time, trigger_time,
def __init__(self, hass, name, code, code_template,
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._trigger_time = datetime.timedelta(seconds=trigger_time)
if code_template:
self._code = code_template
self._code.hass = hass
else:
self._code = code or None
self._disarm_after_trigger = disarm_after_trigger
self._pre_trigger_state = self._state
self._previous_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._delay_time_by_state = {
state: config[state][CONF_DELAY_TIME]
for state in SUPPORTED_PRETRIGGER_STATES}
self._trigger_time_by_state = {
state: config[state][CONF_TRIGGER_TIME]
for state in SUPPORTED_PRETRIGGER_STATES}
self._pending_time_by_state = {
state: config[state][CONF_PENDING_TIME]
for state in SUPPORTED_PENDING_STATES}
@property
def should_poll(self):
@@ -118,15 +162,16 @@ class ManualAlarm(alarm.AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._state == STATE_ALARM_TRIGGERED:
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():
trigger_time = self._trigger_time_by_state[self._previous_state]
if (self._state_ts + self._pending_time(self._state) +
trigger_time) < dt_util.utcnow():
if self._disarm_after_trigger:
return STATE_ALARM_DISARMED
else:
self._state = self._pre_trigger_state
self._state = self._previous_state
return self._state
if self._state in SUPPORTED_PENDING_STATES and \
@@ -135,9 +180,21 @@ class ManualAlarm(alarm.AlarmControlPanel):
return self._state
def _within_pending_time(self, state):
@property
def _active_state(self):
if self.state == STATE_ALARM_PENDING:
return self._previous_state
else:
return self._state
def _pending_time(self, state):
pending_time = self._pending_time_by_state[state]
return self._state_ts + pending_time > dt_util.utcnow()
if state == STATE_ALARM_TRIGGERED:
pending_time += self._delay_time_by_state[self._previous_state]
return pending_time
def _within_pending_time(self, state):
return self._state_ts + self._pending_time(state) > dt_util.utcnow()
@property
def code_format(self):
@@ -174,27 +231,43 @@ class ManualAlarm(alarm.AlarmControlPanel):
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
def alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command."""
if not self._validate_code(code, STATE_ALARM_ARMED_CUSTOM_BYPASS):
return
self._update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS)
def alarm_trigger(self, code=None):
"""
Send alarm trigger command.
No code needed, a trigger time of zero for the current state
disables the alarm.
"""
if not self._trigger_time_by_state[self._active_state]:
return
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
if self._state == state:
return
self._previous_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:
pending_time = self._pending_time(state)
if state == STATE_ALARM_TRIGGERED:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
trigger_time = self._trigger_time_by_state[self._previous_state]
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._trigger_time + pending_time)
self._state_ts + pending_time + trigger_time)
elif state in SUPPORTED_PENDING_STATES and pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
@@ -202,7 +275,14 @@ class ManualAlarm(alarm.AlarmControlPanel):
def _validate_code(self, code, state):
"""Validate given code."""
check = self._code is None or code == self._code
if self._code is None:
return True
if isinstance(self._code, str):
alarm_code = self._code
else:
alarm_code = self._code.render(from_state=self._state,
to_state=state)
check = not alarm_code or code == alarm_code
if not check:
_LOGGER.warning("Invalid code given for %s", state)
return check
@@ -213,6 +293,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
state_attr = {}
if self.state == STATE_ALARM_PENDING:
state_attr[ATTR_PRE_PENDING_STATE] = self._previous_state
state_attr[ATTR_POST_PENDING_STATE] = self._state
return state_attr

View File

@@ -16,8 +16,8 @@ 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)
CONF_PLATFORM, CONF_NAME, CONF_CODE, CONF_DELAY_TIME, 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
@@ -26,28 +26,44 @@ from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import track_point_in_time
CONF_CODE_TEMPLATE = 'code_template'
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_DELAY_TIME = datetime.timedelta(seconds=0)
DEFAULT_PENDING_TIME = datetime.timedelta(seconds=60)
DEFAULT_TRIGGER_TIME = datetime.timedelta(seconds=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]
SUPPORTED_STATES = [STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_TRIGGERED]
SUPPORTED_PRETRIGGER_STATES = [state for state in SUPPORTED_STATES
if state != STATE_ALARM_TRIGGERED]
SUPPORTED_PENDING_STATES = [state for state in SUPPORTED_STATES
if state != STATE_ALARM_DISARMED]
ATTR_PRE_PENDING_STATE = 'pre_pending_state'
ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
config = copy.deepcopy(config)
for state in SUPPORTED_PRETRIGGER_STATES:
if CONF_DELAY_TIME not in config[state]:
config[state][CONF_DELAY_TIME] = config[CONF_DELAY_TIME]
if CONF_TRIGGER_TIME not in config[state]:
config[state][CONF_TRIGGER_TIME] = config[CONF_TRIGGER_TIME]
for state in SUPPORTED_PENDING_STATES:
if CONF_PENDING_TIME not in config[state]:
config[state][CONF_PENDING_TIME] = config[CONF_PENDING_TIME]
@@ -55,27 +71,44 @@ def _state_validator(config):
return config
STATE_SETTING_SCHEMA = vol.Schema({
vol.Optional(CONF_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0))
})
def _state_schema(state):
schema = {}
if state in SUPPORTED_PRETRIGGER_STATES:
schema[vol.Optional(CONF_DELAY_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
schema[vol.Optional(CONF_TRIGGER_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
if state in SUPPORTED_PENDING_STATES:
schema[vol.Optional(CONF_PENDING_TIME)] = vol.All(
cv.time_period, cv.positive_timedelta)
return vol.Schema(schema)
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.Exclusive(CONF_CODE, 'code validation'): cv.string,
vol.Exclusive(CONF_CODE_TEMPLATE, 'code validation'): cv.template,
vol.Optional(CONF_DELAY_TIME, default=DEFAULT_DELAY_TIME):
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_PENDING_TIME, default=DEFAULT_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.All(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_TRIGGER_TIME, default=DEFAULT_TRIGGER_TIME):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.All(cv.time_period, cv.positive_timedelta),
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.Optional(STATE_ALARM_ARMED_AWAY, default={}):
_state_schema(STATE_ALARM_ARMED_AWAY),
vol.Optional(STATE_ALARM_ARMED_HOME, default={}):
_state_schema(STATE_ALARM_ARMED_HOME),
vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}):
_state_schema(STATE_ALARM_ARMED_NIGHT),
vol.Optional(STATE_ALARM_DISARMED, default={}):
_state_schema(STATE_ALARM_DISARMED),
vol.Optional(STATE_ALARM_TRIGGERED, default={}):
_state_schema(STATE_ALARM_TRIGGERED),
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,
@@ -93,8 +126,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
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_CODE_TEMPLATE),
config.get(CONF_DISARM_AFTER_TRIGGER, DEFAULT_DISARM_AFTER_TRIGGER),
config.get(mqtt.CONF_STATE_TOPIC),
config.get(mqtt.CONF_COMMAND_TOPIC),
@@ -111,13 +143,15 @@ 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.
When triggered, will be pending for the triggering state's 'delay_time'
plus the triggered state's 'pending_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.
A trigger_time of zero disables the alarm_trigger service.
"""
def __init__(self, hass, name, code, pending_time,
trigger_time, disarm_after_trigger,
def __init__(self, hass, name, code, code_template,
disarm_after_trigger,
state_topic, command_topic, qos,
payload_disarm, payload_arm_home, payload_arm_away,
payload_arm_night, config):
@@ -125,17 +159,24 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
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)
if code_template:
self._code = code_template
self._code.hass = hass
else:
self._code = code or None
self._disarm_after_trigger = disarm_after_trigger
self._pre_trigger_state = self._state
self._previous_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._delay_time_by_state = {
state: config[state][CONF_DELAY_TIME]
for state in SUPPORTED_PRETRIGGER_STATES}
self._trigger_time_by_state = {
state: config[state][CONF_TRIGGER_TIME]
for state in SUPPORTED_PRETRIGGER_STATES}
self._pending_time_by_state = {
state: config[state][CONF_PENDING_TIME]
for state in SUPPORTED_PENDING_STATES}
self._state_topic = state_topic
self._command_topic = command_topic
@@ -158,15 +199,16 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._state == STATE_ALARM_TRIGGERED:
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():
trigger_time = self._trigger_time_by_state[self._previous_state]
if (self._state_ts + self._pending_time(self._state) +
trigger_time) < dt_util.utcnow():
if self._disarm_after_trigger:
return STATE_ALARM_DISARMED
else:
self._state = self._pre_trigger_state
self._state = self._previous_state
return self._state
if self._state in SUPPORTED_PENDING_STATES and \
@@ -175,9 +217,21 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
return self._state
def _within_pending_time(self, state):
@property
def _active_state(self):
if self.state == STATE_ALARM_PENDING:
return self._previous_state
else:
return self._state
def _pending_time(self, state):
pending_time = self._pending_time_by_state[state]
return self._state_ts + pending_time > dt_util.utcnow()
if state == STATE_ALARM_TRIGGERED:
pending_time += self._delay_time_by_state[self._previous_state]
return pending_time
def _within_pending_time(self, state):
return self._state_ts + self._pending_time(state) > dt_util.utcnow()
@property
def code_format(self):
@@ -215,26 +269,35 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
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
"""
Send alarm trigger command.
No code needed, a trigger time of zero for the current state
disables the alarm.
"""
if not self._trigger_time_by_state[self._active_state]:
return
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
if self._state == state:
return
self._previous_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:
pending_time = self._pending_time(state)
if state == STATE_ALARM_TRIGGERED:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
trigger_time = self._trigger_time_by_state[self._previous_state]
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._trigger_time + pending_time)
self._state_ts + pending_time + trigger_time)
elif state in SUPPORTED_PENDING_STATES and pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
@@ -242,7 +305,14 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
def _validate_code(self, code, state):
"""Validate given code."""
check = self._code is None or code == self._code
if self._code is None:
return True
if isinstance(self._code, str):
alarm_code = self._code
else:
alarm_code = self._code.render(from_state=self._state,
to_state=state)
check = not alarm_code or code == alarm_code
if not check:
_LOGGER.warning("Invalid code given for %s", state)
return check
@@ -253,6 +323,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
state_attr = {}
if self.state == STATE_ALARM_PENDING:
state_attr[ATTR_PRE_PENDING_STATE] = self._previous_state
state_attr[ATTR_POST_PENDING_STATE] = self._state
return state_attr

View File

@@ -17,7 +17,9 @@ from homeassistant.const import (
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN,
CONF_NAME, CONF_CODE)
from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS)
CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS,
MqttAvailability)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -54,15 +56,21 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
config.get(CONF_PAYLOAD_DISARM),
config.get(CONF_PAYLOAD_ARM_HOME),
config.get(CONF_PAYLOAD_ARM_AWAY),
config.get(CONF_CODE))])
config.get(CONF_CODE),
config.get(CONF_AVAILABILITY_TOPIC),
config.get(CONF_PAYLOAD_AVAILABLE),
config.get(CONF_PAYLOAD_NOT_AVAILABLE))])
class MqttAlarm(alarm.AlarmControlPanel):
class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
"""Representation of a MQTT alarm status."""
def __init__(self, name, state_topic, command_topic, qos, payload_disarm,
payload_arm_home, payload_arm_away, code):
payload_arm_home, payload_arm_away, code, availability_topic,
payload_available, payload_not_available):
"""Init the MQTT Alarm Control Panel."""
super().__init__(availability_topic, qos, payload_available,
payload_not_available)
self._state = STATE_UNKNOWN
self._name = name
self._state_topic = state_topic
@@ -73,11 +81,11 @@ class MqttAlarm(alarm.AlarmControlPanel):
self._payload_arm_away = payload_arm_away
self._code = code
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe mqtt events.
"""Subscribe mqtt events."""
yield from super().async_added_to_hass()
This method must be run in the event loop and returns a coroutine.
"""
@callback
def message_received(topic, payload, qos):
"""Run when new MQTT message has been received."""
@@ -89,7 +97,7 @@ class MqttAlarm(alarm.AlarmControlPanel):
self._state = payload
self.async_schedule_update_ha_state()
return mqtt.async_subscribe(
yield from mqtt.async_subscribe(
self.hass, self._state_topic, message_received, self._qos)
@property

View File

@@ -1,65 +1,71 @@
alarm_disarm:
description: Send the alarm the command for disarm
# Describes the format for available alarm control panel services
alarm_disarm:
description: Send the alarm the command for disarm.
fields:
entity_id:
description: Name of alarm control panel to disarm
description: Name of alarm control panel to disarm.
example: 'alarm_control_panel.downstairs'
code:
description: An optional code to disarm the alarm control panel with
description: An optional code to disarm the alarm control panel with.
example: 1234
alarm_arm_home:
description: Send the alarm the command for arm home
description: Send the alarm the command for arm home.
fields:
entity_id:
description: Name of alarm control panel to arm home
description: Name of alarm control panel to arm home.
example: 'alarm_control_panel.downstairs'
code:
description: An optional code to arm home the alarm control panel with
description: An optional code to arm home the alarm control panel with.
example: 1234
alarm_arm_away:
description: Send the alarm the command for arm away
description: Send the alarm the command for arm away.
fields:
entity_id:
description: Name of alarm control panel to arm away
description: Name of alarm control panel to arm away.
example: 'alarm_control_panel.downstairs'
code:
description: An optional code to arm away the alarm control panel with
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
description: Send the alarm the command for arm night.
fields:
entity_id:
description: Name of alarm control panel to arm night
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
description: An optional code to arm night the alarm control panel with.
example: 1234
alarm_trigger:
description: Send the alarm the command for trigger
description: Send the alarm the command for trigger.
fields:
entity_id:
description: Name of alarm control panel to trigger
description: Name of alarm control panel to trigger.
example: 'alarm_control_panel.downstairs'
code:
description: An optional code to trigger the alarm control panel with
description: An optional code to trigger the alarm control panel with.
example: 1234
envisalink_alarm_keypress:
description: Send custom keypresses to the alarm
description: Send custom keypresses to the alarm.
fields:
entity_id:
description: Name of the alarm control panel to trigger
description: Name of the alarm control panel to trigger.
example: 'alarm_control_panel.downstairs'
keypress:
description: 'String to send to the alarm panel (1-6 characters)'
description: 'String to send to the alarm panel (1-6 characters).'
example: '*71'
alarmdecoder_alarm_toggle_chime:
description: Send the alarm the toggle chime command.
fields:
entity_id:
description: Name of the alarm control panel to trigger.
example: 'alarm_control_panel.downstairs'
code:
description: A required code to toggle the alarm control panel chime with.
example: 1234

View File

@@ -34,10 +34,8 @@ def async_setup_platform(hass, config, async_add_devices,
discovery_info[ATTR_DISCOVER_AREAS] is None):
return
devices = [SpcAlarm(hass=hass,
area_id=area['id'],
name=area['name'],
state=_get_alarm_state(area['mode']))
api = hass.data[DATA_API]
devices = [SpcAlarm(api, area)
for area in discovery_info[ATTR_DISCOVER_AREAS]]
async_add_devices(devices)
@@ -46,21 +44,29 @@ def async_setup_platform(hass, config, async_add_devices,
class SpcAlarm(alarm.AlarmControlPanel):
"""Represents the SPC alarm panel."""
def __init__(self, hass, area_id, name, state):
def __init__(self, api, area):
"""Initialize the SPC alarm panel."""
self._hass = hass
self._area_id = area_id
self._name = name
self._state = state
self._api = hass.data[DATA_API]
hass.data[DATA_REGISTRY].register_alarm_device(area_id, self)
self._area_id = area['id']
self._name = area['name']
self._state = _get_alarm_state(area['mode'])
if self._state == STATE_ALARM_DISARMED:
self._changed_by = area.get('last_unset_user_name', 'unknown')
else:
self._changed_by = area.get('last_set_user_name', 'unknown')
self._api = api
@asyncio.coroutine
def async_update_from_spc(self, state):
def async_added_to_hass(self):
"""Calbback for init handlers."""
self.hass.data[DATA_REGISTRY].register_alarm_device(
self._area_id, self)
@asyncio.coroutine
def async_update_from_spc(self, state, extra):
"""Update the alarm panel with a new state."""
self._state = state
yield from self.async_update_ha_state()
self._changed_by = extra.get('changed_by', 'unknown')
self.async_schedule_update_ha_state()
@property
def should_poll(self):
@@ -72,6 +78,11 @@ class SpcAlarm(alarm.AlarmControlPanel):
"""Return the name of the device."""
return self._name
@property
def changed_by(self):
"""Return the user the last change was triggered by."""
return self._changed_by
@property
def state(self):
"""Return the state of the device."""

View File

@@ -14,9 +14,11 @@ 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_ARMED_NIGHT, STATE_ALARM_DISARMED,
STATE_ALARM_ARMING, STATE_ALARM_DISARMING, STATE_UNKNOWN, CONF_NAME)
STATE_ALARM_ARMING, STATE_ALARM_DISARMING, STATE_UNKNOWN, CONF_NAME,
STATE_ALARM_ARMED_CUSTOM_BYPASS)
REQUIREMENTS = ['total_connect_client==0.11']
REQUIREMENTS = ['total_connect_client==0.16']
_LOGGER = logging.getLogger(__name__)
@@ -76,6 +78,8 @@ class TotalConnect(alarm.AlarmControlPanel):
state = STATE_ALARM_ARMED_AWAY
elif status == self._client.ARMED_STAY_NIGHT:
state = STATE_ALARM_ARMED_NIGHT
elif status == self._client.ARMED_CUSTOM_BYPASS:
state = STATE_ALARM_ARMED_CUSTOM_BYPASS
elif status == self._client.ARMING:
state = STATE_ALARM_ARMING
elif status == self._client.DISARMING:

View File

@@ -4,18 +4,18 @@ Support for AlarmDecoder devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alarmdecoder/
"""
import asyncio
import logging
from datetime import timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.discovery import load_platform
from homeassistant.util import dt as dt_util
from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA
REQUIREMENTS = ['alarmdecoder==0.12.3']
REQUIREMENTS = ['alarmdecoder==1.13.2']
_LOGGER = logging.getLogger(__name__)
@@ -32,6 +32,7 @@ CONF_DEVICE_TYPE = 'type'
CONF_PANEL_DISPLAY = 'panel_display'
CONF_ZONE_NAME = 'name'
CONF_ZONE_TYPE = 'type'
CONF_ZONE_RFID = 'rfid'
CONF_ZONES = 'zones'
DEFAULT_DEVICE_TYPE = 'socket'
@@ -51,6 +52,7 @@ SIGNAL_PANEL_DISARM = 'alarmdecoder.panel_disarm'
SIGNAL_ZONE_FAULT = 'alarmdecoder.zone_fault'
SIGNAL_ZONE_RESTORE = 'alarmdecoder.zone_restore'
SIGNAL_RFX_MESSAGE = 'alarmdecoder.rfx_message'
DEVICE_SOCKET_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICE_TYPE): 'socket',
@@ -67,13 +69,15 @@ DEVICE_USB_SCHEMA = vol.Schema({
ZONE_SCHEMA = vol.Schema({
vol.Required(CONF_ZONE_NAME): cv.string,
vol.Optional(CONF_ZONE_TYPE, default=DEFAULT_ZONE_TYPE): cv.string})
vol.Optional(CONF_ZONE_TYPE,
default=DEFAULT_ZONE_TYPE): vol.Any(DEVICE_CLASSES_SCHEMA),
vol.Optional(CONF_ZONE_RFID): cv.string})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_DEVICE): vol.Any(DEVICE_SOCKET_SCHEMA,
DEVICE_SERIAL_SCHEMA,
DEVICE_USB_SCHEMA),
vol.Required(CONF_DEVICE): vol.Any(
DEVICE_SOCKET_SCHEMA, DEVICE_SERIAL_SCHEMA,
DEVICE_USB_SCHEMA),
vol.Optional(CONF_PANEL_DISPLAY,
default=DEFAULT_PANEL_DISPLAY): cv.boolean,
vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA},
@@ -81,14 +85,14 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
def setup(hass, config):
"""Set up for the AlarmDecoder devices."""
from alarmdecoder import AlarmDecoder
from alarmdecoder.devices import (SocketDevice, SerialDevice, USBDevice)
conf = config.get(DOMAIN)
restart = False
device = conf.get(CONF_DEVICE)
display = conf.get(CONF_PANEL_DISPLAY)
zones = conf.get(CONF_ZONES)
@@ -99,32 +103,55 @@ def async_setup(hass, config):
path = DEFAULT_DEVICE_PATH
baud = DEFAULT_DEVICE_BAUD
sync_connect = asyncio.Future(loop=hass.loop)
def handle_open(device):
"""Handle the successful connection."""
_LOGGER.info("Established a connection with the alarmdecoder")
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
sync_connect.set_result(True)
@callback
def stop_alarmdecoder(event):
"""Handle the shutdown of AlarmDecoder."""
_LOGGER.debug("Shutting down alarmdecoder")
nonlocal restart
restart = False
controller.close()
@callback
def open_connection(now=None):
"""Open a connection to AlarmDecoder."""
from alarmdecoder.util import NoDeviceError
nonlocal restart
try:
controller.open(baud)
except NoDeviceError:
_LOGGER.debug("Failed to connect. Retrying in 5 seconds")
hass.helpers.event.track_point_in_time(
open_connection, dt_util.utcnow() + timedelta(seconds=5))
return
_LOGGER.debug("Established a connection with the alarmdecoder")
restart = True
def handle_closed_connection(event):
"""Restart after unexpected loss of connection."""
nonlocal restart
if not restart:
return
restart = False
_LOGGER.warning("AlarmDecoder unexpectedly lost connection.")
hass.add_job(open_connection)
def handle_message(sender, message):
"""Handle message from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_PANEL_MESSAGE, message)
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_PANEL_MESSAGE, message)
def handle_rfx_message(sender, message):
"""Handle RFX message from AlarmDecoder."""
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_RFX_MESSAGE, message)
def zone_fault_callback(sender, zone):
"""Handle zone fault from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_FAULT, zone)
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_ZONE_FAULT, zone)
def zone_restore_callback(sender, zone):
"""Handle zone restore from AlarmDecoder."""
async_dispatcher_send(hass, SIGNAL_ZONE_RESTORE, zone)
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_ZONE_RESTORE, zone)
controller = False
if device_type == 'socket':
@@ -139,30 +166,25 @@ def async_setup(hass, config):
AlarmDecoder(USBDevice.find())
return False
controller.on_open += handle_open
controller.on_message += handle_message
controller.on_rfx_message += handle_rfx_message
controller.on_zone_fault += zone_fault_callback
controller.on_zone_restore += zone_restore_callback
controller.on_close += handle_closed_connection
hass.data[DATA_AD] = controller
controller.open(baud)
open_connection()
result = yield from sync_connect
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
if not result:
return False
hass.async_add_job(
async_load_platform(hass, 'alarm_control_panel', DOMAIN, conf,
config))
load_platform(hass, 'alarm_control_panel', DOMAIN, conf, config)
if zones:
hass.async_add_job(async_load_platform(
hass, 'binary_sensor', DOMAIN, {CONF_ZONES: zones}, config))
load_platform(
hass, 'binary_sensor', DOMAIN, {CONF_ZONES: zones}, config)
if display:
hass.async_add_job(async_load_platform(
hass, 'sensor', DOMAIN, conf, config))
load_platform(hass, 'sensor', DOMAIN, conf, config)
return True

View File

@@ -7,12 +7,10 @@ https://home-assistant.io/components/alert/
import asyncio
from datetime import datetime, timedelta
import logging
import os
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE, STATE_ON, STATE_OFF,
SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, ATTR_ENTITY_ID)
@@ -129,22 +127,16 @@ def async_setup(hass, config):
alert[CONF_NOTIFIERS], alert[CONF_CAN_ACK])
all_alerts[entity.entity_id] = entity
# Read descriptions
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
descriptions = descriptions.get(DOMAIN, {})
# Setup service calls
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handle_alert_service,
descriptions.get(SERVICE_TURN_OFF), schema=ALERT_SERVICE_SCHEMA)
schema=ALERT_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_alert_service,
descriptions.get(SERVICE_TURN_ON), schema=ALERT_SERVICE_SCHEMA)
schema=ALERT_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_alert_service,
descriptions.get(SERVICE_TOGGLE), schema=ALERT_SERVICE_SCHEMA)
schema=ALERT_SERVICE_SCHEMA)
tasks = [alert.async_update_ha_state() for alert in all_alerts.values()]
if tasks:

View File

@@ -15,4 +15,6 @@ ATTR_STREAM_URL = 'streamUrl'
ATTR_MAIN_TEXT = 'mainText'
ATTR_REDIRECTION_URL = 'redirectionURL'
SYN_RESOLUTION_MATCH = 'ER_SUCCESS_MATCH'
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.0Z'

View File

@@ -3,20 +3,22 @@ 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.exceptions import HomeAssistantError
from homeassistant.core import callback
from homeassistant.const import HTTP_BAD_REQUEST
from homeassistant.helpers import intent
from homeassistant.components import http
from homeassistant.util.decorator import Registry
from .const import DOMAIN
from .const import DOMAIN, SYN_RESOLUTION_MATCH
INTENTS_API_ENDPOINT = '/api/alexa'
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
@@ -46,6 +48,10 @@ def async_setup(hass):
hass.http.register_view(AlexaIntentsView)
class UnknownRequest(HomeAssistantError):
"""When an unknown Alexa request is passed in."""
class AlexaIntentsView(http.HomeAssistantView):
"""Handle Alexa requests."""
@@ -56,71 +62,149 @@ class AlexaIntentsView(http.HomeAssistantView):
def post(self, request):
"""Handle Alexa."""
hass = request.app['hass']
data = yield from request.json()
message = 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']
_LOGGER.debug('Received Alexa request: %s', message)
try:
intent_response = yield from intent.async_handle(
hass, DOMAIN, intent_name,
{key: {'value': value} for key, value
in alexa_response.variables.items()})
response = yield from async_handle_message(hass, message)
return b'' if response is None else self.json(response)
except UnknownRequest as err:
_LOGGER.warning(str(err))
return self.json(intent_error_response(
hass, message, str(err)))
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)
_LOGGER.warning(str(err))
return self.json(intent_error_response(
hass, message,
"This intent is not yet configured within Home Assistant."))
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)
return self.json(intent_error_response(
hass, message,
"Invalid slot information received for this intent."))
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
except intent.IntentError as err:
_LOGGER.exception(str(err))
return self.json(intent_error_response(
hass, message, "Error handling intent."))
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)
def intent_error_response(hass, message, error):
"""Return an Alexa response that will speak the error message."""
alexa_intent_info = message.get('request').get('intent')
alexa_response = AlexaResponse(hass, alexa_intent_info)
alexa_response.add_speech(SpeechType.plaintext, error)
return alexa_response.as_dict()
@asyncio.coroutine
def async_handle_message(hass, message):
"""Handle an Alexa intent.
Raises:
- UnknownRequest
- intent.UnknownIntent
- intent.InvalidSlotInfo
- intent.IntentError
"""
req = message.get('request')
req_type = req['type']
handler = HANDLERS.get(req_type)
if not handler:
raise UnknownRequest('Received unknown request {}'.format(req_type))
return (yield from handler(hass, message))
@HANDLERS.register('SessionEndedRequest')
@asyncio.coroutine
def async_handle_session_end(hass, message):
"""Handle a session end request."""
return None
@HANDLERS.register('IntentRequest')
@HANDLERS.register('LaunchRequest')
@asyncio.coroutine
def async_handle_intent(hass, message):
"""Handle an intent request.
Raises:
- intent.UnknownIntent
- intent.InvalidSlotInfo
- intent.IntentError
"""
req = message.get('request')
alexa_intent_info = req.get('intent')
alexa_response = AlexaResponse(hass, alexa_intent_info)
if req['type'] == 'LaunchRequest':
intent_name = message.get('session', {}) \
.get('application', {}) \
.get('applicationId')
else:
intent_name = alexa_intent_info['name']
intent_response = yield from intent.async_handle(
hass, DOMAIN, intent_name,
{key: {'value': value} for key, value
in alexa_response.variables.items()})
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 alexa_response.as_dict()
def resolve_slot_synonyms(key, request):
"""Check slot request for synonym resolutions."""
# Default to the spoken slot value if more than one or none are found. For
# reference to the request object structure, see the Alexa docs:
# https://tinyurl.com/ybvm7jhs
resolved_value = request['value']
if ('resolutions' in request and
'resolutionsPerAuthority' in request['resolutions'] and
len(request['resolutions']['resolutionsPerAuthority']) >= 1):
# Extract all of the possible values from each authority with a
# successful match
possible_values = []
for entry in request['resolutions']['resolutionsPerAuthority']:
if entry['status']['code'] != SYN_RESOLUTION_MATCH:
continue
possible_values.extend([item['value']['name']
for item
in entry['values']])
# If there is only one match use the resolved value, otherwise the
# resolution cannot be determined, so use the spoken slot value
if len(possible_values) == 1:
resolved_value = possible_values[0]
else:
_LOGGER.debug(
'Found multiple synonym resolutions for slot value: {%s: %s}',
key,
request['value']
)
return resolved_value
class AlexaResponse(object):
@@ -135,12 +219,17 @@ class AlexaResponse(object):
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']
# Only include slots with values
if 'value' not in value:
continue
_key = key.replace('.', '_')
self.variables[_key] = resolve_slot_synonyms(key, value)
def add_card(self, card_type, title, content):
"""Add a card to the response."""

View File

@@ -1,35 +1,87 @@
"""Support for alexa Smart Home Skill API."""
import asyncio
import logging
import math
from uuid import uuid4
import homeassistant.core as ha
from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
from homeassistant.components import switch, light
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, SERVICE_LOCK,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_UNLOCK, SERVICE_VOLUME_SET)
from homeassistant.components import (
alert, automation, cover, fan, group, input_boolean, light, lock,
media_player, scene, script, switch)
import homeassistant.util.color as color_util
from homeassistant.util.decorator import Registry
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__)
API_DIRECTIVE = 'directive'
API_ENDPOINT = 'endpoint'
API_EVENT = 'event'
API_HEADER = 'header'
API_PAYLOAD = 'payload'
API_ENDPOINT = 'endpoint'
CONF_DESCRIPTION = 'description'
CONF_DISPLAY_CATEGORIES = 'display_categories'
CONF_NAME = 'name'
MAPPING_COMPONENT = {
switch.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
light.DOMAIN: [
'LIGHT', ('Alexa.PowerController',), {
light.SUPPORT_BRIGHTNESS: 'Alexa.BrightnessController'
alert.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
automation.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
cover.DOMAIN: [
'DOOR', ('Alexa.PowerController',), {
cover.SUPPORT_SET_POSITION: 'Alexa.PercentageController',
}
],
fan.DOMAIN: [
'OTHER', ('Alexa.PowerController',), {
fan.SUPPORT_SET_SPEED: 'Alexa.PercentageController',
}
],
group.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
input_boolean.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
light.DOMAIN: [
'LIGHT', ('Alexa.PowerController',), {
light.SUPPORT_BRIGHTNESS: 'Alexa.BrightnessController',
light.SUPPORT_RGB_COLOR: 'Alexa.ColorController',
light.SUPPORT_XY_COLOR: 'Alexa.ColorController',
light.SUPPORT_COLOR_TEMP: 'Alexa.ColorTemperatureController',
}
],
lock.DOMAIN: ['SMARTLOCK', ('Alexa.LockController',), None],
media_player.DOMAIN: [
'TV', ('Alexa.PowerController',), {
media_player.SUPPORT_VOLUME_SET: 'Alexa.Speaker',
media_player.SUPPORT_PLAY: 'Alexa.PlaybackController',
media_player.SUPPORT_PAUSE: 'Alexa.PlaybackController',
media_player.SUPPORT_STOP: 'Alexa.PlaybackController',
media_player.SUPPORT_NEXT_TRACK: 'Alexa.PlaybackController',
media_player.SUPPORT_PREVIOUS_TRACK: 'Alexa.PlaybackController',
}
],
scene.DOMAIN: ['ACTIVITY_TRIGGER', ('Alexa.SceneController',), None],
script.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
switch.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
}
class Config:
"""Hold the configuration for Alexa."""
def __init__(self, should_expose, entity_config=None):
"""Initialize the configuration."""
self.should_expose = should_expose
self.entity_config = entity_config or {}
@asyncio.coroutine
def async_handle_message(hass, message):
def async_handle_message(hass, config, message):
"""Handle incoming API messages."""
assert message[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3'
@@ -45,7 +97,7 @@ def async_handle_message(hass, message):
"Unsupported API request %s/%s", namespace, name)
return api_error(message)
return (yield from funct_ref(hass, message))
return (yield from funct_ref(hass, config, message))
def api_message(request, name='Response', namespace='Alexa', payload=None):
@@ -94,7 +146,7 @@ def api_error(request, error_type='INTERNAL_ERROR', error_message=""):
@HANDLERS.register(('Alexa.Discovery', 'Discover'))
@asyncio.coroutine
def async_api_discovery(hass, request):
def async_api_discovery(hass, config, request):
"""Create a API formatted discovery response.
Async friendly.
@@ -102,18 +154,36 @@ def async_api_discovery(hass, request):
discovery_endpoints = []
for entity in hass.states.async_all():
if not config.should_expose(entity.entity_id):
_LOGGER.debug("Not exposing %s because filtered by config",
entity.entity_id)
continue
class_data = MAPPING_COMPONENT.get(entity.domain)
if not class_data:
continue
entity_conf = config.entity_config.get(entity.entity_id, {})
friendly_name = entity_conf.get(CONF_NAME, entity.name)
description = entity_conf.get(CONF_DESCRIPTION, entity.entity_id)
# Required description as per Amazon Scene docs
if entity.domain == scene.DOMAIN:
scene_fmt = '{} (Scene connected via Home Assistant)'
description = scene_fmt.format(description)
display_categories = entity_conf.get(CONF_DISPLAY_CATEGORIES,
class_data[0])
endpoint = {
'displayCategories': [class_data[0]],
'displayCategories': [display_categories],
'additionalApplianceDetails': {},
'endpointId': entity.entity_id.replace('.', '#'),
'friendlyName': entity.name,
'description': '',
'manufacturerName': 'Unknown',
'friendlyName': friendly_name,
'description': description,
'manufacturerName': 'Home Assistant',
}
actions = set()
@@ -148,7 +218,7 @@ def async_api_discovery(hass, request):
def extract_entity(funct):
"""Decorator for extract entity object from request."""
@asyncio.coroutine
def async_api_entity_wrapper(hass, request):
def async_api_entity_wrapper(hass, config, request):
"""Process a turn on request."""
entity_id = request[API_ENDPOINT]['endpointId'].replace('#', '.')
@@ -159,7 +229,7 @@ def extract_entity(funct):
request[API_HEADER]['name'], entity_id)
return api_error(request, error_type='NO_SUCH_ENDPOINT')
return (yield from funct(hass, request, entity))
return (yield from funct(hass, config, request, entity))
return async_api_entity_wrapper
@@ -167,11 +237,19 @@ def extract_entity(funct):
@HANDLERS.register(('Alexa.PowerController', 'TurnOn'))
@extract_entity
@asyncio.coroutine
def async_api_turn_on(hass, request, entity):
def async_api_turn_on(hass, config, request, entity):
"""Process a turn on request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
domain = entity.domain
if entity.domain == group.DOMAIN:
domain = ha.DOMAIN
service = SERVICE_TURN_ON
if entity.domain == cover.DOMAIN:
service = cover.SERVICE_OPEN_COVER
yield from hass.services.async_call(domain, service, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=True)
}, blocking=False)
return api_message(request)
@@ -179,11 +257,19 @@ def async_api_turn_on(hass, request, entity):
@HANDLERS.register(('Alexa.PowerController', 'TurnOff'))
@extract_entity
@asyncio.coroutine
def async_api_turn_off(hass, request, entity):
def async_api_turn_off(hass, config, request, entity):
"""Process a turn off request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_OFF, {
domain = entity.domain
if entity.domain == group.DOMAIN:
domain = ha.DOMAIN
service = SERVICE_TURN_OFF
if entity.domain == cover.DOMAIN:
service = cover.SERVICE_CLOSE_COVER
yield from hass.services.async_call(domain, service, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=True)
}, blocking=False)
return api_message(request)
@@ -191,13 +277,379 @@ def async_api_turn_off(hass, request, entity):
@HANDLERS.register(('Alexa.BrightnessController', 'SetBrightness'))
@extract_entity
@asyncio.coroutine
def async_api_set_brightness(hass, request, entity):
def async_api_set_brightness(hass, config, request, entity):
"""Process a set brightness request."""
brightness = request[API_PAYLOAD]['brightness']
brightness = int(request[API_PAYLOAD]['brightness'])
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_BRIGHTNESS: brightness,
}, blocking=True)
light.ATTR_BRIGHTNESS_PCT: brightness,
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.BrightnessController', 'AdjustBrightness'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_brightness(hass, config, request, entity):
"""Process a adjust brightness request."""
brightness_delta = int(request[API_PAYLOAD]['brightnessDelta'])
# read current state
try:
current = math.floor(
int(entity.attributes.get(light.ATTR_BRIGHTNESS)) / 255 * 100)
except ZeroDivisionError:
current = 0
# set brightness
brightness = max(0, brightness_delta + current)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_BRIGHTNESS_PCT: brightness,
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.ColorController', 'SetColor'))
@extract_entity
@asyncio.coroutine
def async_api_set_color(hass, config, request, entity):
"""Process a set color request."""
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES)
rgb = color_util.color_hsb_to_RGB(
float(request[API_PAYLOAD]['color']['hue']),
float(request[API_PAYLOAD]['color']['saturation']),
float(request[API_PAYLOAD]['color']['brightness'])
)
if supported & light.SUPPORT_RGB_COLOR > 0:
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_RGB_COLOR: rgb,
}, blocking=False)
else:
xyz = color_util.color_RGB_to_xy(*rgb)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_XY_COLOR: (xyz[0], xyz[1]),
light.ATTR_BRIGHTNESS: xyz[2],
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.ColorTemperatureController', 'SetColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_set_color_temperature(hass, config, request, entity):
"""Process a set color temperature request."""
kelvin = int(request[API_PAYLOAD]['colorTemperatureInKelvin'])
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_KELVIN: kelvin,
}, blocking=False)
return api_message(request)
@HANDLERS.register(
('Alexa.ColorTemperatureController', 'DecreaseColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_decrease_color_temp(hass, config, request, entity):
"""Process a decrease color temperature request."""
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS))
value = min(max_mireds, current + 50)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_COLOR_TEMP: value,
}, blocking=False)
return api_message(request)
@HANDLERS.register(
('Alexa.ColorTemperatureController', 'IncreaseColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_increase_color_temp(hass, config, request, entity):
"""Process a increase color temperature request."""
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
value = max(min_mireds, current - 50)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_COLOR_TEMP: value,
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.SceneController', 'Activate'))
@extract_entity
@asyncio.coroutine
def async_api_activate(hass, config, request, entity):
"""Process a activate request."""
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
@extract_entity
@asyncio.coroutine
def async_api_set_percentage(hass, config, request, entity):
"""Process a set percentage request."""
percentage = int(request[API_PAYLOAD]['percentage'])
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == fan.DOMAIN:
service = fan.SERVICE_SET_SPEED
speed = "off"
if percentage <= 33:
speed = "low"
elif percentage <= 66:
speed = "medium"
elif percentage <= 100:
speed = "high"
data[fan.ATTR_SPEED] = speed
elif entity.domain == cover.DOMAIN:
service = SERVICE_SET_COVER_POSITION
data[cover.ATTR_POSITION] = percentage
yield from hass.services.async_call(
entity.domain, service, data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_percentage(hass, config, request, entity):
"""Process a adjust percentage request."""
percentage_delta = int(request[API_PAYLOAD]['percentageDelta'])
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == fan.DOMAIN:
service = fan.SERVICE_SET_SPEED
speed = entity.attributes.get(fan.ATTR_SPEED)
if speed == "off":
current = 0
elif speed == "low":
current = 33
elif speed == "medium":
current = 66
elif speed == "high":
current = 100
# set percentage
percentage = max(0, percentage_delta + current)
speed = "off"
if percentage <= 33:
speed = "low"
elif percentage <= 66:
speed = "medium"
elif percentage <= 100:
speed = "high"
data[fan.ATTR_SPEED] = speed
elif entity.domain == cover.DOMAIN:
service = SERVICE_SET_COVER_POSITION
current = entity.attributes.get(cover.ATTR_POSITION)
data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
yield from hass.services.async_call(
entity.domain, service, data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.LockController', 'Lock'))
@extract_entity
@asyncio.coroutine
def async_api_lock(hass, config, request, entity):
"""Process a lock request."""
yield from hass.services.async_call(entity.domain, SERVICE_LOCK, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
return api_message(request)
# Not supported by Alexa yet
@HANDLERS.register(('Alexa.LockController', 'Unlock'))
@extract_entity
@asyncio.coroutine
def async_api_unlock(hass, config, request, entity):
"""Process a unlock request."""
yield from hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'SetVolume'))
@extract_entity
@asyncio.coroutine
def async_api_set_volume(hass, config, request, entity):
"""Process a set volume request."""
volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2)
data = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
yield from hass.services.async_call(
entity.domain, SERVICE_VOLUME_SET,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_volume(hass, config, request, entity):
"""Process a adjust volume request."""
volume_delta = int(request[API_PAYLOAD]['volume'])
current_level = entity.attributes.get(media_player.ATTR_MEDIA_VOLUME_LEVEL)
# read current state
try:
current = math.floor(int(current_level * 100))
except ZeroDivisionError:
current = 0
volume = float(max(0, volume_delta + current) / 100)
data = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
yield from hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_SET,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'SetMute'))
@extract_entity
@asyncio.coroutine
def async_api_set_mute(hass, config, request, entity):
"""Process a set mute request."""
mute = bool(request[API_PAYLOAD]['mute'])
data = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
}
yield from hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_MUTE,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Play'))
@extract_entity
@asyncio.coroutine
def async_api_play(hass, config, request, entity):
"""Process a play request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
entity.domain, SERVICE_MEDIA_PLAY,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Pause'))
@extract_entity
@asyncio.coroutine
def async_api_pause(hass, config, request, entity):
"""Process a pause request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
entity.domain, SERVICE_MEDIA_PAUSE,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Stop'))
@extract_entity
@asyncio.coroutine
def async_api_stop(hass, config, request, entity):
"""Process a stop request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
entity.domain, SERVICE_MEDIA_STOP,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Next'))
@extract_entity
@asyncio.coroutine
def async_api_next(hass, config, request, entity):
"""Process a next request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
entity.domain, SERVICE_MEDIA_NEXT_TRACK,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Previous'))
@extract_entity
@asyncio.coroutine
def async_api_previous(hass, config, request, entity):
"""Process a previous request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
entity.domain, SERVICE_MEDIA_PREVIOUS_TRACK,
data, blocking=False)
return api_message(request)

View File

@@ -89,6 +89,7 @@ def setup(hass, config):
"""Set up the Amcrest IP Camera component."""
from amcrest import AmcrestCamera
hass.data[DATA_AMCREST] = {}
amcrest_cams = config[DOMAIN]
for device in amcrest_cams:
@@ -126,22 +127,34 @@ def setup(hass, config):
else:
authentication = None
hass.data[DATA_AMCREST][name] = AmcrestDevice(
camera, name, authentication, ffmpeg_arguments, stream_source,
resolution)
discovery.load_platform(
hass, 'camera', DOMAIN, {
'device': camera,
CONF_AUTHENTICATION: authentication,
CONF_FFMPEG_ARGUMENTS: ffmpeg_arguments,
CONF_NAME: name,
CONF_RESOLUTION: resolution,
CONF_STREAM_SOURCE: stream_source,
}, config)
if sensors:
discovery.load_platform(
hass, 'sensor', DOMAIN, {
'device': camera,
CONF_NAME: name,
CONF_SENSORS: sensors,
}, config)
return True
class AmcrestDevice(object):
"""Representation of a base Amcrest discovery device."""
def __init__(self, camera, name, authentication, ffmpeg_arguments,
stream_source, resolution):
"""Initialize the entity."""
self.device = camera
self.name = name
self.authentication = authentication
self.ffmpeg_arguments = ffmpeg_arguments
self.stream_source = stream_source
self.resolution = resolution

View File

@@ -24,6 +24,7 @@ from homeassistant.const import (
__version__)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import AsyncTrackStates
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.helpers import template
from homeassistant.components.http import HomeAssistantView
@@ -262,7 +263,11 @@ class APIEventView(HomeAssistantView):
def post(self, request, event_type):
"""Fire events."""
body = yield from request.text()
event_data = json.loads(body) if body else None
try:
event_data = json.loads(body) if body else None
except ValueError:
return self.json_message('Event data should be valid JSON',
HTTP_BAD_REQUEST)
if event_data is not None and not isinstance(event_data, dict):
return self.json_message('Event data should be a JSON object',
@@ -289,10 +294,11 @@ class APIServicesView(HomeAssistantView):
url = URL_API_SERVICES
name = "api:services"
@ha.callback
@asyncio.coroutine
def get(self, request):
"""Get registered services."""
return self.json(async_services_json(request.app['hass']))
services = yield from async_services_json(request.app['hass'])
return self.json(services)
class APIDomainServicesView(HomeAssistantView):
@@ -309,7 +315,11 @@ class APIDomainServicesView(HomeAssistantView):
"""
hass = request.app['hass']
body = yield from request.text()
data = json.loads(body) if body else None
try:
data = json.loads(body) if body else None
except ValueError:
return self.json_message('Data should be valid JSON',
HTTP_BAD_REQUEST)
with AsyncTrackStates(hass) as changed_states:
yield from hass.services.async_call(domain, service, data, True)
@@ -347,10 +357,12 @@ class APITemplateView(HomeAssistantView):
HTTP_BAD_REQUEST)
@asyncio.coroutine
def async_services_json(hass):
"""Generate services data to JSONify."""
descriptions = yield from async_get_all_descriptions(hass)
return [{"domain": key, "services": value}
for key, value in hass.services.async_services().items()]
for key, value in descriptions.items()]
def async_events_json(hass):

View File

@@ -1,133 +0,0 @@
"""
Support for API.AI webhook.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/apiai/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.const import PROJECT_NAME, HTTP_BAD_REQUEST
from homeassistant.helpers import intent, template
from homeassistant.components.http import HomeAssistantView
_LOGGER = logging.getLogger(__name__)
INTENTS_API_ENDPOINT = '/api/apiai'
CONF_INTENTS = 'intents'
CONF_SPEECH = 'speech'
CONF_ACTION = 'action'
CONF_ASYNC_ACTION = 'async_action'
DEFAULT_CONF_ASYNC_ACTION = False
DOMAIN = 'apiai'
DEPENDENCIES = ['http']
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {}
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Activate API.AI component."""
hass.http.register_view(ApiaiIntentsView)
return True
class ApiaiIntentsView(HomeAssistantView):
"""Handle API.AI requests."""
url = INTENTS_API_ENDPOINT
name = 'api:apiai'
@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)
req = data.get('result')
if req is None:
_LOGGER.error("Received invalid data from api.ai: %s", data)
return self.json_message(
"Expected result value not received", HTTP_BAD_REQUEST)
action_incomplete = req['actionIncomplete']
if action_incomplete:
return None
action = req.get('action')
parameters = req.get('parameters')
apiai_response = ApiaiResponse(parameters)
if action == "":
_LOGGER.warning("Received intent with empty action")
apiai_response.add_speech(
"You have not defined an action in your api.ai intent.")
return self.json(apiai_response)
try:
intent_response = yield from intent.async_handle(
hass, DOMAIN, action,
{key: {'value': value} for key, value
in parameters.items()})
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)
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 'plain' in intent_response.speech:
apiai_response.add_speech(
intent_response.speech['plain']['speech'])
return self.json(apiai_response)
class ApiaiResponse(object):
"""Help generating the response for API.AI."""
def __init__(self, parameters):
"""Initialize the response."""
self.speech = None
self.parameters = {}
# Parameter names replace '.' and '-' for '_'
for key, value in parameters.items():
underscored_key = key.replace('.', '_').replace('-', '_')
self.parameters[underscored_key] = value
def add_speech(self, text):
"""Add speech to the response."""
assert self.speech is None
if isinstance(text, template.Template):
text = text.async_render(self.parameters)
self.speech = text
def as_dict(self):
"""Return response in an API.AI valid dict."""
return {
'speech': self.speech,
'displayText': self.speech,
'source': PROJECT_NAME,
}

View File

@@ -4,7 +4,6 @@ Support for Apple TV.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/apple_tv/
"""
import os
import asyncio
import logging
@@ -12,13 +11,12 @@ 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
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyatv==0.3.5']
REQUIREMENTS = ['pyatv==0.3.9']
_LOGGER = logging.getLogger(__name__)
@@ -183,18 +181,12 @@ def async_setup(hass, config):
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
hass.services.async_register(
DOMAIN, SERVICE_SCAN, async_service_handler,
descriptions.get(SERVICE_SCAN),
schema=APPLE_TV_SCAN_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_AUTHENTICATE, async_service_handler,
descriptions.get(SERVICE_AUTHENTICATE),
schema=APPLE_TV_AUTHENTICATE_SCHEMA)
return True

View File

@@ -12,7 +12,7 @@ from requests.exceptions import HTTPError, ConnectTimeout
from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
REQUIREMENTS = ['pyarlo==0.0.7']
REQUIREMENTS = ['pyarlo==0.1.2']
_LOGGER = logging.getLogger(__name__)

View File

@@ -7,14 +7,12 @@ https://home-assistant.io/components/automation/
import asyncio
from functools import partial
import logging
import os
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,
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
@@ -166,11 +164,6 @@ def async_setup(hass, config):
yield from _async_process_config(hass, config, component)
descriptions = yield from hass.async_add_job(
conf_util.load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml')
)
@asyncio.coroutine
def trigger_service_handler(service_call):
"""Handle automation triggers."""
@@ -216,20 +209,20 @@ def async_setup(hass, config):
hass.services.async_register(
DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
descriptions.get(SERVICE_TRIGGER), schema=TRIGGER_SERVICE_SCHEMA)
schema=TRIGGER_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_RELOAD, reload_service_handler,
descriptions.get(SERVICE_RELOAD), schema=RELOAD_SERVICE_SCHEMA)
schema=RELOAD_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, toggle_service_handler,
descriptions.get(SERVICE_TOGGLE), schema=SERVICE_SCHEMA)
schema=SERVICE_SCHEMA)
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF):
hass.services.async_register(
DOMAIN, service, turn_onoff_service_handler,
descriptions.get(service), schema=SERVICE_SCHEMA)
schema=SERVICE_SCHEMA)
return True

View File

@@ -21,7 +21,7 @@ _LOGGER = logging.getLogger(__name__)
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'event',
vol.Required(CONF_EVENT_TYPE): cv.string,
vol.Optional(CONF_EVENT_DATA, default={}): dict,
vol.Optional(CONF_EVENT_DATA): dict,
})
@@ -31,16 +31,19 @@ def async_trigger(hass, config, action):
event_type = config.get(CONF_EVENT_TYPE)
event_data_schema = vol.Schema(
config.get(CONF_EVENT_DATA),
extra=vol.ALLOW_EXTRA)
extra=vol.ALLOW_EXTRA) if config.get(CONF_EVENT_DATA) else None
@callback
def handle_event(event):
"""Listen for events and calls the action when data matches."""
try:
event_data_schema(event.data)
except vol.Invalid:
# If event data doesn't match requested schema, skip event
return
if event_data_schema:
# Check that the event data matches the configured
# schema if one was provided
try:
event_data_schema(event.data)
except vol.Invalid:
# If event data doesn't match requested schema, skip event
return
hass.async_run_job(action, {
'trigger': {

View File

@@ -37,14 +37,15 @@ def async_trigger(hass, config, action):
above = config.get(CONF_ABOVE)
time_delta = config.get(CONF_FOR)
value_template = config.get(CONF_VALUE_TEMPLATE)
async_remove_track_same = None
unsub_track_same = {}
entities_triggered = set()
if value_template is not None:
value_template.hass = hass
@callback
def check_numeric_state(entity, from_s, to_s):
"""Return True if they should trigger."""
"""Return True if criteria are now met."""
if to_s is None:
return False
@@ -56,51 +57,39 @@ def async_trigger(hass, config, action):
'above': above,
}
}
# 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
return condition.async_numeric_state(
hass, to_s, below, above, value_template, variables)
@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
@callback
def call_action():
"""Call action with right context."""
hass.async_run_job(action, variables)
hass.async_run_job(action, {
'trigger': {
'platform': 'numeric_state',
'entity_id': entity,
'below': below,
'above': above,
'from_state': from_s,
'to_state': to_s,
}
})
if not time_delta:
call_action()
return
matching = check_numeric_state(entity, from_s, to_s)
async_remove_track_same = async_track_same_state(
hass, time_delta, call_action, entity_ids=entity_id,
async_check_same_func=check_numeric_state)
if not matching:
entities_triggered.discard(entity)
elif entity not in entities_triggered:
entities_triggered.add(entity)
if time_delta:
unsub_track_same[entity] = async_track_same_state(
hass, time_delta, call_action, entity_ids=entity_id,
async_check_same_func=check_numeric_state)
else:
call_action()
unsub = async_track_state_change(
hass, entity_id, state_automation_listener)
@@ -109,7 +98,8 @@ def async_trigger(hass, config, action):
def async_remove():
"""Remove state listeners async."""
unsub()
if async_remove_track_same:
async_remove_track_same() # pylint: disable=not-callable
for async_remove in unsub_track_same.values():
async_remove()
unsub_track_same.clear()
return async_remove

View File

@@ -1,6 +1,7 @@
# Describes the format for available automation services
turn_on:
description: Enable an automation.
fields:
entity_id:
description: Name of the automation to turn on.
@@ -8,7 +9,6 @@ turn_on:
turn_off:
description: Disable an automation.
fields:
entity_id:
description: Name of the automation to turn off.
@@ -16,7 +16,6 @@ turn_off:
toggle:
description: Toggle an automation.
fields:
entity_id:
description: Name of the automation to toggle on/off.
@@ -24,7 +23,6 @@ toggle:
trigger:
description: Trigger the action of an automation.
fields:
entity_id:
description: Name of the automation to trigger.

View File

@@ -35,13 +35,11 @@ def async_trigger(hass, config, action):
to_state = config.get(CONF_TO, MATCH_ALL)
time_delta = config.get(CONF_FOR)
match_all = (from_state == MATCH_ALL and to_state == MATCH_ALL)
async_remove_track_same = None
unsub_track_same = {}
@callback
def state_automation_listener(entity, from_s, to_s):
"""Listen for state changes and calls action."""
nonlocal async_remove_track_same
@callback
def call_action():
"""Call action with right context."""
@@ -57,14 +55,14 @@ def async_trigger(hass, config, action):
# Ignore changes to state attributes if from/to is in use
if (not match_all and from_s is not None and to_s is not None and
from_s.last_changed == to_s.last_changed):
from_s.state == to_s.state):
return
if not time_delta:
call_action()
return
async_remove_track_same = async_track_same_state(
unsub_track_same[entity] = async_track_same_state(
hass, time_delta, call_action,
lambda _, _2, to_state: to_state.state == to_s.state,
entity_ids=entity_id)
@@ -76,7 +74,8 @@ def async_trigger(hass, config, action):
def async_remove():
"""Remove state listeners async."""
unsub()
if async_remove_track_same:
async_remove_track_same() # pylint: disable=not-callable
for async_remove in unsub_track_same.values():
async_remove()
unsub_track_same.clear()
return async_remove

View File

@@ -5,25 +5,24 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/axis/
"""
import json
import logging
import os
import voluptuous as vol
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_HOST, CONF_INCLUDE, CONF_NAME,
CONF_PASSWORD, CONF_PORT, CONF_TRIGGER_TIME,
CONF_USERNAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.components.discovery import SERVICE_AXIS
from homeassistant.const import (ATTR_LOCATION, ATTR_TRIPPED,
CONF_EVENT, CONF_HOST, CONF_INCLUDE,
CONF_NAME, CONF_PASSWORD, CONF_PORT,
CONF_TRIGGER_TIME, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.helpers.entity import Entity
from homeassistant.util.json import load_json, save_json
REQUIREMENTS = ['axis==12']
REQUIREMENTS = ['axis==14']
_LOGGER = logging.getLogger(__name__)
@@ -87,10 +86,13 @@ def request_configuration(hass, config, name, host, serialnumber):
configurator.notify_errors(request_id,
"Functionality mandatory.")
return False
callback_data[CONF_INCLUDE] = callback_data[CONF_INCLUDE].split()
callback_data[CONF_HOST] = host
if CONF_NAME not in callback_data:
callback_data[CONF_NAME] = name
try:
device_config = DEVICE_SCHEMA(callback_data)
except vol.Invalid:
@@ -99,10 +101,9 @@ def request_configuration(hass, config, name, host, serialnumber):
return False
if setup_device(hass, config, device_config):
config_file = _read_config(hass)
config_file = load_json(hass.config.path(CONFIG_FILE))
config_file[serialnumber] = dict(device_config)
del config_file[serialnumber]['hass']
_write_config(hass, config_file)
save_json(hass.config.path(CONFIG_FILE), config_file)
configurator.request_done(request_id)
else:
configurator.notify_errors(request_id,
@@ -146,10 +147,10 @@ def request_configuration(hass, config, name, host, serialnumber):
def setup(hass, config):
"""Common setup for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
"""Stop the metadatastream on shutdown."""
"""Stop the event stream on shutdown."""
for serialnumber, device in AXIS_DEVICES.items():
_LOGGER.info("Stopping metadatastream for %s.", serialnumber)
device.stop_metadatastream()
_LOGGER.info("Stopping event stream for %s.", serialnumber)
device.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)
@@ -160,9 +161,9 @@ def setup(hass, config):
serialnumber = discovery_info['properties']['macaddress']
if serialnumber not in AXIS_DEVICES:
config_file = _read_config(hass)
config_file = load_json(hass.config.path(CONFIG_FILE))
if serialnumber in config_file:
# Device config saved to file
# Device config previously saved to file
try:
device_config = DEVICE_SCHEMA(config_file[serialnumber])
device_config[CONF_HOST] = host
@@ -178,10 +179,8 @@ def setup(hass, config):
else:
# Device already registered, but on a different IP
device = AXIS_DEVICES[serialnumber]
device.url = host
async_dispatcher_send(hass,
DOMAIN + '_' + device.name + '_new_ip',
host)
device.config.host = host
dispatcher_send(hass, DOMAIN + '_' + device.name + '_new_ip', host)
# Register discovery service
discovery.listen(hass, SERVICE_AXIS, axis_device_discovered)
@@ -194,18 +193,15 @@ def setup(hass, config):
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(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
def vapix_service(call):
"""Service to send a message."""
for _, device in AXIS_DEVICES.items():
if device.name == call.data[CONF_NAME]:
response = device.do_request(call.data[SERVICE_CGI],
call.data[SERVICE_ACTION],
call.data[SERVICE_PARAM])
hass.bus.async_fire(SERVICE_VAPIX_CALL_RESPONSE, response)
response = device.vapix.do_request(
call.data[SERVICE_CGI],
call.data[SERVICE_ACTION],
call.data[SERVICE_PARAM])
hass.bus.fire(SERVICE_VAPIX_CALL_RESPONSE, response)
return True
_LOGGER.info("Couldn\'t find device %s", call.data[CONF_NAME])
return False
@@ -214,9 +210,7 @@ def setup(hass, config):
hass.services.register(DOMAIN,
SERVICE_VAPIX_CALL,
vapix_service,
descriptions[DOMAIN][SERVICE_VAPIX_CALL],
schema=SERVICE_SCHEMA)
return True
@@ -224,9 +218,28 @@ def setup_device(hass, config, device_config):
"""Set up device."""
from axis import AxisDevice
device_config['hass'] = hass
device = AxisDevice(device_config) # Initialize device
enable_metadatastream = False
def signal_callback(action, event):
"""Callback to configure events when initialized on event stream."""
if action == 'add':
event_config = {
CONF_EVENT: event,
CONF_NAME: device_config[CONF_NAME],
ATTR_LOCATION: device_config[ATTR_LOCATION],
CONF_TRIGGER_TIME: device_config[CONF_TRIGGER_TIME]
}
component = event.event_platform
discovery.load_platform(hass,
component,
DOMAIN,
event_config,
config)
event_types = list(filter(lambda x: x in device_config[CONF_INCLUDE],
EVENT_TYPES))
device_config['events'] = event_types
device_config['signal'] = signal_callback
device = AxisDevice(hass.loop, **device_config)
device.name = device_config[CONF_NAME]
if device.serial_number is None:
# If there is no serial number a connection could not be made
@@ -234,16 +247,10 @@ def setup_device(hass, config, device_config):
return False
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
device.add_event_topic(convert(component, 'type', 'subscribe'))
if not enable_metadatastream:
enable_metadatastream = True
else:
if component == 'camera':
camera_config = {
CONF_HOST: device_config[CONF_HOST],
CONF_NAME: device_config[CONF_NAME],
CONF_HOST: device_config[CONF_HOST],
CONF_PORT: device_config[CONF_PORT],
CONF_USERNAME: device_config[CONF_USERNAME],
CONF_PASSWORD: device_config[CONF_PASSWORD]
@@ -254,58 +261,22 @@ def setup_device(hass, config, device_config):
camera_config,
config)
if enable_metadatastream:
device.initialize_new_event = event_initialized
if not device.initiate_metadatastream():
hass.components.persistent_notification.create(
'Dependency missing for sensors, '
'please check documentation',
title=DOMAIN,
notification_id='axis_notification')
AXIS_DEVICES[device.serial_number] = device
if event_types:
hass.add_job(device.start)
return True
def _read_config(hass):
"""Read Axis config."""
path = hass.config.path(CONFIG_FILE)
if not os.path.isfile(path):
return {}
with open(path) as f_handle:
# Guard against empty file
return json.loads(f_handle.read() or '{}')
def _write_config(hass, config):
"""Write Axis config."""
data = json.dumps(config)
with open(hass.config.path(CONFIG_FILE), 'w', encoding='utf-8') as outfile:
outfile.write(data)
def event_initialized(event):
"""Register event initialized on metadatastream here."""
hass = event.device_config('hass')
discovery.load_platform(hass,
convert(event.topic, 'topic', 'platform'),
DOMAIN, {'axis_event': event})
class AxisDeviceEvent(Entity):
"""Representation of a Axis device event."""
def __init__(self, axis_event):
def __init__(self, event_config):
"""Initialize the event."""
self.axis_event = axis_event
self._event_class = convert(self.axis_event.topic, 'topic', 'class')
self._name = '{}_{}_{}'.format(self.axis_event.device_name,
convert(self.axis_event.topic,
'topic', 'type'),
self.axis_event = event_config[CONF_EVENT]
self._name = '{}_{}_{}'.format(event_config[CONF_NAME],
self.axis_event.event_type,
self.axis_event.id)
self.location = event_config[ATTR_LOCATION]
self.axis_event.callback = self._update_callback
def _update_callback(self):
@@ -321,7 +292,7 @@ class AxisDeviceEvent(Entity):
@property
def device_class(self):
"""Return the class of the event."""
return self._event_class
return self.axis_event.event_class
@property
def should_poll(self):
@@ -336,52 +307,6 @@ class AxisDeviceEvent(Entity):
tripped = self.axis_event.is_tripped
attr[ATTR_TRIPPED] = 'True' if tripped else 'False'
location = self.axis_event.device_config(ATTR_LOCATION)
if location:
attr[ATTR_LOCATION] = location
attr[ATTR_LOCATION] = self.location
return attr
def convert(item, from_key, to_key):
"""Translate between Axis and HASS syntax."""
for entry in REMAP:
if entry[from_key] == item:
return entry[to_key]
REMAP = [{'type': 'motion',
'class': 'motion',
'topic': 'tns1:VideoAnalytics/tnsaxis:MotionDetection',
'subscribe': 'onvif:VideoAnalytics/axis:MotionDetection',
'platform': 'binary_sensor'},
{'type': 'vmd3',
'class': 'motion',
'topic': 'tns1:RuleEngine/tnsaxis:VMD3/vmd3_video_1',
'subscribe': 'onvif:RuleEngine/axis:VMD3/vmd3_video_1',
'platform': 'binary_sensor'},
{'type': 'pir',
'class': 'motion',
'topic': 'tns1:Device/tnsaxis:Sensor/PIR',
'subscribe': 'onvif:Device/axis:Sensor/axis:PIR',
'platform': 'binary_sensor'},
{'type': 'sound',
'class': 'sound',
'topic': 'tns1:AudioSource/tnsaxis:TriggerLevel',
'subscribe': 'onvif:AudioSource/axis:TriggerLevel',
'platform': 'binary_sensor'},
{'type': 'daynight',
'class': 'light',
'topic': 'tns1:VideoSource/tnsaxis:DayNightVision',
'subscribe': 'onvif:VideoSource/axis:DayNightVision',
'platform': 'binary_sensor'},
{'type': 'tampering',
'class': 'safety',
'topic': 'tns1:VideoSource/tnsaxis:Tampering',
'subscribe': 'onvif:VideoSource/axis:Tampering',
'platform': 'binary_sensor'},
{'type': 'input',
'class': 'input',
'topic': 'tns1:Device/tnsaxis:IO/Port',
'subscribe': 'onvif:Device/axis:IO/Port',
'platform': 'binary_sensor'}, ]

View File

@@ -20,21 +20,28 @@ SCAN_INTERVAL = timedelta(seconds=30)
ENTITY_ID_FORMAT = DOMAIN + '.{}'
DEVICE_CLASSES = [
'cold', # On means cold (or too cold)
'connectivity', # On means connection present, Off = no connection
'gas', # CO, CO2, etc.
'heat', # On means hot (or too hot)
'light', # Lightness threshold
'moisture', # Specifically a wetness sensor
'motion', # Motion sensor
'moving', # On means moving, Off means stopped
'occupancy', # On means occupied, Off means not occupied
'opening', # Door, window, etc.
'power', # Power, over-current, etc
'safety', # Generic on=unsafe, off=safe
'smoke', # Smoke detector
'sound', # On means sound detected, Off means no sound
'battery', # On means low, Off means normal
'cold', # On means cold, Off means normal
'connectivity', # On means connected, Off means disconnected
'door', # On means open, Off means closed
'garage_door', # On means open, Off means closed
'gas', # On means gas detected, Off means no gas (clear)
'heat', # On means hot, Off means normal
'light', # On means light detected, Off means no light
'moisture', # On means wet, Off means dry
'motion', # On means motion detected, Off means no motion (clear)
'moving', # On means moving, Off means not moving (stopped)
'occupancy', # On means occupied, Off means not occupied (clear)
'opening', # On means open, Off means closed
'plug', # On means plugged in, Off means unplugged
'power', # On means power detected, Off means no power
'presence', # On means home, Off means away
'problem', # On means problem detected, Off means no problem (OK)
'safety', # On means unsafe, Off means safe
'smoke', # On means smoke detected, Off means no smoke (clear)
'sound', # On means sound detected, Off means no sound (clear)
'vibration', # On means vibration detected, Off means no vibration
'window', # On means open, Off means closed
]
DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))

View File

@@ -0,0 +1,87 @@
"""
Support for ADS binary sensors.
For more details about this platform, please refer to the documentation.
https://home-assistant.io/components/binary_sensor.ads/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorDevice, \
PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA
from homeassistant.components.ads import DATA_ADS, CONF_ADS_VAR
from homeassistant.const import CONF_NAME, CONF_DEVICE_CLASS
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['ads']
DEFAULT_NAME = 'ADS binary sensor'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ADS_VAR): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Binary Sensor platform for ADS."""
ads_hub = hass.data.get(DATA_ADS)
ads_var = config.get(CONF_ADS_VAR)
name = config.get(CONF_NAME)
device_class = config.get(CONF_DEVICE_CLASS)
ads_sensor = AdsBinarySensor(ads_hub, name, ads_var, device_class)
add_devices([ads_sensor])
class AdsBinarySensor(BinarySensorDevice):
"""Representation of ADS binary sensors."""
def __init__(self, ads_hub, name, ads_var, device_class):
"""Initialize AdsBinarySensor entity."""
self._name = name
self._state = False
self._device_class = device_class or 'moving'
self._ads_hub = ads_hub
self.ads_var = ads_var
@asyncio.coroutine
def async_added_to_hass(self):
"""Register device notification."""
def update(name, value):
"""Handle device notifications."""
_LOGGER.debug('Variable %s changed its value to %d',
name, value)
self._state = value
self.schedule_update_ha_state()
self.hass.async_add_job(
self._ads_hub.add_device_notification,
self.ads_var, self._ads_hub.PLCTYPE_BOOL, update
)
@property
def name(self):
"""Return the default name of the binary sensor."""
return self._name
@property
def device_class(self):
"""Return the device class."""
return self._device_class
@property
def is_on(self):
"""Return if the binary sensor is on."""
return self._state
@property
def should_poll(self):
"""Return False because entity pushes its state to HA."""
return False

View File

@@ -7,39 +7,41 @@ https://home-assistant.io/components/binary_sensor.alarmdecoder/
import asyncio
import logging
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.alarmdecoder import (ZONE_SCHEMA,
CONF_ZONES,
CONF_ZONE_NAME,
CONF_ZONE_TYPE,
SIGNAL_ZONE_FAULT,
SIGNAL_ZONE_RESTORE)
from homeassistant.components.alarmdecoder import (
ZONE_SCHEMA, CONF_ZONES, CONF_ZONE_NAME, CONF_ZONE_TYPE,
CONF_ZONE_RFID, SIGNAL_ZONE_FAULT, SIGNAL_ZONE_RESTORE,
SIGNAL_RFX_MESSAGE)
DEPENDENCIES = ['alarmdecoder']
_LOGGER = logging.getLogger(__name__)
ATTR_RF_BIT0 = 'rf_bit0'
ATTR_RF_LOW_BAT = 'rf_low_battery'
ATTR_RF_SUPERVISED = 'rf_supervised'
ATTR_RF_BIT3 = 'rf_bit3'
ATTR_RF_LOOP3 = 'rf_loop3'
ATTR_RF_LOOP2 = 'rf_loop2'
ATTR_RF_LOOP4 = 'rf_loop4'
ATTR_RF_LOOP1 = 'rf_loop1'
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the AlarmDecoder binary sensor devices."""
configured_zones = discovery_info[CONF_ZONES]
devices = []
for zone_num in configured_zones:
device_config_data = ZONE_SCHEMA(configured_zones[zone_num])
zone_type = device_config_data[CONF_ZONE_TYPE]
zone_name = device_config_data[CONF_ZONE_NAME]
zone_rfid = device_config_data.get(CONF_ZONE_RFID)
device = AlarmDecoderBinarySensor(
hass, zone_num, zone_name, zone_type)
zone_num, zone_name, zone_type, zone_rfid)
devices.append(device)
async_add_devices(devices)
add_devices(devices)
return True
@@ -47,46 +49,52 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Representation of an AlarmDecoder binary sensor."""
def __init__(self, hass, zone_number, zone_name, zone_type):
def __init__(self, zone_number, zone_name, zone_type, zone_rfid):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
self._zone_type = zone_type
self._state = 0
self._state = None
self._name = zone_name
self._type = zone_type
_LOGGER.debug("Setup up zone: %s", self._name)
self._rfid = zone_rfid
self._rfstate = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_FAULT, self._fault_callback)
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_ZONE_FAULT, self._fault_callback)
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_RESTORE, self._restore_callback)
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_ZONE_RESTORE, self._restore_callback)
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_RFX_MESSAGE, self._rfx_message_callback)
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def icon(self):
"""Icon for device by its type."""
if "window" in self._name.lower():
return "mdi:window-open" if self.is_on else "mdi:window-closed"
if self._type == 'smoke':
return "mdi:fire"
return None
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_state_attributes(self):
"""Return the state attributes."""
attr = {}
if self._rfid and self._rfstate is not None:
attr[ATTR_RF_BIT0] = True if self._rfstate & 0x01 else False
attr[ATTR_RF_LOW_BAT] = True if self._rfstate & 0x02 else False
attr[ATTR_RF_SUPERVISED] = True if self._rfstate & 0x04 else False
attr[ATTR_RF_BIT3] = True if self._rfstate & 0x08 else False
attr[ATTR_RF_LOOP3] = True if self._rfstate & 0x10 else False
attr[ATTR_RF_LOOP2] = True if self._rfstate & 0x20 else False
attr[ATTR_RF_LOOP4] = True if self._rfstate & 0x40 else False
attr[ATTR_RF_LOOP1] = True if self._rfstate & 0x80 else False
return attr
@property
def is_on(self):
"""Return true if sensor is on."""
@@ -97,16 +105,20 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Return the class of this sensor, from DEVICE_CLASSES."""
return self._zone_type
@callback
def _fault_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._state = 1
self.async_schedule_update_ha_state()
self.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.async_schedule_update_ha_state()
self.schedule_update_ha_state()
def _rfx_message_callback(self, message):
"""Update RF state."""
if self._rfid and message and message.serial_number == self._rfid:
self._rfstate = message.value
self.schedule_update_ha_state()

View File

@@ -7,25 +7,32 @@ https://home-assistant.io/components/binary_sensor.aurora/
from datetime import timedelta
import logging
from aiohttp.hdrs import USER_AGENT
import requests
import voluptuous as vol
from homeassistant.components.binary_sensor \
import (BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME)
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
CONF_THRESHOLD = "forecast_threshold"
_LOGGER = logging.getLogger(__name__)
CONF_ATTRIBUTION = "Data provided by the National Oceanic and Atmospheric" \
"Administration"
CONF_THRESHOLD = 'forecast_threshold'
DEFAULT_DEVICE_CLASS = 'visible'
DEFAULT_NAME = 'Aurora Visibility'
DEFAULT_DEVICE_CLASS = "visible"
DEFAULT_THRESHOLD = 75
HA_USER_AGENT = "Home Assistant Aurora Tracker v.0.1.0"
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
URL = "http://services.swpc.noaa.gov/text/aurora-nowcast-map.txt"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_THRESHOLD, default=DEFAULT_THRESHOLD): cv.positive_int,
@@ -43,10 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
try:
aurora_data = AuroraData(
hass.config.latitude,
hass.config.longitude,
threshold
)
hass.config.latitude, hass.config.longitude, threshold)
aurora_data.update()
except requests.exceptions.HTTPError as error:
_LOGGER.error(
@@ -85,9 +89,9 @@ class AuroraSensor(BinarySensorDevice):
attrs = {}
if self.aurora_data:
attrs["visibility_level"] = self.aurora_data.visibility_level
attrs["message"] = self.aurora_data.is_visible_text
attrs['visibility_level'] = self.aurora_data.visibility_level
attrs['message'] = self.aurora_data.is_visible_text
attrs[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION
return attrs
def update(self):
@@ -104,10 +108,7 @@ class AuroraData(object):
self.longitude = longitude
self.number_of_latitude_intervals = 513
self.number_of_longitude_intervals = 1024
self.api_url = \
"http://services.swpc.noaa.gov/text/aurora-nowcast-map.txt"
self.headers = {"User-Agent": "Home Assistant Aurora Tracker v.0.1.0"}
self.headers = {USER_AGENT: HA_USER_AGENT}
self.threshold = int(threshold)
self.is_visible = None
self.is_visible_text = None
@@ -132,14 +133,14 @@ class AuroraData(object):
def get_aurora_forecast(self):
"""Get forecast data and parse for given long/lat."""
raw_data = requests.get(self.api_url, headers=self.headers).text
raw_data = requests.get(URL, headers=self.headers, timeout=5).text
forecast_table = [
row.strip(" ").split(" ")
for row in raw_data.split("\n")
if not row.startswith("#")
]
# convert lat and long for data points in table
# Convert lat and long for data points in table
converted_latitude = round((self.latitude / 180)
* self.number_of_latitude_intervals)
converted_longitude = round((self.longitude / 360)

View File

@@ -21,19 +21,19 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Axis device event."""
add_devices([AxisBinarySensor(discovery_info['axis_event'], hass)], True)
add_devices([AxisBinarySensor(hass, discovery_info)], True)
class AxisBinarySensor(AxisDeviceEvent, BinarySensorDevice):
"""Representation of a binary Axis event."""
def __init__(self, axis_event, hass):
def __init__(self, hass, event_config):
"""Initialize the binary sensor."""
self.hass = hass
self._state = False
self._delay = axis_event.device_config(CONF_TRIGGER_TIME)
self._delay = event_config[CONF_TRIGGER_TIME]
self._timer = None
AxisDeviceEvent.__init__(self, axis_event)
AxisDeviceEvent.__init__(self, event_config)
@property
def is_on(self):

View File

@@ -22,6 +22,10 @@ from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
ATTR_OBSERVATIONS = 'observations'
ATTR_PROBABILITY = 'probability'
ATTR_PROBABILITY_THRESHOLD = 'probability_threshold'
CONF_OBSERVATIONS = 'observations'
CONF_PRIOR = 'prior'
CONF_PROBABILITY_THRESHOLD = 'probability_threshold'
@@ -29,7 +33,8 @@ CONF_P_GIVEN_F = 'prob_given_false'
CONF_P_GIVEN_T = 'prob_given_true'
CONF_TO_STATE = 'to_state'
DEFAULT_NAME = 'BayesianBinary'
DEFAULT_NAME = "Bayesian Binary Sensor"
DEFAULT_PROBABILITY_THRESHOLD = 0.5
NUMERIC_STATE_SCHEMA = vol.Schema({
CONF_PLATFORM: 'numeric_state',
@@ -49,16 +54,14 @@ STATE_SCHEMA = vol.Schema({
}, required=True)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME):
cv.string,
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_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),
vol.Optional(CONF_PROBABILITY_THRESHOLD,
default=DEFAULT_PROBABILITY_THRESHOLD): vol.Coerce(float),
})
@@ -73,16 +76,16 @@ def update_probability(prior, prob_true, prob_false):
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Threshold sensor."""
"""Set up the Bayesian Binary 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)
probability_threshold = config.get(CONF_PROBABILITY_THRESHOLD)
device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices([
BayesianBinarySensor(name, prior, observations, probability_threshold,
device_class)
BayesianBinarySensor(
name, prior, observations, probability_threshold, device_class)
], True)
@@ -107,7 +110,7 @@ class BayesianBinarySensor(BinarySensorDevice):
self.entity_obs = dict.fromkeys(to_observe, [])
for ind, obs in enumerate(self._observations):
obs["id"] = ind
obs['id'] = ind
self.entity_obs[obs['entity_id']].append(obs)
self.watchers = {
@@ -117,7 +120,7 @@ class BayesianBinarySensor(BinarySensorDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Call when entity about to be added to hass."""
"""Call when entity about to be added."""
@callback
# pylint: disable=invalid-name
def async_threshold_sensor_state_listener(entity, old_state,
@@ -135,8 +138,8 @@ class BayesianBinarySensor(BinarySensorDevice):
prior = self.prior
for obs in self.current_obs.values():
prior = update_probability(prior, obs['prob_true'],
obs['prob_false'])
prior = update_probability(
prior, obs['prob_true'], obs['prob_false'])
self.probability = prior
self.hass.async_add_job(self.async_update_ha_state, True)
@@ -206,9 +209,9 @@ class BayesianBinarySensor(BinarySensorDevice):
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
ATTR_OBSERVATIONS: [val for val in self.current_obs.values()],
ATTR_PROBABILITY: round(self.probability, 2),
ATTR_PROBABILITY_THRESHOLD: self._probability_threshold,
}
@asyncio.coroutine

11
homeassistant/components/binary_sensor/concord232.py Executable file → Normal file
View File

@@ -15,7 +15,7 @@ from homeassistant.components.binary_sensor import (
from homeassistant.const import (CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['concord232==0.14']
REQUIREMENTS = ['concord232==0.15']
_LOGGER = logging.getLogger(__name__)
@@ -62,6 +62,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
return False
# The order of zones returned by client.list_zones() can vary.
# When the zones are not named, this can result in the same entity
# name mapping to different sensors in an unpredictable way. Sort
# the zones by zone number to prevent this.
client.zones.sort(key=lambda zone: zone['number'])
for zone in client.zones:
_LOGGER.info("Loading Zone found: %s", zone['name'])
if zone['number'] not in exclude:
@@ -118,7 +125,7 @@ class Concord232ZoneSensor(BinarySensorDevice):
def is_on(self):
"""Return true if the binary sensor is on."""
# True means "faulted" or "open" or "abnormal state"
return bool(self._zone['state'] == 'Normal')
return bool(self._zone['state'] != 'Normal')
def update(self):
"""Get updated stats from API."""

View File

@@ -0,0 +1,97 @@
"""
Support for deCONZ binary sensor.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.deconz/
"""
import asyncio
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.deconz import DOMAIN as DECONZ_DATA
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import callback
DEPENDENCIES = ['deconz']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup binary sensor for deCONZ component."""
if discovery_info is None:
return
from pydeconz.sensor import DECONZ_BINARY_SENSOR
sensors = hass.data[DECONZ_DATA].sensors
entities = []
for sensor in sensors.values():
if sensor.type in DECONZ_BINARY_SENSOR:
entities.append(DeconzBinarySensor(sensor))
async_add_devices(entities, True)
class DeconzBinarySensor(BinarySensorDevice):
"""Representation of a binary sensor."""
def __init__(self, sensor):
"""Setup sensor and add update callback to get data from websocket."""
self._sensor = sensor
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe sensors events."""
self._sensor.register_async_callback(self.async_update_callback)
@callback
def async_update_callback(self, reason):
"""Update the sensor's state.
If reason is that state is updated,
or reachable has changed or battery has changed.
"""
if reason['state'] or \
'reachable' in reason['attr'] or \
'battery' in reason['attr']:
self.async_schedule_update_ha_state()
@property
def is_on(self):
"""Return true if sensor is on."""
return self._sensor.is_tripped
@property
def name(self):
"""Return the name of the sensor."""
return self._sensor.name
@property
def device_class(self):
"""Class of the sensor."""
return self._sensor.sensor_class
@property
def icon(self):
"""Return the icon to use in the frontend."""
return self._sensor.sensor_icon
@property
def available(self):
"""Return True if sensor is available."""
return self._sensor.reachable
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
from pydeconz.sensor import PRESENCE
attr = {
ATTR_BATTERY_LEVEL: self._sensor.battery,
}
if self._sensor.type == PRESENCE:
attr['dark'] = self._sensor.dark
return attr

View File

@@ -1,60 +0,0 @@
"""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

@@ -0,0 +1,69 @@
"""
Support for binary sensor using GC100.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.gc100/
"""
import voluptuous as vol
from homeassistant.components.gc100 import DATA_GC100, CONF_PORTS
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import DEVICE_DEFAULT_NAME
import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['gc100']
_SENSORS_SCHEMA = vol.Schema({
cv.string: cv.string,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PORTS): vol.All(cv.ensure_list, [_SENSORS_SCHEMA])
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the GC100 devices."""
binary_sensors = []
ports = config.get(CONF_PORTS)
for port in ports:
for port_addr, port_name in port.items():
binary_sensors.append(GC100BinarySensor(
port_name, port_addr, hass.data[DATA_GC100]))
add_devices(binary_sensors, True)
class GC100BinarySensor(BinarySensorDevice):
"""Representation of a binary sensor from GC100."""
def __init__(self, name, port_addr, gc100):
"""Initialize the GC100 binary sensor."""
# pylint: disable=no-member
self._name = name or DEVICE_DEFAULT_NAME
self._port_addr = port_addr
self._gc100 = gc100
self._state = None
# Subscribe to be notified about state changes (PUSH)
self._gc100.subscribe(self._port_addr, self.set_state)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return the state of the entity."""
return self._state
def update(self):
"""Update the sensor state."""
self._gc100.read_sensor(self._port_addr, self.set_state)
def set_state(self, state):
"""Set the current state."""
self._state = state == 1
self.schedule_update_ha_state()

View File

@@ -0,0 +1,63 @@
"""
Support for the Hive devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.hive/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.hive import DATA_HIVE
DEPENDENCIES = ['hive']
DEVICETYPE_DEVICE_CLASS = {'motionsensor': 'motion',
'contactsensor': 'opening'}
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Hive sensor devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
add_devices([HiveBinarySensorEntity(session, discovery_info)])
class HiveBinarySensorEntity(BinarySensorDevice):
"""Representation of a Hive binary sensor."""
def __init__(self, hivesession, hivedevice):
"""Initialize the hive sensor."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.node_device_type = hivedevice["Hive_DeviceType"]
self.session = hivesession
self.data_updatesource = '{}.{}'.format(self.device_type,
self.node_id)
self.session.entities.append(self)
def handle_update(self, updatesource):
"""Handle the new update request."""
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource:
self.schedule_update_ha_state()
@property
def device_class(self):
"""Return the class of this sensor."""
return DEVICETYPE_DEVICE_CLASS.get(self.node_device_type)
@property
def name(self):
"""Return the name of the binary sensor."""
return self.node_name
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.session.sensor.get_state(self.node_id,
self.node_device_type)
def update(self):
"""Update all Node data frome Hive."""
self.session.core.update_data(self.node_id)

View File

@@ -25,6 +25,7 @@ SENSOR_TYPES_CLASS = {
'RemoteMotion': None,
'WeatherSensor': None,
'TiltSensor': None,
'PresenceIP': 'motion',
}

View File

@@ -4,67 +4,364 @@ Support for ISY994 binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.isy994/
"""
import asyncio
import logging
from datetime import timedelta
from typing import Callable # noqa
from homeassistant.core import callback
from homeassistant.components.binary_sensor import BinarySensorDevice, DOMAIN
import homeassistant.components.isy994 as isy
from homeassistant.components.isy994 import (ISY994_NODES, ISY994_PROGRAMS,
ISYDevice)
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
VALUE_TO_STATE = {
False: STATE_OFF,
True: STATE_ON,
ISY_DEVICE_TYPES = {
'moisture': ['16.8', '16.13', '16.14'],
'opening': ['16.9', '16.6', '16.7', '16.2', '16.17', '16.20', '16.21'],
'motion': ['16.1', '16.4', '16.5', '16.3']
}
UOM = ['2', '78']
STATES = [STATE_OFF, STATE_ON, 'true', 'false']
# pylint: disable=unused-argument
def setup_platform(hass, config: ConfigType,
add_devices: Callable[[list], None], discovery_info=None):
"""Set up the ISY994 binary sensor platform."""
if isy.ISY is None or not isy.ISY.connected:
_LOGGER.error("A connection has not been made to the ISY controller")
return False
devices = []
devices_by_nid = {}
child_nodes = []
for node in isy.filter_nodes(isy.SENSOR_NODES, units=UOM,
states=STATES):
devices.append(ISYBinarySensorDevice(node))
for program in isy.PROGRAMS.get(DOMAIN, []):
try:
status = program[isy.KEY_STATUS]
except (KeyError, AssertionError):
pass
for node in hass.data[ISY994_NODES][DOMAIN]:
if node.parent_node is None:
device = ISYBinarySensorDevice(node)
devices.append(device)
devices_by_nid[node.nid] = device
else:
devices.append(ISYBinarySensorProgram(program.name, status))
# We'll process the child nodes last, to ensure all parent nodes
# have been processed
child_nodes.append(node)
for node in child_nodes:
try:
parent_device = devices_by_nid[node.parent_node.nid]
except KeyError:
_LOGGER.error("Node %s has a parent node %s, but no device "
"was created for the parent. Skipping.",
node.nid, node.parent_nid)
else:
device_type = _detect_device_type(node)
subnode_id = int(node.nid[-1])
if device_type == 'opening':
# Door/window sensors use an optional "negative" node
if subnode_id == 4:
# Subnode 4 is the heartbeat node, which we will represent
# as a separate binary_sensor
device = ISYBinarySensorHeartbeat(node, parent_device)
parent_device.add_heartbeat_device(device)
devices.append(device)
elif subnode_id == 2:
parent_device.add_negative_node(node)
elif device_type == 'moisture':
# Moisure nodes have a subnode 2, but we ignore it because it's
# just the inverse of the primary node.
if subnode_id == 4:
# Heartbeat node
device = ISYBinarySensorHeartbeat(node, parent_device)
parent_device.add_heartbeat_device(device)
devices.append(device)
else:
# We don't yet have any special logic for other sensor types,
# so add the nodes as individual devices
device = ISYBinarySensorDevice(node)
devices.append(device)
for name, status, _ in hass.data[ISY994_PROGRAMS][DOMAIN]:
devices.append(ISYBinarySensorProgram(name, status))
add_devices(devices)
class ISYBinarySensorDevice(isy.ISYDevice, BinarySensorDevice):
"""Representation of an ISY994 binary sensor device."""
def _detect_device_type(node) -> str:
try:
device_type = node.type
except AttributeError:
# The type attribute didn't exist in the ISY's API response
return None
split_type = device_type.split('.')
for device_class, ids in ISY_DEVICE_TYPES.items():
if '{}.{}'.format(split_type[0], split_type[1]) in ids:
return device_class
return None
def _is_val_unknown(val):
"""Determine if a number value represents UNKNOWN from PyISY."""
return val == -1*float('inf')
class ISYBinarySensorDevice(ISYDevice, BinarySensorDevice):
"""Representation of an ISY994 binary sensor device.
Often times, a single device is represented by multiple nodes in the ISY,
allowing for different nuances in how those devices report their on and
off events. This class turns those multiple nodes in to a single Hass
entity and handles both ways that ISY binary sensors can work.
"""
def __init__(self, node) -> None:
"""Initialize the ISY994 binary sensor device."""
isy.ISYDevice.__init__(self, node)
super().__init__(node)
self._negative_node = None
self._heartbeat_device = None
self._device_class_from_type = _detect_device_type(self._node)
# pylint: disable=protected-access
if _is_val_unknown(self._node.status._val):
self._computed_state = None
else:
self._computed_state = bool(self._node.status._val)
@asyncio.coroutine
def async_added_to_hass(self) -> None:
"""Subscribe to the node and subnode event emitters."""
yield from super().async_added_to_hass()
self._node.controlEvents.subscribe(self._positive_node_control_handler)
if self._negative_node is not None:
self._negative_node.controlEvents.subscribe(
self._negative_node_control_handler)
def add_heartbeat_device(self, device) -> None:
"""Register a heartbeat device for this sensor.
The heartbeat node beats on its own, but we can gain a little
reliability by considering any node activity for this sensor
to be a heartbeat as well.
"""
self._heartbeat_device = device
def _heartbeat(self) -> None:
"""Send a heartbeat to our heartbeat device, if we have one."""
if self._heartbeat_device is not None:
self._heartbeat_device.heartbeat()
def add_negative_node(self, child) -> None:
"""Add a negative node to this binary sensor device.
The negative node is a node that can receive the 'off' events
for the sensor, depending on device configuration and type.
"""
self._negative_node = child
# pylint: disable=protected-access
if not _is_val_unknown(self._negative_node.status._val):
# If the negative node has a value, it means the negative node is
# in use for this device. Therefore, we cannot determine the state
# of the sensor until we receive our first ON event.
self._computed_state = None
def _negative_node_control_handler(self, event: object) -> None:
"""Handle an "On" control event from the "negative" node."""
if event == 'DON':
_LOGGER.debug("Sensor %s turning Off via the Negative node "
"sending a DON command", self.name)
self._computed_state = False
self.schedule_update_ha_state()
self._heartbeat()
def _positive_node_control_handler(self, event: object) -> None:
"""Handle On and Off control event coming from the primary node.
Depending on device configuration, sometimes only On events
will come to this node, with the negative node representing Off
events
"""
if event == 'DON':
_LOGGER.debug("Sensor %s turning On via the Primary node "
"sending a DON command", self.name)
self._computed_state = True
self.schedule_update_ha_state()
self._heartbeat()
if event == 'DOF':
_LOGGER.debug("Sensor %s turning Off via the Primary node "
"sending a DOF command", self.name)
self._computed_state = False
self.schedule_update_ha_state()
self._heartbeat()
# pylint: disable=unused-argument
def on_update(self, event: object) -> None:
"""Ignore primary node status updates.
We listen directly to the Control events on all nodes for this
device.
"""
pass
@property
def value(self) -> object:
"""Get the current value of the device.
Insteon leak sensors set their primary node to On when the state is
DRY, not WET, so we invert the binary state if the user indicates
that it is a moisture sensor.
"""
if self._computed_state is None:
# Do this first so we don't invert None on moisture sensors
return None
if self.device_class == 'moisture':
return not self._computed_state
return self._computed_state
@property
def is_on(self) -> bool:
"""Get whether the ISY994 binary sensor device is on.
Note: This method will return false if the current state is UNKNOWN
"""
return bool(self.value)
@property
def state(self):
"""Return the state of the binary sensor."""
if self._computed_state is None:
return None
return STATE_ON if self.is_on else STATE_OFF
@property
def device_class(self) -> str:
"""Return the class of this device.
This was discovered by parsing the device type code during init
"""
return self._device_class_from_type
class ISYBinarySensorHeartbeat(ISYDevice, BinarySensorDevice):
"""Representation of the battery state of an ISY994 sensor."""
def __init__(self, node, parent_device) -> None:
"""Initialize the ISY994 binary sensor device."""
super().__init__(node)
self._computed_state = None
self._parent_device = parent_device
self._heartbeat_timer = None
@asyncio.coroutine
def async_added_to_hass(self) -> None:
"""Subscribe to the node and subnode event emitters."""
yield from super().async_added_to_hass()
self._node.controlEvents.subscribe(
self._heartbeat_node_control_handler)
# Start the timer on bootup, so we can change from UNKNOWN to ON
self._restart_timer()
def _heartbeat_node_control_handler(self, event: object) -> None:
"""Update the heartbeat timestamp when an On event is sent."""
if event == 'DON':
self.heartbeat()
def heartbeat(self):
"""Mark the device as online, and restart the 25 hour timer.
This gets called when the heartbeat node beats, but also when the
parent sensor sends any events, as we can trust that to mean the device
is online. This mitigates the risk of false positives due to a single
missed heartbeat event.
"""
self._computed_state = False
self._restart_timer()
self.schedule_update_ha_state()
def _restart_timer(self):
"""Restart the 25 hour timer."""
try:
self._heartbeat_timer()
self._heartbeat_timer = None
except TypeError:
# No heartbeat timer is active
pass
# pylint: disable=unused-argument
@callback
def timer_elapsed(now) -> None:
"""Heartbeat missed; set state to indicate dead battery."""
self._computed_state = True
self._heartbeat_timer = None
self.schedule_update_ha_state()
point_in_time = dt_util.utcnow() + timedelta(hours=25)
_LOGGER.debug("Timer starting. Now: %s Then: %s",
dt_util.utcnow(), point_in_time)
self._heartbeat_timer = async_track_point_in_utc_time(
self.hass, timer_elapsed, point_in_time)
# pylint: disable=unused-argument
def on_update(self, event: object) -> None:
"""Ignore node status updates.
We listen directly to the Control events for this device.
"""
pass
@property
def value(self) -> object:
"""Get the current value of this sensor."""
return self._computed_state
@property
def is_on(self) -> bool:
"""Get whether the ISY994 binary sensor device is on.
Note: This method will return false if the current state is UNKNOWN
"""
return bool(self.value)
@property
def state(self):
"""Return the state of the binary sensor."""
if self._computed_state is None:
return None
return STATE_ON if self.is_on else STATE_OFF
@property
def device_class(self) -> str:
"""Get the class of this device."""
return 'battery'
@property
def device_state_attributes(self):
"""Get the state attributes for the device."""
attr = super().device_state_attributes
attr['parent_entity_id'] = self._parent_device.entity_id
return attr
class ISYBinarySensorProgram(ISYDevice, BinarySensorDevice):
"""Representation of an ISY994 binary sensor program.
This does not need all of the subnode logic in the device version of binary
sensors.
"""
def __init__(self, name, node) -> None:
"""Initialize the ISY994 binary sensor program."""
super().__init__(node)
self._name = name
@property
def is_on(self) -> bool:
"""Get whether the ISY994 binary sensor device is on."""
return bool(self.value)
class ISYBinarySensorProgram(ISYBinarySensorDevice):
"""Representation of an ISY994 binary sensor program."""
def __init__(self, name, node) -> None:
"""Initialize the ISY994 binary sensor program."""
ISYBinarySensorDevice.__init__(self, node)
self._name = name

View File

@@ -129,6 +129,11 @@ class KNXBinarySensor(BinarySensorDevice):
"""Return the name of the KNX device."""
return self.device.name
@property
def available(self):
"""Return True if entity is available."""
return self.hass.data[DATA_KNX].connected
@property
def should_poll(self):
"""No polling needed within KNX."""

View File

@@ -0,0 +1,96 @@
"""
Support for monitoring the state of Linode Nodes.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.linode/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.linode import (
CONF_NODES, ATTR_CREATED, ATTR_NODE_ID, ATTR_NODE_NAME,
ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY,
ATTR_REGION, ATTR_VCPUS, DATA_LINODE)
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Node'
DEFAULT_DEVICE_CLASS = 'moving'
DEPENDENCIES = ['linode']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_NODES): vol.All(cv.ensure_list, [cv.string]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Linode droplet sensor."""
linode = hass.data.get(DATA_LINODE)
nodes = config.get(CONF_NODES)
dev = []
for node in nodes:
node_id = linode.get_node_id(node)
if node_id is None:
_LOGGER.error("Node %s is not available", node)
return
dev.append(LinodeBinarySensor(linode, node_id))
add_devices(dev, True)
class LinodeBinarySensor(BinarySensorDevice):
"""Representation of a Linode droplet sensor."""
def __init__(self, li, node_id):
"""Initialize a new Linode sensor."""
self._linode = li
self._node_id = node_id
self._state = None
self.data = None
@property
def name(self):
"""Return the name of the sensor."""
if self.data is not None:
return self.data.label
@property
def is_on(self):
"""Return true if the binary sensor is on."""
if self.data is not None:
return self.data.status == 'running'
return False
@property
def device_class(self):
"""Return the class of this sensor."""
return DEFAULT_DEVICE_CLASS
@property
def device_state_attributes(self):
"""Return the state attributes of the Linode Node."""
if self.data:
return {
ATTR_CREATED: self.data.created,
ATTR_NODE_ID: self.data.id,
ATTR_NODE_NAME: self.data.label,
ATTR_IPV4_ADDRESS: self.data.ipv4,
ATTR_IPV6_ADDRESS: self.data.ipv6,
ATTR_MEMORY: self.data.specs.memory,
ATTR_REGION: self.data.region.country,
ATTR_VCPUS: self.data.specs.vcpus,
}
return {}
def update(self):
"""Update state of sensor."""
self._linode.update()
if self._linode.data is not None:
for node in self._linode.data:
if node.id == self._node_id:
self.data = node

View File

@@ -17,19 +17,15 @@ from homeassistant.const import (
CONF_NAME, CONF_VALUE_TEMPLATE, CONF_PAYLOAD_ON, CONF_PAYLOAD_OFF,
CONF_DEVICE_CLASS)
from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_AVAILABILITY_TOPIC, CONF_QOS, valid_subscribe_topic)
CONF_STATE_TOPIC, CONF_AVAILABILITY_TOPIC, CONF_PAYLOAD_AVAILABLE,
CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS, MqttAvailability)
import homeassistant.helpers.config_validation as cv
_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']
@@ -38,12 +34,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
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_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,
})
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@asyncio.coroutine
@@ -70,31 +61,29 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
)])
class MqttBinarySensor(BinarySensorDevice):
class MqttBinarySensor(MqttAvailability, BinarySensorDevice):
"""Representation a binary sensor that is updated by MQTT."""
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."""
super().__init__(availability_topic, qos, payload_available,
payload_not_available)
self._name = name
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
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe mqtt events.
"""Subscribe mqtt events."""
yield from super().async_added_to_hass()
This method must be run in the event loop and returns a coroutine.
"""
@callback
def state_message_received(topic, payload, qos):
"""Handle a new received MQTT state message."""
@@ -111,21 +100,6 @@ class MqttBinarySensor(BinarySensorDevice):
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):
"""Return the polling state."""
@@ -136,11 +110,6 @@ 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

@@ -0,0 +1,64 @@
"""
Support for showing random states.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.random/
"""
import asyncio
import logging
import voluptuous as vol
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_DEVICE_CLASS
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Random Binary Sensor'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Random binary sensor."""
name = config.get(CONF_NAME)
device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices([RandomSensor(name, device_class)], True)
class RandomSensor(BinarySensorDevice):
"""Representation of a Random binary sensor."""
def __init__(self, name, device_class):
"""Initialize the Random binary sensor."""
self._name = name
self._device_class = device_class
self._state = None
@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._state
@property
def device_class(self):
"""Return the sensor class of the sensor."""
return self._device_class
@asyncio.coroutine
def async_update(self):
"""Get new state and update the sensor's state."""
from random import getrandbits
self._state = bool(getrandbits(1))

View File

@@ -98,6 +98,11 @@ class RestBinarySensor(BinarySensorDevice):
"""Return the class of this sensor."""
return self._device_class
@property
def available(self):
"""Return the availability of this sensor."""
return self.rest.data is not None
@property
def is_on(self):
"""Return true if the binary sensor is on."""

View File

@@ -7,30 +7,41 @@ tested. Other types may need some work.
"""
import logging
import voluptuous as vol
from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_COMMAND_ON, CONF_COMMAND_OFF, CONF_NAME)
from homeassistant.components import rfxtrx
from homeassistant.helpers import event as evt
from homeassistant.helpers import config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.rfxtrx import (
ATTR_NAME, CONF_AUTOMATIC_ADD, CONF_FIRE_EVENT,
CONF_OFF_DELAY, CONF_DATA_BITS, CONF_DEVICES)
from homeassistant.util import slugify
from homeassistant.util import dt as dt_util
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import event as evt
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.rfxtrx import (
ATTR_AUTOMATIC_ADD, ATTR_NAME, ATTR_OFF_DELAY, ATTR_FIREEVENT,
ATTR_DATA_BITS, CONF_DEVICES
)
from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_COMMAND_ON, CONF_COMMAND_OFF
)
DEPENDENCIES = ["rfxtrx"]
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = vol.Schema({
vol.Required("platform"): rfxtrx.DOMAIN,
vol.Optional(CONF_DEVICES, default={}): vol.All(
dict, rfxtrx.valid_binary_sensor),
vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_DEVICES, default={}): {
cv.string: vol.Schema({
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): cv.string,
vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean,
vol.Optional(CONF_OFF_DELAY, default=None):
vol.Any(cv.time_period, cv.positive_timedelta),
vol.Optional(CONF_DATA_BITS, default=None): cv.positive_int,
vol.Optional(CONF_COMMAND_ON, default=None): cv.byte,
vol.Optional(CONF_COMMAND_OFF, default=None): cv.byte
})
},
vol.Optional(CONF_AUTOMATIC_ADD, default=False): cv.boolean,
}, extra=vol.ALLOW_EXTRA)
@@ -46,23 +57,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
if device_id in rfxtrx.RFX_DEVICES:
continue
if entity[ATTR_DATA_BITS] is not None:
_LOGGER.info("Masked device id: %s",
rfxtrx.get_pt2262_deviceid(device_id,
entity[ATTR_DATA_BITS]))
if entity[CONF_DATA_BITS] is not None:
_LOGGER.debug("Masked device id: %s",
rfxtrx.get_pt2262_deviceid(device_id,
entity[CONF_DATA_BITS]))
_LOGGER.info("Add %s rfxtrx.binary_sensor (class %s)",
entity[ATTR_NAME], entity[CONF_DEVICE_CLASS])
_LOGGER.debug("Add %s rfxtrx.binary_sensor (class %s)",
entity[ATTR_NAME], entity[CONF_DEVICE_CLASS])
device = RfxtrxBinarySensor(event, entity[ATTR_NAME],
entity[CONF_DEVICE_CLASS],
entity[ATTR_FIREEVENT],
entity[ATTR_OFF_DELAY],
entity[ATTR_DATA_BITS],
entity[CONF_FIRE_EVENT],
entity[CONF_OFF_DELAY],
entity[CONF_DATA_BITS],
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
@@ -83,20 +93,19 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
if sensor is None:
# Add the entity if not exists and automatic_add is True
if not config[ATTR_AUTOMATIC_ADD]:
if not config[CONF_AUTOMATIC_ADD]:
return
poss_dev = rfxtrx.find_possible_pt2262_device(device_id)
if poss_dev is not None:
poss_id = slugify(poss_dev.event.device.id_string.lower())
_LOGGER.info("Found possible matching deviceid %s.",
poss_id)
if event.device.packettype == 0x13:
poss_dev = rfxtrx.find_possible_pt2262_device(device_id)
if poss_dev is not None:
poss_id = slugify(poss_dev.event.device.id_string.lower())
_LOGGER.debug("Found possible matching deviceid %s.",
poss_id)
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 "
@@ -109,11 +118,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
elif not isinstance(sensor, RfxtrxBinarySensor):
return
else:
_LOGGER.info("Binary sensor update "
"(Device_id: %s Class: %s Sub: %s)",
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype)
_LOGGER.debug("Binary sensor update "
"(Device_id: %s Class: %s Sub: %s)",
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype)
if sensor.is_lighting4:
if sensor.data_bits is not None:
cmd = rfxtrx.get_pt2262_cmd(device_id, sensor.data_bits)
@@ -154,7 +164,7 @@ class RfxtrxBinarySensor(BinarySensorDevice):
self._device_class = device_class
self._off_delay = off_delay
self._state = False
self.is_lighting4 = False
self.is_lighting4 = (event.device.packettype == 0x13)
self.delay_listener = None
self._data_bits = data_bits
self._cmd_on = cmd_on
@@ -164,10 +174,8 @@ class RfxtrxBinarySensor(BinarySensorDevice):
self._masked_id = rfxtrx.get_pt2262_deviceid(
event.device.id_string.lower(),
data_bits)
def __str__(self):
"""Return the name of the sensor."""
return self._name
else:
self._masked_id = None
@property
def name(self):

View File

@@ -11,7 +11,7 @@ import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.ring import (
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE)
CONF_ATTRIBUTION, DEFAULT_ENTITY_NAMESPACE, DATA_RING)
from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_ENTITY_NAMESPACE, CONF_MONITORED_CONDITIONS)
@@ -28,20 +28,20 @@ SCAN_INTERVAL = timedelta(seconds=5)
# Sensor types: Name, category, device_class
SENSOR_TYPES = {
'ding': ['Ding', ['doorbell'], 'occupancy'],
'motion': ['Motion', ['doorbell'], 'motion'],
'motion': ['Motion', ['doorbell', 'stickup_cams'], 'motion'],
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_ENTITY_NAMESPACE, default=DEFAULT_ENTITY_NAMESPACE):
cv.string,
vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)):
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for a Ring device."""
ring = hass.data.get('ring')
ring = hass.data[DATA_RING]
sensors = []
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
@@ -50,6 +50,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensors.append(RingBinarySensor(hass,
device,
sensor_type))
for device in ring.stickup_cams:
if 'stickup_cams' in SENSOR_TYPES[sensor_type][1]:
sensors.append(RingBinarySensor(hass,
device,
sensor_type))
add_devices(sensors, True)
return True

View File

@@ -67,7 +67,7 @@ class SpcBinarySensor(BinarySensorDevice):
spc_registry.register_sensor_device(zone_id, self)
@asyncio.coroutine
def async_update_from_spc(self, state):
def async_update_from_spc(self, state, extra):
"""Update the state of the device."""
self._state = state
yield from self.async_update_ha_state()

View File

@@ -0,0 +1,34 @@
"""
Support for binary sensors using Tellstick Net.
This platform uses the Telldus Live online service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tellduslive/
"""
import logging
from homeassistant.components.tellduslive import TelldusLiveEntity
from homeassistant.components.binary_sensor import BinarySensorDevice
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Tellstick sensors."""
if discovery_info is None:
return
add_devices(
TelldusLiveSensor(hass, binary_sensor)
for binary_sensor in discovery_info
)
class TelldusLiveSensor(TelldusLiveEntity, BinarySensorDevice):
"""Representation of a Tellstick sensor."""
@property
def is_on(self):
"""Return true if switch is on."""
return self.device.is_on

View File

@@ -38,6 +38,11 @@ SENSOR_SCHEMA = vol.Schema({
vol.All(cv.time_period, cv.positive_timedelta),
})
SENSOR_SCHEMA = vol.All(
cv.deprecated(ATTR_ENTITY_ID),
SENSOR_SCHEMA,
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SENSORS): vol.Schema({cv.slug: SENSOR_SCHEMA}),
})

View File

@@ -9,40 +9,48 @@ import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import (
CONF_NAME, CONF_ENTITY_ID, CONF_TYPE, STATE_UNKNOWN,
ATTR_ENTITY_ID, CONF_DEVICE_CLASS)
ATTR_ENTITY_ID, CONF_DEVICE_CLASS, CONF_ENTITY_ID, CONF_NAME,
STATE_UNKNOWN)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__)
ATTR_HYSTERESIS = 'hysteresis'
ATTR_LOWER = 'lower'
ATTR_POSITION = 'position'
ATTR_SENSOR_VALUE = 'sensor_value'
ATTR_THRESHOLD = 'threshold'
ATTR_TYPE = 'type'
ATTR_UPPER = 'upper'
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]
POSITION_ABOVE = 'above'
POSITION_BELOW = 'below'
POSITION_IN_RANGE = 'in_range'
POSITION_UNKNOWN = 'unknown'
TYPE_LOWER = 'lower'
TYPE_RANGE = 'range'
TYPE_UPPER = 'upper'
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_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_HYSTERESIS, default=DEFAULT_HYSTERESIS):
vol.Coerce(float),
vol.Optional(CONF_LOWER): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_UPPER): vol.Coerce(float),
})
@@ -51,47 +59,44 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Threshold sensor."""
entity_id = config.get(CONF_ENTITY_ID)
name = config.get(CONF_NAME)
threshold = config.get(CONF_THRESHOLD)
lower = config.get(CONF_LOWER)
upper = config.get(CONF_UPPER)
hysteresis = config.get(CONF_HYSTERESIS)
limit_type = config.get(CONF_TYPE)
device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices([ThresholdSensor(
hass, entity_id, name, threshold,
hysteresis, limit_type, device_class)
], True)
return True
hass, entity_id, name, lower, upper, hysteresis, device_class)], True)
class ThresholdSensor(BinarySensorDevice):
"""Representation of a Threshold sensor."""
def __init__(self, hass, entity_id, name, threshold,
hysteresis, limit_type, device_class):
def __init__(self, hass, entity_id, name, lower, upper, hysteresis,
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._threshold_lower = lower
self._threshold_upper = upper
self._hysteresis = hysteresis
self._device_class = device_class
self._state = False
self.sensor_value = 0
@callback
self._state_position = None
self._state = False
self.sensor_value = None
# pylint: disable=invalid-name
@callback
def async_threshold_sensor_state_listener(
entity, old_state, new_state):
"""Handle sensor state changes."""
if new_state.state == STATE_UNKNOWN:
return
try:
self.sensor_value = float(new_state.state)
except ValueError:
_LOGGER.error("State is not numerical")
self.sensor_value = None if new_state.state == STATE_UNKNOWN \
else float(new_state.state)
except (ValueError, TypeError):
self.sensor_value = None
_LOGGER.warning("State is not numerical")
hass.async_add_job(self.async_update_ha_state, True)
@@ -118,23 +123,67 @@ class ThresholdSensor(BinarySensorDevice):
"""Return the sensor class of the sensor."""
return self._device_class
@property
def threshold_type(self):
"""Return the type of threshold this sensor represents."""
if self._threshold_lower and self._threshold_upper:
return TYPE_RANGE
elif self._threshold_lower:
return TYPE_LOWER
elif self._threshold_upper:
return TYPE_UPPER
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
return {
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,
ATTR_LOWER: self._threshold_lower,
ATTR_POSITION: self._state_position,
ATTR_SENSOR_VALUE: self.sensor_value,
ATTR_TYPE: self.threshold_type,
ATTR_UPPER: self._threshold_upper,
}
@asyncio.coroutine
def async_update(self):
"""Get the latest data and updates the states."""
if self._hysteresis == 0 and self.sensor_value == self._threshold:
def below(threshold):
"""Determine if the sensor value is below a threshold."""
return self.sensor_value < (threshold - self._hysteresis)
def above(threshold):
"""Determine if the sensor value is above a threshold."""
return self.sensor_value > (threshold + self._hysteresis)
if self.sensor_value is None:
self._state_position = POSITION_UNKNOWN
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
elif self.threshold_type == TYPE_LOWER:
if below(self._threshold_lower):
self._state_position = POSITION_BELOW
self._state = True
elif above(self._threshold_lower):
self._state_position = POSITION_ABOVE
self._state = False
elif self.threshold_type == TYPE_UPPER:
if above(self._threshold_upper):
self._state_position = POSITION_ABOVE
self._state = True
elif below(self._threshold_upper):
self._state_position = POSITION_BELOW
self._state = False
elif self.threshold_type == TYPE_RANGE:
if below(self._threshold_lower):
self._state_position = POSITION_BELOW
self._state = False
if above(self._threshold_upper):
self._state_position = POSITION_ABOVE
self._state = False
elif above(self._threshold_lower) and below(self._threshold_upper):
self._state_position = POSITION_IN_RANGE
self._state = True

View File

@@ -1,36 +1,55 @@
"""
A sensor that monitors trands in other components.
A sensor that monitors trends in other components.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.trend/
"""
import asyncio
from collections import deque
import logging
import math
import voluptuous as vol
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA, ENTITY_ID_FORMAT, PLATFORM_SCHEMA,
BinarySensorDevice)
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, CONF_DEVICE_CLASS, CONF_ENTITY_ID,
CONF_FRIENDLY_NAME, STATE_UNKNOWN)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
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_DEVICE_CLASS, STATE_UNKNOWN)
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.event import async_track_state_change
from homeassistant.util import utcnow
REQUIREMENTS = ['numpy==1.14.0']
_LOGGER = logging.getLogger(__name__)
CONF_SENSORS = 'sensors'
ATTR_ATTRIBUTE = 'attribute'
ATTR_GRADIENT = 'gradient'
ATTR_MIN_GRADIENT = 'min_gradient'
ATTR_INVERT = 'invert'
ATTR_SAMPLE_DURATION = 'sample_duration'
ATTR_SAMPLE_COUNT = 'sample_count'
CONF_ATTRIBUTE = 'attribute'
CONF_INVERT = 'invert'
CONF_MAX_SAMPLES = 'max_samples'
CONF_MIN_GRADIENT = 'min_gradient'
CONF_SAMPLE_DURATION = 'sample_duration'
CONF_SENSORS = 'sensors'
SENSOR_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Optional(CONF_ATTRIBUTE): cv.string,
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
vol.Optional(CONF_INVERT, default=False): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_FRIENDLY_NAME): cv.string,
vol.Optional(CONF_INVERT, default=False): cv.boolean,
vol.Optional(CONF_MAX_SAMPLES, default=2): cv.positive_int,
vol.Optional(CONF_MIN_GRADIENT, default=0.0): vol.Coerce(float),
vol.Optional(CONF_SAMPLE_DURATION, default=0): cv.positive_int,
})
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
@@ -43,17 +62,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the trend sensors."""
sensors = []
for device, device_config in config[CONF_SENSORS].items():
for device_id, device_config in config[CONF_SENSORS].items():
entity_id = device_config[ATTR_ENTITY_ID]
attribute = device_config.get(CONF_ATTRIBUTE)
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
device_class = device_config.get(CONF_DEVICE_CLASS)
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device_id)
invert = device_config[CONF_INVERT]
max_samples = device_config[CONF_MAX_SAMPLES]
min_gradient = device_config[CONF_MIN_GRADIENT]
sample_duration = device_config[CONF_SAMPLE_DURATION]
sensors.append(
SensorTrend(
hass, device, friendly_name, entity_id, attribute,
device_class, invert)
hass, device_id, friendly_name, entity_id, attribute,
device_class, invert, max_samples, min_gradient,
sample_duration)
)
if not sensors:
_LOGGER.error("No sensors added")
@@ -65,30 +88,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class SensorTrend(BinarySensorDevice):
"""Representation of a trend Sensor."""
def __init__(self, hass, device_id, friendly_name,
target_entity, attribute, device_class, invert):
def __init__(self, hass, device_id, friendly_name, entity_id,
attribute, device_class, invert, max_samples,
min_gradient, sample_duration):
"""Initialize the sensor."""
self._hass = hass
self.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, device_id, hass=hass)
self._name = friendly_name
self._target_entity = target_entity
self._entity_id = entity_id
self._attribute = attribute
self._device_class = device_class
self._invert = invert
self._sample_duration = sample_duration
self._min_gradient = min_gradient
self._gradient = None
self._state = None
self.from_state = None
self.to_state = None
@callback
def trend_sensor_state_listener(entity, old_state, new_state):
"""Handle the target device state changes."""
self.from_state = old_state
self.to_state = new_state
hass.async_add_job(self.async_update_ha_state(True))
track_state_change(hass, target_entity,
trend_sensor_state_listener)
self.samples = deque(maxlen=max_samples)
@property
def name(self):
@@ -105,33 +121,77 @@ class SensorTrend(BinarySensorDevice):
"""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 {
ATTR_ENTITY_ID: self._entity_id,
ATTR_FRIENDLY_NAME: self._name,
ATTR_GRADIENT: self._gradient,
ATTR_INVERT: self._invert,
ATTR_MIN_GRADIENT: self._min_gradient,
ATTR_SAMPLE_COUNT: len(self.samples),
ATTR_SAMPLE_DURATION: self._sample_duration,
}
@property
def should_poll(self):
"""No polling needed."""
return False
@asyncio.coroutine
def async_added_to_hass(self):
"""Complete device setup after being added to hass."""
@callback
def trend_sensor_state_listener(entity, old_state, new_state):
"""Handle state changes on the observed device."""
try:
if self._attribute:
state = new_state.attributes.get(self._attribute)
else:
state = new_state.state
if state != STATE_UNKNOWN:
sample = (utcnow().timestamp(), float(state))
self.samples.append(sample)
self.async_schedule_update_ha_state(True)
except (ValueError, TypeError) as ex:
_LOGGER.error(ex)
async_track_state_change(
self.hass, self._entity_id,
trend_sensor_state_listener)
@asyncio.coroutine
def async_update(self):
"""Get the latest data and update the states."""
if self.from_state is None or self.to_state is None:
return
if (self.from_state.state == STATE_UNKNOWN or
self.to_state.state == STATE_UNKNOWN):
return
try:
if self._attribute:
from_value = float(
self.from_state.attributes.get(self._attribute))
to_value = float(
self.to_state.attributes.get(self._attribute))
else:
from_value = float(self.from_state.state)
to_value = float(self.to_state.state)
# Remove outdated samples
if self._sample_duration > 0:
cutoff = utcnow().timestamp() - self._sample_duration
while self.samples and self.samples[0][0] < cutoff:
self.samples.popleft()
self._state = to_value > from_value
if self._invert:
self._state = not self._state
if len(self.samples) < 2:
return
except (ValueError, TypeError) as ex:
self._state = None
_LOGGER.error(ex)
# Calculate gradient of linear trend
yield from self.hass.async_add_job(self._calculate_gradient)
# Update state
self._state = (
abs(self._gradient) > abs(self._min_gradient) and
math.copysign(self._gradient, self._min_gradient) == self._gradient
)
if self._invert:
self._state = not self._state
def _calculate_gradient(self):
"""Compute the linear trend gradient of the current samples.
This need run inside executor.
"""
import numpy as np
timestamps = np.array([t for t, _ in self.samples])
values = np.array([s for _, s in self.samples])
coeffs = np.polyfit(timestamps, values, 1)
self._gradient = coeffs[0]

View File

@@ -19,8 +19,8 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Perform the setup for Vera controller devices."""
add_devices(
VeraBinarySensor(device, VERA_CONTROLLER)
for device in VERA_DEVICES['binary_sensor'])
VeraBinarySensor(device, hass.data[VERA_CONTROLLER])
for device in hass.data[VERA_DEVICES]['binary_sensor'])
class VeraBinarySensor(VeraDevice, BinarySensorDevice):

View File

@@ -0,0 +1,103 @@
"""
Support for monitoring the state of Vultr subscriptions (VPS).
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.vultr/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.vultr import (
CONF_SUBSCRIPTION, ATTR_AUTO_BACKUPS, ATTR_ALLOWED_BANDWIDTH,
ATTR_CREATED_AT, ATTR_SUBSCRIPTION_ID, ATTR_SUBSCRIPTION_NAME,
ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, ATTR_DISK,
ATTR_COST_PER_MONTH, ATTR_OS, ATTR_REGION, ATTR_VCPUS, DATA_VULTR)
_LOGGER = logging.getLogger(__name__)
DEFAULT_DEVICE_CLASS = 'power'
DEFAULT_NAME = 'Vultr {}'
DEPENDENCIES = ['vultr']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_SUBSCRIPTION): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Vultr subscription (server) sensor."""
vultr = hass.data[DATA_VULTR]
subscription = config.get(CONF_SUBSCRIPTION)
name = config.get(CONF_NAME)
if subscription not in vultr.data:
_LOGGER.error("Subscription %s not found", subscription)
return False
add_devices([VultrBinarySensor(vultr, subscription, name)], True)
class VultrBinarySensor(BinarySensorDevice):
"""Representation of a Vultr subscription sensor."""
def __init__(self, vultr, subscription, name):
"""Initialize a new Vultr sensor."""
self._vultr = vultr
self._name = name
self.subscription = subscription
self.data = None
@property
def name(self):
"""Return the name of the sensor."""
try:
return self._name.format(self.data['label'])
except (KeyError, TypeError):
return self._name
@property
def icon(self):
"""Return the icon of this server."""
return 'mdi:server' if self.is_on else 'mdi:server-off'
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self.data['power_status'] == 'running'
@property
def device_class(self):
"""Return the class of this sensor."""
return DEFAULT_DEVICE_CLASS
@property
def device_state_attributes(self):
"""Return the state attributes of the Vultr subscription."""
return {
ATTR_ALLOWED_BANDWIDTH: self.data.get('allowed_bandwidth_gb'),
ATTR_AUTO_BACKUPS: self.data.get('auto_backups'),
ATTR_COST_PER_MONTH: self.data.get('cost_per_month'),
ATTR_CREATED_AT: self.data.get('date_created'),
ATTR_DISK: self.data.get('disk'),
ATTR_IPV4_ADDRESS: self.data.get('main_ip'),
ATTR_IPV6_ADDRESS: self.data.get('v6_main_ip'),
ATTR_MEMORY: self.data.get('ram'),
ATTR_OS: self.data.get('os'),
ATTR_REGION: self.data.get('location'),
ATTR_SUBSCRIPTION_ID: self.data.get('SUBID'),
ATTR_SUBSCRIPTION_NAME: self.data.get('label'),
ATTR_VCPUS: self.data.get('vcpu_count')
}
def update(self):
"""Update state of sensor."""
self._vultr.update()
self.data = self._vultr.data[self.subscription]

View File

@@ -64,7 +64,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
excludes = config.get(CONF_EXCLUDES)
days_offset = config.get(CONF_OFFSET)
year = (datetime.now() + timedelta(days=days_offset)).year
year = (get_date(datetime.today()) + timedelta(days=days_offset)).year
obj_holidays = getattr(holidays, country)(years=year)
if province:
@@ -99,6 +99,11 @@ def day_to_string(day):
return None
def get_date(date):
"""Return date. Needed for testing."""
return date
class IsWorkdaySensor(BinarySensorDevice):
"""Implementation of a Workday sensor."""
@@ -156,7 +161,7 @@ class IsWorkdaySensor(BinarySensorDevice):
self._state = False
# Get iso day of the week (1 = Monday, 7 = Sunday)
date = datetime.today() + timedelta(days=self._days_offset)
date = get_date(datetime.today()) + timedelta(days=self._days_offset)
day = date.isoweekday() - 1
day_of_week = day_to_string(day)

View File

@@ -25,13 +25,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items():
for device in gateway.devices['binary_sensor']:
model = device['model']
if model == 'motion':
if model in ['motion', 'sensor_motion.aq2']:
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':
elif model in ['magnet', 'sensor_magnet.aq2']:
devices.append(XiaomiDoorSensor(device, gateway))
elif model == 'sensor_wleak.aq1':
devices.append(XiaomiWaterLeakSensor(device, gateway))
@@ -39,10 +35,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
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':
elif model in ['switch', 'sensor_switch.aq2', 'sensor_switch.aq3']:
devices.append(XiaomiButton(device, 'Switch', 'status',
hass, gateway))
elif model == '86sw1':
@@ -289,9 +282,17 @@ class XiaomiButton(XiaomiBinarySensor):
def __init__(self, device, name, data_key, hass, xiaomi_hub):
"""Initialize the XiaomiButton."""
self._hass = hass
self._last_action = None
XiaomiBinarySensor.__init__(self, device, name, xiaomi_hub,
data_key, None)
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {ATTR_LAST_ACTION: self._last_action}
attrs.update(super().device_state_attributes)
return attrs
def parse_data(self, data):
"""Parse data sent by gateway."""
value = data.get(self._data_key)
@@ -317,6 +318,8 @@ class XiaomiButton(XiaomiBinarySensor):
'entity_id': self.entity_id,
'click_type': click_type
})
self._last_action = click_type
if value in ['long_click_press', 'long_click_release']:
return True
return False

View File

@@ -4,16 +4,17 @@ Support for BloomSky weather station.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/bloomsky/
"""
import logging
from datetime import timedelta
import logging
from aiohttp.hdrs import AUTHORIZATION
import requests
import voluptuous as vol
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import discovery
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
@@ -68,7 +69,7 @@ class BloomSky(object):
"""Use the API to retrieve a list of devices."""
_LOGGER.debug("Fetching BloomSky update")
response = requests.get(
self.API_URL, headers={"Authorization": self._api_key}, timeout=10)
self.API_URL, headers={AUTHORIZATION: self._api_key}, timeout=10)
if response.status_code == 401:
raise RuntimeError("Invalid API_KEY")
elif response.status_code != 200:

View File

@@ -0,0 +1,230 @@
"""
Support for WebDav Calendar.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/calendar.caldav/
"""
import logging
import re
from datetime import datetime, timedelta
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.calendar import (
CalendarEventDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_PASSWORD, CONF_URL, CONF_USERNAME)
from homeassistant.util import dt, Throttle
REQUIREMENTS = ['caldav==0.5.0']
_LOGGER = logging.getLogger(__name__)
CONF_DEVICE_ID = 'device_id'
CONF_CALENDARS = 'calendars'
CONF_CUSTOM_CALENDARS = 'custom_calendars'
CONF_CALENDAR = 'calendar'
CONF_SEARCH = 'search'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
# pylint: disable=no-value-for-parameter
vol.Required(CONF_URL): vol.Url(),
vol.Optional(CONF_CALENDARS, default=[]):
vol.All(cv.ensure_list, vol.Schema([
cv.string
])),
vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
vol.Optional(CONF_CUSTOM_CALENDARS, default=[]):
vol.All(cv.ensure_list, vol.Schema([
vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_CALENDAR): cv.string,
vol.Required(CONF_SEARCH): cv.string
})
]))
})
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
def setup_platform(hass, config, add_devices, disc_info=None):
"""Set up the WebDav Calendar platform."""
import caldav
client = caldav.DAVClient(config.get(CONF_URL),
None,
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD))
# Retrieve all the remote calendars
calendars = client.principal().calendars()
calendar_devices = []
for calendar in list(calendars):
# If a calendar name was given in the configuration,
# ignore all the others
if (config.get(CONF_CALENDARS)
and calendar.name not in config.get(CONF_CALENDARS)):
_LOGGER.debug("Ignoring calendar '%s'", calendar.name)
continue
# Create additional calendars based on custom filtering
# rules
for cust_calendar in config.get(CONF_CUSTOM_CALENDARS):
# Check that the base calendar matches
if cust_calendar.get(CONF_CALENDAR) != calendar.name:
continue
device_data = {
CONF_NAME: cust_calendar.get(CONF_NAME),
CONF_DEVICE_ID: "{} {}".format(
cust_calendar.get(CONF_CALENDAR),
cust_calendar.get(CONF_NAME)),
}
calendar_devices.append(
WebDavCalendarEventDevice(hass,
device_data,
calendar,
True,
cust_calendar.get(CONF_SEARCH))
)
# Create a default calendar if there was no custom one
if not config.get(CONF_CUSTOM_CALENDARS):
device_data = {
CONF_NAME: calendar.name,
CONF_DEVICE_ID: calendar.name
}
calendar_devices.append(
WebDavCalendarEventDevice(hass, device_data, calendar)
)
# Finally add all the calendars we've created
add_devices(calendar_devices)
class WebDavCalendarEventDevice(CalendarEventDevice):
"""A device for getting the next Task from a WebDav Calendar."""
def __init__(self,
hass,
device_data,
calendar,
all_day=False,
search=None):
"""Create the WebDav Calendar Event Device."""
self.data = WebDavCalendarData(calendar, all_day, search)
super().__init__(hass, device_data)
@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
return attributes
class WebDavCalendarData(object):
"""Class to utilize the calendar dav client object to get next event."""
def __init__(self, calendar, include_all_day, search):
"""Set up how we are going to search the WebDav calendar."""
self.calendar = calendar
self.include_all_day = include_all_day
self.search = search
self.event = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
# We have to retrieve the results for the whole day as the server
# won't return events that have already started
results = self.calendar.date_search(
dt.start_of_local_day(),
dt.start_of_local_day() + timedelta(days=1)
)
# dtstart can be a date or datetime depending if the event lasts a
# whole day. Convert everything to datetime to be able to sort it
results.sort(key=lambda x: self.to_datetime(
x.instance.vevent.dtstart.value
))
vevent = next((
event.instance.vevent for event in results
if (self.is_matching(event.instance.vevent, self.search)
and (not self.is_all_day(event.instance.vevent)
or self.include_all_day)
and not self.is_over(event.instance.vevent))), None)
# If no matching event could be found
if vevent is None:
_LOGGER.debug(
"No matching event found in the %d results for %s",
len(results),
self.calendar.name,
)
self.event = None
return True
# Populate the entity attributes with the event values
self.event = {
"summary": vevent.summary.value,
"start": self.get_hass_date(vevent.dtstart.value),
"end": self.get_hass_date(vevent.dtend.value),
"location": self.get_attr_value(vevent, "location"),
"description": self.get_attr_value(vevent, "description")
}
return True
@staticmethod
def is_matching(vevent, search):
"""Return if the event matches the filter critera."""
if search is None:
return True
pattern = re.compile(search)
return (hasattr(vevent, "summary")
and pattern.match(vevent.summary.value)
or hasattr(vevent, "location")
and pattern.match(vevent.location.value)
or hasattr(vevent, "description")
and pattern.match(vevent.description.value))
@staticmethod
def is_all_day(vevent):
"""Return if the event last the whole day."""
return not isinstance(vevent.dtstart.value, datetime)
@staticmethod
def is_over(vevent):
"""Return if the event is over."""
return dt.now() > WebDavCalendarData.to_datetime(vevent.dtend.value)
@staticmethod
def get_hass_date(obj):
"""Return if the event matches."""
if isinstance(obj, datetime):
return {"dateTime": obj.isoformat()}
return {"date": obj.isoformat()}
@staticmethod
def to_datetime(obj):
"""Return a datetime."""
if isinstance(obj, datetime):
return obj
return dt.as_local(dt.dt.datetime.combine(obj, dt.dt.time.min))
@staticmethod
def get_attr_value(obj, attribute):
"""Return the value of the attribute if defined."""
if hasattr(obj, attribute):
return getattr(obj, attribute).value
return None

0
homeassistant/components/calendar/demo.py Executable file → Normal file
View File

View File

@@ -1,19 +1,21 @@
# Describes the format for available calendar services
todoist:
new_task:
description: Create a new task and add it to a project.
fields:
content:
description: The name of the task. [Required]
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]
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]
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]
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]
description: The day this task is due, in format YYYY-MM-DD (Optional).
example: "2018-04-01"

View File

@@ -9,15 +9,13 @@ 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)
CalendarEventDevice, DOMAIN, 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
@@ -28,7 +26,6 @@ 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'
@@ -80,7 +77,7 @@ SUMMARY = 'summary'
# Todoist API: Fetch all Tasks
TASKS = 'items'
SERVICE_NEW_TASK = 'new_task'
SERVICE_NEW_TASK = 'todoist_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),
@@ -178,10 +175,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
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]
@@ -215,7 +208,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
_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)

View File

@@ -12,7 +12,6 @@ from datetime import timedelta
import logging
import hashlib
from random import SystemRandom
import os
import aiohttp
from aiohttp import web
@@ -21,7 +20,6 @@ import voluptuous as vol
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
@@ -29,18 +27,22 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED
from homeassistant.helpers.event import async_track_time_interval
import homeassistant.helpers.config_validation as cv
DOMAIN = 'camera'
DEPENDENCIES = ['http']
_LOGGER = logging.getLogger(__name__)
SERVICE_EN_MOTION = 'enable_motion_detection'
SERVICE_DISEN_MOTION = 'disable_motion_detection'
DOMAIN = 'camera'
DEPENDENCIES = ['http']
SERVICE_ENABLE_MOTION = 'enable_motion_detection'
SERVICE_DISABLE_MOTION = 'disable_motion_detection'
SERVICE_SNAPSHOT = 'snapshot'
SCAN_INTERVAL = timedelta(seconds=30)
ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_FILENAME = 'filename'
STATE_RECORDING = 'recording'
STATE_STREAMING = 'streaming'
STATE_IDLE = 'idle'
@@ -55,13 +57,17 @@ CAMERA_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})
CAMERA_SERVICE_SNAPSHOT = CAMERA_SERVICE_SCHEMA.extend({
vol.Required(ATTR_FILENAME): cv.template
})
@bind_hass
def enable_motion_detection(hass, entity_id=None):
"""Enable Motion Detection."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.async_add_job(hass.services.async_call(
DOMAIN, SERVICE_EN_MOTION, data))
DOMAIN, SERVICE_ENABLE_MOTION, data))
@bind_hass
@@ -69,9 +75,20 @@ def disable_motion_detection(hass, entity_id=None):
"""Disable Motion Detection."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.async_add_job(hass.services.async_call(
DOMAIN, SERVICE_DISEN_MOTION, data))
DOMAIN, SERVICE_DISABLE_MOTION, data))
@bind_hass
def async_snapshot(hass, filename, entity_id=None):
"""Make a snapshot from a camera."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
data[ATTR_FILENAME] = filename
hass.async_add_job(hass.services.async_call(
DOMAIN, SERVICE_SNAPSHOT, data))
@bind_hass
@asyncio.coroutine
def async_get_image(hass, entity_id, timeout=10):
"""Fetch a image from a camera entity."""
@@ -119,7 +136,8 @@ def async_setup(hass, config):
entity.async_update_token()
hass.async_add_job(entity.async_update_ha_state())
async_track_time_interval(hass, update_tokens, TOKEN_CHANGE_INTERVAL)
hass.helpers.event.async_track_time_interval(
update_tokens, TOKEN_CHANGE_INTERVAL)
@asyncio.coroutine
def async_handle_camera_service(service):
@@ -128,9 +146,9 @@ def async_setup(hass, config):
update_tasks = []
for camera in target_cameras:
if service.service == SERVICE_EN_MOTION:
if service.service == SERVICE_ENABLE_MOTION:
yield from camera.async_enable_motion_detection()
elif service.service == SERVICE_DISEN_MOTION:
elif service.service == SERVICE_DISABLE_MOTION:
yield from camera.async_disable_motion_detection()
if not camera.should_poll:
@@ -140,16 +158,45 @@ def async_setup(hass, config):
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.async_add_job(
load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
@asyncio.coroutine
def async_handle_snapshot_service(service):
"""Handle snapshot services calls."""
target_cameras = component.async_extract_from_service(service)
filename = service.data[ATTR_FILENAME]
filename.hass = hass
for camera in target_cameras:
snapshot_file = filename.async_render(
variables={ATTR_ENTITY_ID: camera})
# check if we allow to access to that file
if not hass.config.is_allowed_path(snapshot_file):
_LOGGER.error(
"Can't write %s, no access to path!", snapshot_file)
continue
image = yield from camera.async_camera_image()
def _write_image(to_file, image_data):
"""Executor helper to write image."""
with open(to_file, 'wb') as img_file:
img_file.write(image_data)
try:
yield from hass.async_add_job(
_write_image, snapshot_file, image)
except OSError as err:
_LOGGER.error("Can't write image to file: %s", err)
hass.services.async_register(
DOMAIN, SERVICE_EN_MOTION, async_handle_camera_service,
descriptions.get(SERVICE_EN_MOTION), schema=CAMERA_SERVICE_SCHEMA)
DOMAIN, SERVICE_ENABLE_MOTION, async_handle_camera_service,
schema=CAMERA_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_DISEN_MOTION, async_handle_camera_service,
descriptions.get(SERVICE_DISEN_MOTION), schema=CAMERA_SERVICE_SCHEMA)
DOMAIN, SERVICE_DISABLE_MOTION, async_handle_camera_service,
schema=CAMERA_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_SNAPSHOT, async_handle_snapshot_service,
schema=CAMERA_SERVICE_SNAPSHOT)
return True

View File

@@ -8,9 +8,10 @@ import asyncio
import logging
from homeassistant.components.amcrest import (
STREAM_SOURCE_LIST, TIMEOUT)
DATA_AMCREST, STREAM_SOURCE_LIST, TIMEOUT)
from homeassistant.components.camera import Camera
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import CONF_NAME
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_web,
async_aiohttp_proxy_stream)
@@ -26,21 +27,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
if discovery_info is None:
return
device = discovery_info['device']
authentication = discovery_info['authentication']
ffmpeg_arguments = discovery_info['ffmpeg_arguments']
name = discovery_info['name']
resolution = discovery_info['resolution']
stream_source = discovery_info['stream_source']
device_name = discovery_info[CONF_NAME]
amcrest = hass.data[DATA_AMCREST][device_name]
async_add_devices([
AmcrestCam(hass,
name,
device,
authentication,
ffmpeg_arguments,
stream_source,
resolution)], True)
async_add_devices([AmcrestCam(hass, amcrest)], True)
return True
@@ -48,18 +38,17 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class AmcrestCam(Camera):
"""An implementation of an Amcrest IP camera."""
def __init__(self, hass, name, camera, authentication,
ffmpeg_arguments, stream_source, resolution):
def __init__(self, hass, amcrest):
"""Initialize an Amcrest camera."""
super(AmcrestCam, self).__init__()
self._name = name
self._camera = camera
self._name = amcrest.name
self._camera = amcrest.device
self._base_url = self._camera.get_base_url()
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = ffmpeg_arguments
self._stream_source = stream_source
self._resolution = resolution
self._token = self._auth = authentication
self._ffmpeg_arguments = amcrest.ffmpeg_arguments
self._stream_source = amcrest.stream_source
self._resolution = amcrest.resolution
self._token = self._auth = amcrest.authentication
def camera_image(self):
"""Return a still image response from the camera."""

View File

@@ -19,7 +19,7 @@ from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=10)
SCAN_INTERVAL = timedelta(seconds=90)
ARLO_MODE_ARMED = 'armed'
ARLO_MODE_DISARMED = 'disarmed'
@@ -31,6 +31,7 @@ ATTR_MOTION = 'motion_detection_sensitivity'
ATTR_POWERSAVE = 'power_save_mode'
ATTR_SIGNAL_STRENGTH = 'signal_strength'
ATTR_UNSEEN_VIDEOS = 'unseen_videos'
ATTR_LAST_REFRESH = 'last_refresh'
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
@@ -73,6 +74,8 @@ class ArloCam(Camera):
self._motion_status = False
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
self._last_refresh = None
self._camera.base_station.refresh_rate = SCAN_INTERVAL.total_seconds()
self.attrs = {}
def camera_image(self):
@@ -105,14 +108,17 @@ class ArloCam(Camera):
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_BATTERY_LEVEL: self.attrs.get(ATTR_BATTERY_LEVEL),
ATTR_BRIGHTNESS: self.attrs.get(ATTR_BRIGHTNESS),
ATTR_FLIPPED: self.attrs.get(ATTR_FLIPPED),
ATTR_MIRRORED: self.attrs.get(ATTR_MIRRORED),
ATTR_MOTION: self.attrs.get(ATTR_MOTION),
ATTR_POWERSAVE: self.attrs.get(ATTR_POWERSAVE),
ATTR_SIGNAL_STRENGTH: self.attrs.get(ATTR_SIGNAL_STRENGTH),
ATTR_UNSEEN_VIDEOS: self.attrs.get(ATTR_UNSEEN_VIDEOS),
name: value for name, value in (
(ATTR_BATTERY_LEVEL, self._camera.battery_level),
(ATTR_BRIGHTNESS, self._camera.brightness),
(ATTR_FLIPPED, self._camera.flip_state),
(ATTR_MIRRORED, self._camera.mirror_state),
(ATTR_MOTION, self._camera.motion_detection_sensitivity),
(ATTR_POWERSAVE, POWERSAVE_MODE_MAPPING.get(
self._camera.powersave_mode)),
(ATTR_SIGNAL_STRENGTH, self._camera.signal_strength),
(ATTR_UNSEEN_VIDEOS, self._camera.unseen_videos),
) if value is not None
}
@property
@@ -160,13 +166,4 @@ class ArloCam(Camera):
def update(self):
"""Add an attribute-update task to the executor pool."""
self.attrs[ATTR_BATTERY_LEVEL] = self._camera.get_battery_level
self.attrs[ATTR_BRIGHTNESS] = self._camera.get_battery_level
self.attrs[ATTR_FLIPPED] = self._camera.get_flip_state,
self.attrs[ATTR_MIRRORED] = self._camera.get_mirror_state,
self.attrs[
ATTR_MOTION] = self._camera.get_motion_detection_sensitivity,
self.attrs[ATTR_POWERSAVE] = POWERSAVE_MODE_MAPPING[
self._camera.get_powersave_mode],
self.attrs[ATTR_SIGNAL_STRENGTH] = self._camera.get_signal_strength,
self.attrs[ATTR_UNSEEN_VIDEOS] = self._camera.unseen_videos
self._camera.update()

View File

@@ -11,7 +11,7 @@ from homeassistant.const import (
CONF_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera.mjpeg import (
CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, MjpegCamera)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.dispatcher import dispatcher_connect
_LOGGER = logging.getLogger(__name__)
@@ -52,9 +52,9 @@ class AxisCamera(MjpegCamera):
"""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)
dispatcher_connect(hass,
DOMAIN + '_' + config[CONF_NAME] + '_new_ip',
self._new_ip)
def _new_ip(self, host):
"""Set new IP for video stream."""

View File

@@ -0,0 +1,95 @@
"""
Support for Canary camera.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.canary/
"""
import logging
import requests
from homeassistant.components.camera import Camera
from homeassistant.components.canary import DATA_CANARY, DEFAULT_TIMEOUT
DEPENDENCIES = ['canary']
_LOGGER = logging.getLogger(__name__)
ATTR_MOTION_START_TIME = "motion_start_time"
ATTR_MOTION_END_TIME = "motion_end_time"
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Canary sensors."""
data = hass.data[DATA_CANARY]
devices = []
for location in data.locations:
entries = data.get_motion_entries(location.location_id)
if entries:
devices.append(CanaryCamera(data, location.location_id,
DEFAULT_TIMEOUT))
add_devices(devices, True)
class CanaryCamera(Camera):
"""An implementation of a Canary security camera."""
def __init__(self, data, location_id, timeout):
"""Initialize a Canary security camera."""
super().__init__()
self._data = data
self._location_id = location_id
self._timeout = timeout
self._location = None
self._motion_entry = None
self._image_content = None
def camera_image(self):
"""Update the status of the camera and return bytes of camera image."""
self.update()
return self._image_content
@property
def name(self):
"""Return the name of this device."""
return self._location.name
@property
def is_recording(self):
"""Return true if the device is recording."""
return self._location.is_recording
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
if self._motion_entry is None:
return None
return {
ATTR_MOTION_START_TIME: self._motion_entry.start_time,
ATTR_MOTION_END_TIME: self._motion_entry.end_time,
}
def update(self):
"""Update the status of the camera."""
self._data.update()
self._location = self._data.get_location(self._location_id)
entries = self._data.get_motion_entries(self._location_id)
if entries:
current = entries[0]
previous = self._motion_entry
if previous is None or previous.entry_id != current.entry_id:
self._motion_entry = current
self._image_content = requests.get(
current.thumbnails[0].image_url,
timeout=self._timeout).content
@property
def motion_detection_enabled(self):
"""Return the camera motion detection status."""
return not self._location.is_recording

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,51 +1,40 @@
"""Support for viewing the camera feed from a DoorBird video doorbell."""
"""
Support for viewing the camera feed from a DoorBird video doorbell.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.doorbird/
"""
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.camera import 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)
_CAMERA_LIVE = "DoorBird Live"
_LAST_VISITOR_INTERVAL = datetime.timedelta(minutes=1)
_LIVE_INTERVAL = datetime.timedelta(seconds=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)")
async_add_devices([
DoorBirdCamera(device.live_image_url, _CAMERA_LIVE, _LIVE_INTERVAL),
DoorBirdCamera(
device.history_image_url(1, 'doorbell'), _CAMERA_LAST_VISITOR,
_LAST_VISITOR_INTERVAL),
])
class DoorBirdCamera(Camera):
@@ -75,7 +64,6 @@ class DoorBirdCamera(Camera):
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(_TIMEOUT, loop=self.hass.loop):
response = yield from websession.get(self._url)

6
homeassistant/components/camera/mqtt.py Executable file → Normal file
View File

@@ -60,11 +60,9 @@ class MqttCamera(Camera):
"""Return the name of this camera."""
return self._name
@asyncio.coroutine
def async_added_to_hass(self):
"""Subscribe MQTT events.
This method must be run in the event loop and returns a coroutine.
"""
"""Subscribe MQTT events."""
@callback
def message_received(topic, payload, qos):
"""Handle new MQTT messages."""

View File

@@ -0,0 +1,167 @@
"""
This component provides support to the Ring Door Bell camera.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/camera.ring/
"""
import asyncio
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.components.ring import (
DATA_RING, CONF_ATTRIBUTION, NOTIFICATION_ID)
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import ATTR_ATTRIBUTION, CONF_SCAN_INTERVAL
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
from homeassistant.util import dt as dt_util
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
DEPENDENCIES = ['ring', 'ffmpeg']
FORCE_REFRESH_INTERVAL = timedelta(minutes=45)
_LOGGER = logging.getLogger(__name__)
NOTIFICATION_TITLE = 'Ring Camera Setup'
SCAN_INTERVAL = timedelta(seconds=90)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL):
cv.time_period,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up a Ring Door Bell and StickUp Camera."""
ring = hass.data[DATA_RING]
cams = []
cams_no_plan = []
for camera in ring.doorbells:
if camera.has_subscription:
cams.append(RingCam(hass, camera, config))
else:
cams_no_plan.append(camera)
for camera in ring.stickup_cams:
if camera.has_subscription:
cams.append(RingCam(hass, camera, config))
else:
cams_no_plan.append(camera)
# show notification for all cameras without an active subscription
if cams_no_plan:
cameras = str(', '.join([camera.name for camera in cams_no_plan]))
err_msg = '''A Ring Protect Plan is required for the''' \
''' following cameras: {}.'''.format(cameras)
_LOGGER.error(err_msg)
hass.components.persistent_notification.async_create(
'Error: {}<br />'
'You will need to restart hass after fixing.'
''.format(err_msg),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
async_add_devices(cams, True)
return True
class RingCam(Camera):
"""An implementation of a Ring Door Bell camera."""
def __init__(self, hass, camera, device_info):
"""Initialize a Ring Door Bell camera."""
super(RingCam, self).__init__()
self._camera = camera
self._hass = hass
self._name = self._camera.name
self._ffmpeg = hass.data[DATA_FFMPEG]
self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS)
self._last_video_id = self._camera.last_recording_id
self._video_url = self._camera.recording_url(self._last_video_id)
self._utcnow = dt_util.utcnow()
self._expires_at = FORCE_REFRESH_INTERVAL + self._utcnow
@property
def name(self):
"""Return the name of this camera."""
return self._name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
'device_id': self._camera.id,
'firmware': self._camera.firmware,
'kind': self._camera.kind,
'timezone': self._camera.timezone,
'type': self._camera.family,
'video_url': self._video_url,
}
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
from haffmpeg import ImageFrame, IMAGE_JPEG
ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop)
if self._video_url is None:
return
image = yield from asyncio.shield(ffmpeg.get_image(
self._video_url, output_format=IMAGE_JPEG,
extra_cmd=self._ffmpeg_arguments), loop=self.hass.loop)
return image
@asyncio.coroutine
def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpeg
if self._video_url is None:
return
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
yield from stream.open_camera(
self._video_url, extra_cmd=self._ffmpeg_arguments)
yield from async_aiohttp_proxy_stream(
self.hass, request, stream,
'multipart/x-mixed-replace;boundary=ffserver')
yield from stream.close()
@property
def should_poll(self):
"""Update the image periodically."""
return True
def update(self):
"""Update camera entity and refresh attributes."""
_LOGGER.debug("Checking if Ring DoorBell needs to refresh video_url")
self._camera.update()
self._utcnow = dt_util.utcnow()
last_recording_id = self._camera.last_recording_id
if self._last_video_id != last_recording_id or \
self._utcnow >= self._expires_at:
_LOGGER.info("Ring DoorBell properties refreshed")
# update attributes if new video or if URL has expired
self._last_video_id = self._camera.last_recording_id
self._video_url = self._camera.recording_url(self._last_video_id)
self._expires_at = FORCE_REFRESH_INTERVAL + self._utcnow

View File

@@ -1,17 +1,25 @@
# Describes the format for available camera services
enable_motion_detection:
description: Enable the motion detection in a camera
description: Enable the motion detection in a camera.
fields:
entity_id:
description: Name(s) of entities to enable motion detection
description: Name(s) of entities to enable motion detection.
example: 'camera.living_room_camera'
disable_motion_detection:
description: Disable the motion detection in a camera
description: Disable the motion detection in a camera.
fields:
entity_id:
description: Name(s) of entities to disable motion detection
description: Name(s) of entities to disable motion detection.
example: 'camera.living_room_camera'
snapshot:
description: Take a snapshot from a camera.
fields:
entity_id:
description: Name(s) of entities to create snapshots from.
example: 'camera.living_room_camera'
filename:
description: Template of a Filename. Variable is entity_id.
example: '/tmp/snapshot_{{ entity_id }}'

View File

@@ -82,6 +82,7 @@ class UnifiVideoCamera(Camera):
self.is_streaming = False
self._connect_addr = None
self._camera = None
self._motion_status = False
@property
def name(self):
@@ -94,6 +95,12 @@ class UnifiVideoCamera(Camera):
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['recordingSettings']['fullTimeRecordEnabled']
@property
def motion_detection_enabled(self):
"""Camera Motion Detection Status."""
caminfo = self._nvr.get_camera(self._uuid)
return caminfo['recordingSettings']['motionRecordEnabled']
@property
def brand(self):
"""Return the brand of this camera."""
@@ -165,3 +172,26 @@ class UnifiVideoCamera(Camera):
raise
return _get_image()
def set_motion_detection(self, mode):
"""Set motion detection on or off."""
from uvcclient.nvr import NvrError
if mode is True:
set_mode = 'motion'
else:
set_mode = 'none'
try:
self._nvr.set_recordmode(self._uuid, set_mode)
self._motion_status = mode
except NvrError as err:
_LOGGER.error("Unable to set recordmode to " + set_mode)
_LOGGER.debug(err)
def enable_motion_detection(self):
"""Enable motion detection in camera."""
self.set_motion_detection(True)
def disable_motion_detection(self):
"""Disable motion detection in camera."""
self.set_motion_detection(False)

View File

@@ -0,0 +1,117 @@
"""
Support for Canary.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/canary/
"""
import logging
from datetime import timedelta
import voluptuous as vol
from requests import ConnectTimeout, HTTPError
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT
from homeassistant.helpers import discovery
from homeassistant.util import Throttle
REQUIREMENTS = ['py-canary==0.2.3']
_LOGGER = logging.getLogger(__name__)
NOTIFICATION_ID = 'canary_notification'
NOTIFICATION_TITLE = 'Canary Setup'
DOMAIN = 'canary'
DATA_CANARY = 'canary'
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
DEFAULT_TIMEOUT = 10
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}),
}, extra=vol.ALLOW_EXTRA)
CANARY_COMPONENTS = [
'alarm_control_panel', 'camera', 'sensor'
]
def setup(hass, config):
"""Set up the Canary component."""
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
timeout = conf.get(CONF_TIMEOUT)
try:
hass.data[DATA_CANARY] = CanaryData(username, password, timeout)
except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Canary service: %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
for component in CANARY_COMPONENTS:
discovery.load_platform(hass, component, DOMAIN, {}, config)
return True
class CanaryData(object):
"""Get the latest data and update the states."""
def __init__(self, username, password, timeout):
"""Init the Canary data object."""
from canary.api import Api
self._api = Api(username, password, timeout)
self._locations_by_id = {}
self._readings_by_device_id = {}
self._entries_by_location_id = {}
self.update()
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""Get the latest data from py-canary."""
for location in self._api.get_locations():
location_id = location.location_id
self._locations_by_id[location_id] = location
self._entries_by_location_id[location_id] = self._api.get_entries(
location_id, entry_type="motion", limit=1)
for device in location.devices:
if device.is_online:
self._readings_by_device_id[device.device_id] = \
self._api.get_latest_readings(device.device_id)
@property
def locations(self):
"""Return a list of locations."""
return self._locations_by_id.values()
def get_motion_entries(self, location_id):
"""Return a list of motion entries based on location_id."""
return self._entries_by_location_id.get(location_id, [])
def get_location(self, location_id):
"""Return a location based on location_id."""
return self._locations_by_id.get(location_id, [])
def get_readings(self, device_id):
"""Return a list of readings based on device_id."""
return self._readings_by_device_id.get(device_id, [])
def set_location_mode(self, location_id, mode_name, is_private=False):
"""Set location mode."""
self._api.set_location_mode(location_id, mode_name, is_private)
self.update(no_throttle=True)

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