Compare commits

...

585 Commits

Author SHA1 Message Date
Paulus Schoutsen
3f8c91d77c Merge pull request #15823 from home-assistant/rc
0.75.1
2018-08-04 15:26:51 +02:00
Paulus Schoutsen
ef5095cf53 Bumped version to 0.75.1 2018-08-04 15:24:44 +02:00
Daniel Høyer Iversen
5015071816 Fix rfxtrx device id matching (#15819)
* Issue #15773

Fix PT2262 devices are incorrectly matched in rfxtrx component

* style
2018-08-04 15:24:34 +02:00
superpuffin
b110a80fbd Upgrade Adafruit-DHT to 1.3.3 (#15706)
* Change to newer pip package

The package Adafruit_Python_DHT==1.3.2 was broken and would not install, breaking DHT sensor support in Home assistant. It has since been fixed in Adafruit-DHT==1.3.3.

See: https://github.com/adafruit/Adafruit_Python_DHT/issues/99

* Update requirements_all.txt

New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.

* Comment out Adafruit-DHT

Adafruit_Python_DHT changed name to Adafruit-DHT, which still need pyx support breaking our CI, need to be comment out.

* Update requirements_all.txt
2018-08-04 15:24:33 +02:00
Paulus Schoutsen
c482d48fde Bump frontend to 20180804.0 2018-08-04 15:21:32 +02:00
Paulus Schoutsen
7d9f8b0d4c Merge pull request #15806 from home-assistant/rc
0.75.0
2018-08-03 16:45:24 +02:00
Paulus Schoutsen
b8981b2675 Merge remote-tracking branch 'origin/master' into rc 2018-08-03 14:25:36 +02:00
Paulus Schoutsen
6028db21ab Bumped version to 0.75.0 2018-08-03 14:23:12 +02:00
Paulus Schoutsen
c63fd974fb Return True from Nest setup (#15797) 2018-08-03 14:22:40 +02:00
Robert Svensson
cdb86ed154 Only report color temp when in the correct color mode (#15791) 2018-08-03 14:22:40 +02:00
Bryan York
0f844311c9 Fix Min/Max Kelvin color temp attribute for Google (#15697)
* Fix Min/Max Kelvin color temp attribute for Google

Max Kelvin is actually Min Mireds and vice-versa. K = 1000000 / mireds

* Update test_smart_home.py

* Update test_trait.py
2018-08-03 14:22:39 +02:00
Paulus Schoutsen
8d2359026c Bump frontend to 20180803.0 2018-08-03 13:48:48 +02:00
Paulus Schoutsen
a5112f317d Update frontend to 20180802.0 2018-08-02 14:23:56 +02:00
Paulus Schoutsen
3ed47b05a5 Update translations 2018-08-02 13:43:36 +02:00
Paulus Schoutsen
163cd72b7a Bumped version to 0.75.0b1 2018-08-02 12:23:36 +02:00
Paulus Schoutsen
5e71f0f0d7 Bumped version to 0.75.0b0 2018-07-30 13:45:43 +02:00
Paulus Schoutsen
be61e2e714 Merge branch 'dev' into rc 2018-07-30 13:45:35 +02:00
Jason Hu
1e5596b594 Remove self type hints (#15732)
* Remove self type hints

* Lint
2018-07-30 12:44:31 +01:00
Dan Faulknor
744c277123 Add other wemo motion sensor identifier (#15627)
* Add other motion sensor identifier

* Fix order
2018-07-30 10:38:49 +02:00
David Straub
460bb69ade Add mvglive option to store multiple departures in attributes (#15454)
* MVG Live sensor: add option to store multiple departures in attributes

* Fix lint error

* mvglive: take into account timeoffset in API call

* Prevent exception if departure list is empty

* Rename state_attributes -> device_state_attributes
2018-07-30 10:32:39 +02:00
Josh Shoemaker
8dbe78a21a Add Genie Aladdin Connect cover component (#15699)
* Add Genie Aladdin Connect cover component

* Fix lines being too long

* Fix issues found in review

* remove Unknown state, use None instead

* Fixed requirements_all
2018-07-30 07:19:34 +02:00
Juha Niemi
3959f82030 Make FutureNow light remember last brightness when turning on (#15733)
* Remember last brightness value and use it on turn_on()

* Pyfnip-0.2 now returns state reliably, no manual changes needed.

* Split too long line of code

* Updated pyfnip library version
2018-07-30 07:09:59 +02:00
starkillerOG
48ba13bc6c Denonavr version push to 0.7.5 (#15743)
* Version push to 0.7.5

Improve logger warning

* Denonavr v.0.7.5
2018-07-29 15:42:23 -07:00
Fabian Affolter
681082a3ad Various updates (#15738) 2018-07-29 23:39:01 +02:00
Fabian Affolter
4013a90f33 Upgrade pyowm to 2.9.0 (#15736) 2018-07-29 23:37:38 +02:00
Fabian Affolter
316ef89541 Upgrade youtube_dl to 2018.07.29 (#15734) 2018-07-29 23:37:10 +02:00
Fabian Affolter
a8dd81e986 Upgrade voluptuous to 0.11.3 (#15735) 2018-07-29 23:36:28 +02:00
Fabian Affolter
4b257c3d01 Upgrade sqlalchemy to 1.2.10 (#15737) 2018-07-29 23:35:47 +02:00
Fabian Affolter
491bc006b2 Upgrade mutagen to 1.41.0 (#15739) 2018-07-29 23:35:27 +02:00
Fabian Affolter
28ad0017e1 Upgrade beautifulsoup4 to 4.6.1 (#15727) 2018-07-29 19:55:49 +02:00
Peter Nijssen
5849381dfb Upgrade spiderpy to 1.2.0 (#15729) 2018-07-29 19:49:36 +02:00
Fabian Affolter
baa974a487 Upgrade numpy to 1.15.0 (#15722) 2018-07-29 08:46:20 +02:00
Fabian Affolter
1a97ba1b46 Upgrade youtube_dl to 2018.07.21 (#15718) 2018-07-29 08:46:06 +02:00
Alexander Hardwicke
1d68f4e279 Command Line Sensor - json_attributes (#15679)
* Add tests to command_line for json_attrs

* Add json_attrs to command_line

* Remove whitespace on blank line

* Stick to <80 row length

* Use collections.Mapping, not dict

* Rename *attrs to *attributes

* Remove extraneous + for string concat

* Test multiple keys

* Add test

Makes sure the sensor's attributes don't contain a value for a missing key,
even if we want that key.

* Test that unwanted keys are skipped

* Remove additional log line

* Update tests for log changes

* Fix ordering
2018-07-29 08:37:34 +02:00
Jonathan Keljo
a2b793c61b Add a component for Sisyphus Kinetic Art Tables (#14472)
* Add a component for Sisyphus Kinetic Art Tables

The [Sisyphus Kinetic Art Table](https://sisyphus-industries.com/) uses a
steel ball to draw intricate patterns in sand, thrown into sharp relief by a
ring of LED lights around the outside.

This component enables basic control of these tables through Home Assistant.

* Fix lints

* Docstrings, other lints

* More lints

* Yet more.

* Feedback

* Lint

* Missed one piece of feedback

* - Use async_added_to_hass in media player
- async_schedule_update_ha_state in listeners
- constants for supported features
- subscripting for required keys
- asyncio.wait
- update to sisyphus-control with passed-in session

* Update requirements

* lint
2018-07-29 07:34:43 +02:00
Jason Hu
93d6fb8c60 Break up components/auth (#15713) 2018-07-28 17:54:26 -07:00
Paulus Schoutsen
c7f4bdafc0 Context (#15674)
* Add context

* Add context to switch/light services

* Test set_state API

* Lint

* Fix tests

* Do not include context yet in comparison

* Do not pass in loop

* Fix Z-Wave tests

* Add websocket test without user
2018-07-28 17:53:37 -07:00
Jens Østergaard Nielsen
867f80715e Remove IHC XML Element from discovery data (#15719) 2018-07-28 19:37:12 +02:00
Alan Fischer
29e668e887 Upgrade pyvera to 0.2.44 (#15708) 2018-07-28 19:17:04 +02:00
JC Connell
944f4f7c05 Add Magicseaweed API support (#15132)
* Added support for magicseaweed surf forecasting

* Added support for magicseaweed surf forecasting

* Added support for magicseaweed surf forecasting

* Incorporate @bachya requested changes.

* Adding support for magicseaweed package.

* Run tests and fix errors.

* Incorporate @balloob requested changes.

* Attempt to fix pylint error e1101.

* Two spaces before inline comments

* Add @MartinHjelmare & @balloob requested changes.

* Remove MagicSeaweedData object inheritance.

* Fix variable logic.
2018-07-27 23:48:56 +02:00
Richard Orr
cd6544d32a Add support for alarm_control_panel to MQTT Discovery. (#15689) 2018-07-27 17:16:49 +02:00
Jason Hu
b2f4bbf93b Only log change to use access token warning once (#15690) 2018-07-27 15:53:46 +02:00
Juha Niemi
a99b4472a8 Add support for P5 FutureNow light platform (#15662)
* Added support for FutureNow light platform and relay/dimmer units

* Pinned specific version for requirement

* Added support for FutureNow light platform and relay/dimmer units

* Added futurenow.py to .coveragerc.

* Minor fixes and enhancements as requested in the code review.

* Minor fixes and enhancements as requested in the code review.

* Use device_config's value directly as it's validated as boolean.

* Simplify state check.

* Fixed brightness update that was broken in previous commit.
2018-07-27 11:11:32 +02:00
Peter Nijssen
33f3e72dda Add spider power plug component (#15682)
* Add spider power plug component

* rounding down the numbers

* ability to throttle the API

* updated to the lastest api

* resolved an issue within the API
2018-07-26 21:43:20 -07:00
Aaron Bach
e30510a688 Fixes a bug with showing a subset of Pollen index conditions (#15694) 2018-07-26 12:31:44 -06:00
Paulus Schoutsen
974fe4d923 Fix frontend tests 2018-07-26 10:25:57 +02:00
Paulus Schoutsen
feb8aff46b Bump frontend to 20180726.0 2018-07-26 09:38:10 +02:00
Ville Skyttä
eee9b50b70 Upgrade pylint to 2.0.1 (#15683)
* Upgrade pylint to 2.0.1

* Pylint 2 bad-whitespace fix

* Pylint 2 possibly-unused-variable fixes

* Pylint 2 try-except-raise fixes

* Disable pylint fixme for todoist for now

https://github.com/PyCQA/pylint/pull/2320

* Disable pylint 2 useless-return for now

https://github.com/PyCQA/pylint/issues/2300

* Disable pylint 2 invalid-name for type variables for now

https://github.com/PyCQA/pylint/issues/1290

* Disable pylint 2 not-an-iterable for now

https://github.com/PyCQA/pylint/issues/2311

* Pylint 2 unsubscriptable-object workarounds

* Disable intentional pylint 2 assignment-from-nones

* Disable pylint 2 unsupported-membership-test apparent false positives

* Disable pylint 2 assignment-from-no-return apparent false positives

* Disable pylint 2 comparison-with-callable false positives

https://github.com/PyCQA/pylint/issues/2306
2018-07-26 08:55:42 +02:00
Jason Hu
9fb8bc8991 Allow Nest Cam turn on/off (#15681)
* Allow Nest Cam turn on/off

* Don't raise Error

* Remove unnecessary state update
2018-07-25 23:17:38 +02:00
Ville Skyttä
1c42caba76 Pylint 2 useless-return fixes (#15677) 2018-07-25 19:35:57 +02:00
Paulus Schoutsen
9d59bfbe00 0.74.2 (#15671)
* Fix CORS duplicate registration (#15670)

* Bumped version to 0.74.2
2018-07-25 13:09:32 +02:00
Eduard van Valkenburg
95dc06cca6 Add Brunt Cover Device (#15653)
* New Brunt Branch

* Some small changes and updates based on review.
2018-07-25 12:17:12 +02:00
Peter Nijssen
9ecbf86fa0 Add spider thermostat (#15499)
* add spider thermostats

* Added load_platform. Added operation dictionary. Minor improvements

* loop over spider components for load_platform

* added empty dict to load_platform. changed add_devices

* moved logic to the API

* fix requirements_all.txt

* minor code improvements
2018-07-25 11:51:48 +02:00
Paulus Schoutsen
588fd1923f Bumped version to 0.74.2 2018-07-25 11:37:17 +02:00
Paulus Schoutsen
2824efd505 Fix CORS duplicate registration (#15670) 2018-07-25 11:37:11 +02:00
Paulus Schoutsen
169c8d793a Fix CORS duplicate registration (#15670) 2018-07-25 11:36:44 +02:00
Ville Skyttä
68f03dcc67 Auth typing improvements (#15640)
* Always return bytes from auth.providers.homeassistant.hash_password

Good for interface cleanliness, typing etc.

* Add some homeassistant auth provider type annotations
2018-07-25 11:36:03 +02:00
Ville Skyttä
397f551e6d Import collections abstract base classes from collections.abc (#15649)
Accessing them directly through collections is deprecated since 3.7, and
will no longer work in 3.8.
2018-07-25 11:35:22 +02:00
Jerad Meisner
cbb5d34167 Added user credentials to current_user ws endpoint. (#15558)
* Added user credentials to current_user ws endpoint.

* Comments. Added another test.

* Return list of credentials.
2018-07-25 10:34:18 +02:00
Daniel Kalmar
0cc9798c8f Allow defining default turn-on values for lights in the profiles file. (#15493)
* Allow defining default turn-on values for lights in the profiles file.

* Mock out file operations in unit test.

* Fix unit test flakiness.

* Avoid unnecessary copy
2018-07-24 20:29:59 +02:00
Jason Hu
45a7ca62ae Add turn_on/off service to camera (#15051)
* Add turn_on/off to camera

* Add turn_on/off supported features to camera.

Add turn_on/off service implementation to camera, add turn_on/off
 supported features and services to Demo camera.

* Add camera supported_features tests

* Resolve code review comment

* Fix unit test

* Use async_add_executor_job

* Address review comment, change DemoCamera to local push

* Rewrite tests/components/camera/test_demo

* raise HTTPError instead return response
2018-07-24 10:13:26 -07:00
Giuseppe
2eb125e90e Downgrade netatmo warning log to info (#15652) 2018-07-24 18:35:57 +02:00
Paulus Schoutsen
264c618b11 Bump frontend to 20180724.0 2018-07-24 14:16:25 +02:00
Paulus Schoutsen
d9cf8fcfe8 Allow changing entity ID (#15637)
* Allow changing entity ID

* Add support to websocket command

* Address comments

* Error handling
2018-07-24 14:12:53 +02:00
Paulus Schoutsen
5e9c1098c0 Merge pull request #15651 from home-assistant/rc
0.74.1
2018-07-24 13:43:25 +02:00
Paulus Schoutsen
d65bd7b7ea Bumped version to 0.74.1 2018-07-24 11:20:13 +02:00
Paulus Schoutsen
45a5ae1f23 Cast/Sonos: create config entry if manually configured (#15630)
* Cast/Sonos: create config entry if manually configured

* Add test for helper
2018-07-24 11:20:07 +02:00
Jason Hu
3eda6db227 Frontend component should auto load auth coomponent (#15606) 2018-07-24 11:20:07 +02:00
Anders Melchiorsen
58f287f551 Use case insensitive comparison for Sonos model check (#15604) 2018-07-24 11:20:07 +02:00
cdce8p
f62f64311d Bugfix HomeKit name and serial_number (#15600)
* Bugfix HomeKit name and serial_number

* Revert serial_number changes
2018-07-24 11:20:06 +02:00
Jan Collijs
fbeaa57604 Update smappy library version (#15636)
Adding latest smappy lib version

Updated smappy library version
2018-07-24 10:41:24 +02:00
huangyupeng
c1f5ead61d Add Tuya cover and scene platform (#15587)
* Add Tuya cover platform

* Add Tuya cover and scene

* fix description

* remove scene default method
2018-07-24 10:29:43 +02:00
Jason Hu
d7690c5fda Add ipban for failed login attempt in new login flow (#15551)
* Add ipban for failed login attempt in new login flow

* Address review comment

* Use decorator to clean up code
2018-07-24 10:09:52 +02:00
Cheong Yip
45c35ceb2b Fix typo asayn_init instead of async_init (#15645) 2018-07-23 20:19:01 -06:00
Daniel Shokouhi
bc481fa366 Update Neato library to allow for dynamic endpoints (#15639) 2018-07-24 00:46:12 +02:00
John Arild Berentsen
1b94fe3613 Add ability to set Zwave protection commandclass (#15390)
* Add API for protection commandclass

* Adjusting

* tests

* Spelling

* Missed flake8

* Period

* spelling

* Review changes

* removing additional .keys()

* period

* Move i/o out into executor pool

* Move i/o out into executor pool

* Forgot get method

* Do it right... I feel stupid

* Long lines

* Merging
2018-07-23 15:31:12 +02:00
Paulus Schoutsen
3204501174 Cast/Sonos: create config entry if manually configured (#15630)
* Cast/Sonos: create config entry if manually configured

* Add test for helper
2018-07-23 15:08:03 +02:00
Pascal Vizeli
f3dfc433c2 Fix aiohttp connection reset errors (#15577)
* Fix aiohttp connection reset errors

* Update aiohttp_client.py

* Update aiohttp_client.py

* Update __init__.py

* Update mjpeg.py

* Update mjpeg.py

* Update ffmpeg.py

* Update ffmpeg.py

* Update ffmpeg.py

* Update proxy.py

* Update __init__.py

* Update aiohttp_client.py

* Update aiohttp_client.py

* Update proxy.py

* Update proxy.py

* Fix await inside coroutine

* Fix async syntax

* Lint
2018-07-23 14:36:36 +02:00
Paulus Schoutsen
8213b1476f WIP: Hass.io sent token to supervisor (#15536)
Hass.io sent token to supervisor
2018-07-23 14:14:57 +02:00
Paulus Schoutsen
4e7dbf9ce5 Allow system users to refresh tokens (#15574) 2018-07-23 14:06:09 +02:00
Paulus Schoutsen
ea2ff6aae3 Use async_create_task (#15633)
* Use async_create_task

* Fix test
2018-07-23 14:05:38 +02:00
starkillerOG
50b6c5948d Suppress error between 00:00 and 01:00 (#15555)
* Suppress error between 00:00 and 01:00

Suppress an error that often occers between 00:00 and 01:00 CE(S)T during that time, probably because buienradar.nl is then updating its forcast for the next day. The API does not always work between these times (in the middle of the night).

* white space & import

* unnecessary brackets
2018-07-23 12:37:23 +02:00
Muhammad Sheraz Lodhi
3acbd5a769 The tense is wrong (#15614)
Instead of spent, we should be using spend :)
2018-07-23 12:31:54 +02:00
Anders Melchiorsen
fddfb9e412 Refresh Sonos source list on changes (#15605) 2018-07-23 12:31:03 +02:00
Anders Melchiorsen
1325682d82 Use case insensitive comparison for Sonos model check (#15604) 2018-07-23 12:29:37 +02:00
Andrey
140a874917 Add typing to homeassistant/*.py and homeassistant/util/ (#15569)
* Add typing to homeassistant/*.py and homeassistant/util/

* Fix wrong merge

* Restore iterable in OrderedSet

* Fix tests
2018-07-23 10:24:39 +02:00
Ville Skyttä
b7c336a687 Pylint cleanups (#15626)
* Pylint 2 no-else-return fixes

* Remove unneeded abstract-class-not-used pylint disable
2018-07-23 10:16:05 +02:00
Ville Skyttä
a38c0d6d15 Upgrade mypy to 0.620 (#15612) 2018-07-22 13:37:26 +02:00
Paulus Schoutsen
75f40ccb06 Remove entity picture of Tuya entity (#15611) 2018-07-22 12:10:32 +02:00
cdce8p
4de847f84e Bugfix HomeKit name and serial_number (#15600)
* Bugfix HomeKit name and serial_number

* Revert serial_number changes
2018-07-22 09:51:42 +02:00
Jason Hu
33f1577dac Frontend component should auto load auth coomponent (#15606) 2018-07-22 09:49:58 +02:00
Anders Melchiorsen
ef3a83048c Throttle unavailability warnings for tplink light/switch (#15591) 2018-07-22 00:51:45 +02:00
Daniel Perna
ae2ee8f006 Update pyhomematic, fixes #15054, #15190 (#15603) 2018-07-22 00:18:50 +02:00
digiblur
6f6d86c700 Add relay addr & chan config to alarmdecoder zones (#15242)
Add relay addr & chan config to alarmdecoder zones
2018-07-21 17:31:07 +02:00
Anders Melchiorsen
d1b16e287c Add unique_id to netgear_lte sensors (#15584) 2018-07-21 10:14:56 +02:00
Ryan Davies
ee8a815e6b Allow MQTT Switch to have an optional state configuration (#15430)
Switches by default use the payload_on and payload_off configuration parameters to specify both the payload the switch should send for a state but also what will be returned for the current state - which isnt always the same
As a toggle switch might always send an ON or TOGGLE to toggle the switch, but still receive an ON or an OFF for the state topic - This change allows for splitting them apart
2018-07-20 23:04:06 +02:00
Paulus Schoutsen
7bc2362e33 Merge branch 'master' into dev 2018-07-20 15:19:06 +02:00
Eugenio Panadero
9a8389060c fix aiohttp InvalidURL exception when fetching media player image (#15572)
* fix aiohttp InvalidURL exception when fetching media player image

The first call for the HA proxy (`/api/media_player_proxy/media_player.kodi?token=...&cache=...`)
is receiving relative urls that are failing, this is a simple fix to precede the base_url when hostname is None.

* fix import location and sort stdlib imports
2018-07-20 15:18:02 +02:00
Paulus Schoutsen
da3366859d Merge pull request #15570 from home-assistant/rc
0.74
2018-07-20 15:11:18 +02:00
Teemu R
200c0a8778 light.tplink: initialize min & max mireds only once, avoid i/o outside update (#15571)
* light.tplink: initialize min & max mireds only once, avoid i/o outside update

* revert the index change

* fix indent, sorry for overwriting your fix, balloob
2018-07-20 14:40:38 +02:00
Teemu R
5cf9cd686c light.tplink: initialize min & max mireds only once, avoid i/o outside update (#15571)
* light.tplink: initialize min & max mireds only once, avoid i/o outside update

* revert the index change

* fix indent, sorry for overwriting your fix, balloob
2018-07-20 14:40:10 +02:00
Paulus Schoutsen
8e659baf25 Bumped version to 0.74.0 2018-07-20 12:44:15 +02:00
Jason Hu
2aa54ce22b Reset failed login attempts counter when login success (#15564) 2018-07-20 12:33:21 +02:00
Paulus Schoutsen
eff334a1d0 Remove relative time from state machine (#15560) 2018-07-20 12:32:45 +02:00
Paulus Schoutsen
b3bed7fb37 Allow auth providers to influence is_active (#15557)
* Allow auth providers to influence is_active

* Fix auth script test
2018-07-20 12:32:44 +02:00
Martin Hjelmare
61b3822374 Upgrade pymysensors to 0.16.0 (#15554) 2018-07-20 12:32:44 +02:00
Paulus Schoutsen
9fb04b5280 Update the frontend to 20180720.0 2018-07-20 12:30:32 +02:00
Paulus Schoutsen
3341c5cf21 Update the frontend to 20180720.0 2018-07-20 12:30:10 +02:00
Jason Hu
f1286f8e6b Reset failed login attempts counter when login success (#15564) 2018-07-20 12:09:48 +02:00
huangyupeng
f2a99e83cd Add Tuya fan support (#15525)
* Add Tuya fan platform

* Add Tuya fan platform

* fix as review required
2018-07-20 11:23:09 +02:00
Ville Skyttä
2f7b79764a More pylint 2 fixes (#15565)
## Description:

More fixes flagged by pylint 2 that don't hurt to have before the actual pylint 2 upgrade (which I'll submit soon).

## Checklist:
  - [ ] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
2018-07-20 11:45:20 +03:00
Paulus Schoutsen
ea18e06b08 Remove relative time from state machine (#15560) 2018-07-19 23:12:17 +02:00
Martin Hjelmare
a0193e8e42 Upgrade pymysensors to 0.16.0 (#15554) 2018-07-19 22:52:03 +02:00
Paulus Schoutsen
2fcacbff23 Allow auth providers to influence is_active (#15557)
* Allow auth providers to influence is_active

* Fix auth script test
2018-07-19 22:10:36 +02:00
William Scanlon
a42288d056 Upgrade to simplisafe-python v2 to use new SimpliSafe API (#15542)
* Upgrade to simplisafe-python v2 to use new SimpliSafe API
2018-07-19 13:13:46 -04:00
Paulus Schoutsen
7aa2a9e506 Bumped version to 0.74.0b4 2018-07-19 12:26:15 +02:00
Paulus Schoutsen
2fc0d83085 Allow CORS requests to token endpoint (#15519)
* Allow CORS requests to token endpoint

* Tests

* Fuck emulated hue

* Clean up

* Only cors existing methods
2018-07-19 12:26:08 +02:00
Paulus Schoutsen
ca0d4226aa Decouple emulated hue from http server (#15530) 2018-07-19 12:25:47 +02:00
Paulus Schoutsen
dff2e4ebc2 Don't be so strict client-side (#15546) 2018-07-19 12:23:14 +02:00
Jerad Meisner
9c337bc621 Added WS endpoint for changing homeassistant password. (#15527)
* Added WS endpoint for changing homeassistant password.

* Remove change password helper. Don't require current password.

* Restore current password verification.

* Added tests.

* Use correct send method
2018-07-19 12:23:14 +02:00
Paulus Schoutsen
5a1360678b Bump frontend to 20180719.0 2018-07-19 10:55:33 +02:00
Paulus Schoutsen
33ee91a748 Bump frontend to 20180719.0 2018-07-19 10:52:28 +02:00
Jerad Meisner
396895d077 Added WS endpoint for changing homeassistant password. (#15527)
* Added WS endpoint for changing homeassistant password.

* Remove change password helper. Don't require current password.

* Restore current password verification.

* Added tests.

* Use correct send method
2018-07-19 09:39:51 +02:00
Paulus Schoutsen
8b04d48ffd Update config entry id in entity registry (#15531) 2018-07-19 08:37:13 +02:00
Paulus Schoutsen
2a76a0852f Allow CORS requests to token endpoint (#15519)
* Allow CORS requests to token endpoint

* Tests

* Fuck emulated hue

* Clean up

* Only cors existing methods
2018-07-19 08:37:00 +02:00
quthla
22d961de70 Update reading when device is added (#15548) 2018-07-18 23:39:37 +02:00
Paulus Schoutsen
4650366f07 Don't be so strict client-side (#15546) 2018-07-18 23:00:26 +02:00
Paulus Schoutsen
7b8ad64ba5 Bumped version to 0.74.0b3 2018-07-18 17:41:36 +02:00
Jason Hu
e64761b15e Disallow use insecure_example auth provider in configuration.yml (#15504)
* Disallow use insecure_example auth provider in configuration.yml

* Add unit test for auth provider config validate
2018-07-18 17:41:22 +02:00
Paulus Schoutsen
61273ff606 Bump frontend to 20180718.0 2018-07-18 17:34:28 +02:00
Paulus Schoutsen
dfe17491f8 Bump frontend to 20180718.0 2018-07-18 17:34:16 +02:00
Giel Janssens
a8c7425e17 Update pyatmo (#15540) 2018-07-18 16:58:45 +02:00
Tom Harris
e5f0da75e2 Mini-Remote events (#15523)
* Add event handler to capture binary sensor on messages

* Log event trigger

* Log event firing

* Capture platform correctly

* Fix test for platform eq binary_sensor

* Create sensor events

* Add light and battery sensors

* Bump insteonplm version to 0.11.6

* Fix naming of BUTTON_PRESSED_STATE_NAME

* Fix naming of fire event methods

* Add logging

* Add DOMAIN definition

* Get state name from plm.devices

* Remove stale reference to button ID

* Fix reference to state name

* Remove incorrect ref to self

* Log remote button pressed event

* Change mode to button_mode and fix values to array

* Rename CONF_MODE to CONF_BUTTON_MODE

* Log platform create with mode

* Properly assign button_mode to track mode

* Implement is_on

* Change mini-remotes to events only

* Remove button_mode config option

* Fix reference to _fire_button_on_off_event

* Bump insteon version to 0.11.7

* Flake8 clean up

* Flake8 cleanup

* Use % format in logging per pylint

* Code review updates

* Resolve conflict

* Lint
2018-07-18 16:11:54 +02:00
fucm
6834e00be6 Add support for Tahoma Soke Sensor (#15441) 2018-07-18 12:38:34 +02:00
John Arild Berentsen
26375a3014 Make RS room thermostat discoverable (#15451)
* Make RS room thermostat discoverable

* Reversed generic type name
2018-07-18 12:20:02 +02:00
Daniel Shokouhi
06c3f756b1 Implement locate service for neato (#15467)
* Implement locate service for neato

* Hound
2018-07-18 12:19:38 +02:00
Mattias Welponer
9c5bbfe96d Cleanup of HomematicIP Cloud code (#15475)
* Check if device supports lowBat and shows it only if battery is low

* Show empty battery icon if lowBat is true

* Default return None

* Sabotage attribute and icon if device has this feature

* Bug fix and cleanup

* Use dedicated function for security state

* Cleanup of sensor attributes and icons

* Empty
2018-07-18 12:19:08 +02:00
Anders Melchiorsen
e427f9ee38 RFC: Only use supported light properties (#15484)
* Only use supported light properties

* Fix tests
2018-07-18 12:18:22 +02:00
Andrey
e62e2bb131 Make sure that only pypi dependencies are used (#15490) 2018-07-18 12:16:27 +02:00
Ville Skyttä
bf17ed0917 More pylint 2 fixes (#15516)
* Pylint 2 useless-import-alias fixes

* Pylint 2 chained-comparison fixes

* Pylint 2 consider-using-get fixes

* Pylint 2 len-as-condition fixes
2018-07-18 11:54:27 +02:00
Pascal Vizeli
058081b1f5 Moon translate (#15498)
* Translate moon

* Create strings.moon.json

* Update moon.py

* Update strings.moon.json

* Update test_moon.py
2018-07-18 10:54:54 +02:00
Paulus Schoutsen
98722e10fc Decouple emulated hue from http server (#15530) 2018-07-18 10:47:06 +02:00
Ville Skyttä
2781796d9c Remove some unused imports (#15529) 2018-07-18 10:46:14 +02:00
Andrey
24d2261060 Add check_untyped_defs (#15510)
* Add check_untyped_defs

* Change to regular if-else
2018-07-18 00:28:44 +02:00
lich
7d7c2104ea Customizable command timeout (#15442)
* Customizable command timeout

* Change string to int

* update the tests. Do the same thing on the binary_sensor.command_line.
2018-07-17 22:58:30 +02:00
Dario Iacampo
4ab502a691 Support latest tplink Archer D9 Firmware version / Device Scanner (#15356)
* Support latest tplink Archer D9 Firmware version / Device Scanner

* tplink integration on pypi package

* initialize the client only once

* remove unnecessary instance attributes
2018-07-17 22:47:32 +02:00
huangyupeng
9292d9255c Add Tuya climate platform (#15500)
* Add Tuya climate platform

* fix as review required

* fix as review required
2018-07-17 20:33:54 +02:00
Jason Hu
2022d39339 Disallow use insecure_example auth provider in configuration.yml (#15504)
* Disallow use insecure_example auth provider in configuration.yml

* Add unit test for auth provider config validate
2018-07-17 19:36:33 +02:00
Ville Skyttä
e31dd4404e Pylint 2 fixes (#15487)
* pylint 2 inline disable syntax fixes

* pylint 2 logging-not-lazy fixes

* pylint 2 consider-using-in fixes

* Revert pylint 2 inline disable syntax fixes addressing unused-imports

Will have a go at removing more unused imports altogether first.
2018-07-17 19:34:29 +02:00
Paulus Schoutsen
5dc29bd2c3 Bumped version to 0.74.0b2 2018-07-17 10:59:07 +02:00
Paulus Schoutsen
20c316bce4 Bump frontend to 20180717.0 2018-07-17 10:58:58 +02:00
Matthew Garrett
8b475f45e9 Update HomeKit module code (#15502)
This fixes a bunch of bugs, including issues with concurrency in devices
that present multiple accessories, devices that insist on the TLV entries
being in the order that Apple use, and handling devices that send headers
and data in separate chunks. This should improve compatibility with
a whole bunch of HomeKit devices.
2018-07-17 10:58:51 +02:00
Paulus Schoutsen
a4318682f7 Add onboarding support (#15492)
* Add onboarding support

* Lint

* Address comments

* Mark user step as done if owner user already created
2018-07-17 10:58:51 +02:00
Paulus Schoutsen
a14d8057ed Add current user WS command (#15485) 2018-07-17 10:58:50 +02:00
Paulus Schoutsen
d2f4bce6c0 Bump frontend to 20180717.0 2018-07-17 10:57:05 +02:00
Paulus Schoutsen
b0a3207454 Add onboarding support (#15492)
* Add onboarding support

* Lint

* Address comments

* Mark user step as done if owner user already created
2018-07-17 10:49:15 +02:00
Matthew Garrett
db3cdb288e Update HomeKit module code (#15502)
This fixes a bunch of bugs, including issues with concurrency in devices
that present multiple accessories, devices that insist on the TLV entries
being in the order that Apple use, and handling devices that send headers
and data in separate chunks. This should improve compatibility with
a whole bunch of HomeKit devices.
2018-07-17 10:06:06 +02:00
Paulus Schoutsen
8797cb78a9 Add current user WS command (#15485) 2018-07-17 09:24:51 +02:00
Luke Fritz
7eb5cd1267 Bump pyarlo==0.2.0, fixes #15486 (#15503) 2018-07-17 07:56:50 +02:00
squirtbrnr
0b2aff61bb Delay setup of waze travel time component (#15455)
* delay setup of component

Copied the necessary lines of code from the google travel time component to fix the setup delay in waze travel time component.  Previously it was only watching the homeassistant start event on the bus, but doing nothing with it.

* Update waze_travel_time.py

* Update waze_travel_time.py
2018-07-16 22:50:56 -06:00
Paulus Schoutsen
55f8b0a2f5 Bumped version to 0.74.0b1 2018-07-16 22:14:51 +02:00
Paulus Schoutsen
bb37300a48 Merge branch 'master' into rc 2018-07-16 22:14:45 +02:00
Paulus Schoutsen
0f12b37977 0.73.2 - security release (#15494)
* Extract SSL context creation to helper (#15483)

* Extract SSL context creation to helper

* Lint

* Bumped version to 0.73.2
2018-07-16 22:13:12 +02:00
Paulus Schoutsen
ad4cba70a0 Extract SSL context creation to helper (#15483)
* Extract SSL context creation to helper

* Lint
2018-07-16 10:32:07 +02:00
Paulus Schoutsen
dd7890c848 Version bump to 0.75.0.dev0 2018-07-16 08:52:37 +02:00
Paulus Schoutsen
7f18739267 Bumped version to 0.74.0b0 2018-07-16 08:51:52 +02:00
Paulus Schoutsen
a1b478b3ac Version bump to 0.74.0.dev0 2018-07-16 08:51:37 +02:00
Paulus Schoutsen
edf1f44668 Bump frontend to 20180716.0 2018-07-16 08:50:21 +02:00
Anders Melchiorsen
60f780cc37 Update limitlessled to 1.1.2 (#15481) 2018-07-15 23:24:35 +02:00
Anders Melchiorsen
7d0cc7e26c Fix flux_led turning on with color or effect (#15472) 2018-07-15 23:18:52 +02:00
Paulus Schoutsen
864a254071 Aware comments (#15480)
* Make sure we cannot deactivate the owner

* Use different error code when trying to fetch token for inactive user
2018-07-15 23:09:05 +02:00
Andrey
5995c6a2ac Switch to own packaged version of pygtfs (#15040) 2018-07-15 21:32:20 +02:00
Paulus Schoutsen
ed0cfc4f31 Add user via cmd line creates owner (#15470)
* Add user via cmd line creates owner

* Ensure access tokens are not verified for inactive users

* Stale print

* Lint
2018-07-15 20:46:15 +02:00
Mattias Welponer
6db069881b Update homematicip_cloud with enum states (#15460)
* Update to next version with enum states

* Change to generic dimmer class

* Update of requirement files

* Update to hmip lib to v0.9.7

* Missing update of requirements files

* Cleanup of icon properties
2018-07-15 02:59:19 +02:00
huangyupeng
ca4f69f557 Add Tuya light platform (#15444)
* add tuya light platform

* fix as review required
2018-07-15 02:48:32 +02:00
Ville Skyttä
37ccf87516 Remove unnecessary executable permissions (#15469) 2018-07-14 23:03:36 +02:00
Tom Harris
201c9fed77 Implement is_on (#15459)
* Implement is_on

* Remove var
2018-07-14 11:04:00 +02:00
Daniel Perna
3b5775573b Add IPPassageSensor (HmIP-SPDR) (#15458) 2018-07-14 02:31:51 +02:00
Jason Antman
6e22a0e4d9 Fix ZWave RGBW lights not producing color without explicit white_value (#15412)
* Fix ZWave RGBW lights not producing color without explicit white_value (#13930)

* simplify conditional in previous commit (#13930)

* ZwaveColorLight - only zero _white if white_value not specified in call (#13930)
2018-07-14 00:54:15 +02:00
Mattias Welponer
ce5b4cd51e Add HomematicIP Cloud dimmer light device (#15456)
* Add dimmable light device

* Add imports

* Fix float and int conversion
2018-07-13 23:25:11 +02:00
Paulus Schoutsen
538236de8f Fix formatting pylint comments in test (#15450) 2018-07-13 23:02:23 +02:00
Fabian Affolter
1007bb83aa Upgrade keyring to 13.2.1 (#15453) 2018-07-13 20:02:13 +02:00
Fabian Affolter
79955a5785 Catch the ValueError if the bulb was in the wrong mode (#15434) 2018-07-13 20:01:57 +02:00
Andrey
e60f9ca392 More typing (#15449)
## Description:

More typing improvements.

Switch to using `mypy.ini` for flexibility

Add `warn_return_any` check except in `homeassistant.util.yaml` that does typing hacks. Fix some type annotations as resulting from this check and ignore others were fixing is hard.

## Checklist:
  - [x] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
2018-07-13 20:14:45 +03:00
Paulus Schoutsen
ae581694ac Bump frontend to 20180713.0 2018-07-13 15:33:33 +02:00
Paulus Schoutsen
70fe463ef0 User management (#15420)
* User management

* Lint

* Fix dict

* Reuse data instance

* OrderedDict all the way
2018-07-13 15:31:20 +02:00
Paulus Schoutsen
84858f5c19 Fix comment formatting (#15447) 2018-07-13 15:05:55 +02:00
Ville Skyttä
a6ba5ec1c8 upgrade-mypy (#14904)
* Upgrade mypy to 0.600

* Upgrade mypy to 0.610

* Typing improvements

* remove unneeded or

* remove merge artifact

* Update loader.py
2018-07-13 13:49:24 +02:00
Andrey
c2fe0d0120 Make typing checks more strict (#14429)
## Description:

Make typing checks more strict: add `--strict-optional` flag that forbids implicit None return type. This flag will become default in the next version of mypy (0.600)

Add `homeassistant/util/` to checked dirs.

## Checklist:
  - [x] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
2018-07-13 13:24:51 +03:00
Paulus Schoutsen
b6ca03ce47 Reorg auth (#15443) 2018-07-13 11:43:08 +02:00
Andrey
23f1b49e55 Add python 3.8-dev to travis and tox (#15347)
* Add Python 3.8-dev tox tests.

* Allow failures on 3.8-dev

* Allow failures on 3.8-dev take2

* Only run on pushes to dev
2018-07-13 11:37:03 +02:00
Jason Hu
6e3ec97acf Include request.path in legacy api password warning message (#15438) 2018-07-13 09:19:13 +02:00
Mattias Welponer
4a6afc5614 Add HomematicIP alarm control panel (#15342)
* Add HomematicIP security zone

* Update access point tests

* Fix state if not armed and coments

* Add comment for the empty state_attributes

* Fix comment

* Fix spelling
2018-07-13 03:57:41 +02:00
Anders Melchiorsen
b557c17f76 Make LimitlessLED color/temperature attributes mutually exclusive (#15298) 2018-07-12 17:17:00 +02:00
Matthew Garrett
c587536547 Ignore some HomeKit devices (#15316)
There are some devices that speak HomeKit that we shouldn't expose. Some
bridges (such as the Hue) provide reduced functionality over HomeKit and
have a functional native API, so should be ignored. We also shouldn't
offer to configure the built-in Home Assistant HomeKit bridge.
2018-07-12 11:52:37 +02:00
Daniel Perna
4c6394b307 Fix HomeMatic variables (#15417)
* Update __init__.py

* Update requirements_all.txt
2018-07-12 11:49:39 +02:00
Paulus Schoutsen
534233388c Merge branch 'master' into dev 2018-07-12 11:47:10 +02:00
huangyupeng
43b31e88ba Add Tuya component and switch support (#15399)
* support for tuya platform

* support tuya platform

* lint fix

* change dependency

* add tuya platform support

* remove tuya platform except switch. fix code as required

* fix the code as review required

* fix as required

* fix a mistake
2018-07-12 10:19:35 +02:00
Marcelo Moreira de Mello
6197fe0121 Change Ring binary_sensor frequency polling to avoid rate limit exceeded errors (#15414) 2018-07-11 09:27:22 +02:00
Paulus Schoutsen
1f6331c69d Fix credentials lookup (#15409) 2018-07-10 20:33:03 +02:00
Philipp Schmitt
fd568d77c7 Fix liveboxplaytv empty channel list (#15404) 2018-07-10 15:51:37 +02:00
Anders Melchiorsen
f32098abe4 Fix confused brightness of xiaomi_aqara gateway light (#15314) 2018-07-10 13:26:42 +02:00
Joakim Sørensen
b65d7daed8 removed unused return (#15402) 2018-07-10 13:19:32 +02:00
Giuseppe
9ea0c409e6 Improve NetAtmo sensors update logic (#14866)
* Added a "last update" sensor that could be used by automations + cosmetic changes

* Improved the update logic of sensor data

The platform is now continuously adjusting the refresh interval
in order to synchronize with the expected next update from the
NetAtmo cloud. This significantly improves reaction time of
automations while keeping the refresh time to the recommended
value (10 minutes).

* Linting

* Incorporated the advanced Throttle class to support adaptive
throttling, as opposed to integrating it in the core framework.

Following code review, it was suggested to implement the
specialised Throttle class in this platform instead of making a
change in the general util package. Except that the required change
(about 4 LoC) is part of the only relevant piece of code of that
class, therefore this commit includes a full copy of the Throttle
class from homeassistant.util, plus the extra feature to support
adaptive throttling.

* Cosmetic changes on the introduced "last updated" sensor

* Alternate implementation for the adaptive throttling

Ensure the updates from the cloud are throttled and adapted to the
last update time provided by NetAtmo, without using the Throttle
decorator. Similar logic and similar usage of a lock to protect
the execution of the remote update.

* Linting
2018-07-10 12:30:48 +02:00
Paulus Schoutsen
2ee62b10bc Bump frontend to 20180710.0 2018-07-10 12:01:52 +02:00
Paulus Schoutsen
dbdd0a1f56 Expire auth code after 10 minutes (#15381) 2018-07-10 11:20:22 +02:00
Robin
df8c59406b Add Facebox teach service (#14998)
* Adds service

* Address pylint

* Update facebox.py

* patch tests

* Update facebox.py

* Update test_facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py

* Update test_facebox.py

* Update test_facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py

* Adds total_matched_faces

* Update test_facebox.py

* Update facebox.py

* Update test_facebox.py

* Update test_facebox.py

* Remove fixtures

Removes the fixtures which were causing `setup` to fail, replace with `@patch`

* Fix teach service test and lint issues
2018-07-10 03:11:39 +02:00
Joakim Sørensen
c5a2ffbcb9 Add Cloudflare DNS component. (#15388)
* Add Cloudflare DNS component

* Removed man

* Update .coveragerc

* Update cloudflare.py

* Update cloudflare.py

* Changed records to be required

* Fix typos, update order and other minor changes
2018-07-09 23:11:54 +02:00
hanzoh
e62bb299ff Add new voices to Amazon Polly (#15320) 2018-07-09 23:01:17 +02:00
Daniel Perna
6ee8d9bd65 Update ha-philipsjs to 0.0.5 (#15378)
* Update requirements_all.txt

* Update philips_js.py
2018-07-09 21:35:06 +02:00
Ville Skyttä
14a34f8c4b Remove some unneeded pylint import-error disables (#15386) 2018-07-09 21:34:27 +02:00
Ville Skyttä
3b93fa80be Add httplib2 to h.c.google requirements (#15385) 2018-07-09 21:33:58 +02:00
Paulus Schoutsen
57977bcef3 Bump frontend to 20180709.0 2018-07-09 18:26:51 +02:00
Paulus Schoutsen
0d4841cbea Use IndieAuth for client ID (#15369)
* Use IndieAuth for client ID

* Lint

* Lint & Fix tests

* Allow local IP addresses

* Update comment
2018-07-09 18:24:46 +02:00
Fabian Affolter
f7d7d825b0 Efergy (#15380)
* Update format

* Use string formatting
2018-07-09 14:39:28 +02:00
iliketoprogram14
1d1408b98d Fixed issue 15340. alexa/smart_home module can now skip properties that aren't supported in the current state, eg lowerSetpoint in Heat mode or targetSetpoint in Eco mode for Nest devices. (#15352) 2018-07-09 11:44:50 +02:00
starkillerOG
b9eb0081cd Add sound mode support (#14910)
* Add sound mode support

* continuation line indent

* indentation

* indentation

* Remove option to configure sound_mode_dict

* Sound mode support

- removed the sound_mode_raw propertie because it was not used, (still available through self._sound_mode_raw (as device attribute for automations and diagnostics)

* Detect sound mode support from device

Removed the config option to indicate if sound mode is supported.
Added detection if sound mode is supported from the receiver itself.
Pushed denonavr library to V.0.7.4

* Pushed denonavr to v.0.7.4
2018-07-09 11:39:41 +02:00
Paul Klingelhuber
287b1bce15 Add support for multi-channel enocean switches (D2-01-12 profile) (#14548) 2018-07-09 11:05:25 +02:00
Diogo Gomes
ec3d2e97e8 fix camera.push API overwrite (#15334)
* fix camera.push API overwrite

* dont search in the component dictionary, but in hour own

* remove error message

* hound
2018-07-09 11:04:51 +02:00
Mattias Welponer
1ff329d9d6 Add HomematicIP Cloud light power consumption and energie attributes (#15343)
* Add power consumption and energie attributes

* Fix lint

* Change attribute name and include kwh
2018-07-09 05:37:59 +02:00
sjabby
703d71c064 Frontend: Allow overriding default url when added to home screen (#15368)
Frontend: Allow overriding default url when added to home screen
2018-07-08 22:45:01 +02:00
Paulus Schoutsen
a2a4c633f3 Merge branch 'rc' 2018-07-08 17:35:59 +02:00
Paulus Schoutsen
e6dd4f6e13 Bumped version to 0.73.1 2018-07-08 17:35:30 +02:00
Paulus Schoutsen
b327ea2023 Bump frontend to 20180708.0 2018-07-08 17:31:03 +02:00
Paulus Schoutsen
b333dba875 Bump frontend to 20180708.0 2018-07-08 17:25:15 +02:00
Andrey
02238b6412 Add python 3.7 to travis and tox (#14523)
* Add python 3.7 to travis and tox

* Use pyyaml from github

* Don't version constraints

* Fix version tag

* Change to new pyyaml release

* Python 3.7 requires xenial

* Fix namespace detection

* Use correct RegEx type

* Update pexpect to 4.6

* Use correct validation for dictionaries

* Disable Py37 incompatible packages

* Upgrade all pexpect to 4.6

* Add explicit None as default param
2018-07-07 10:48:02 -04:00
Tommy Jonsson
bd62248841 Add original message as dialogflow_query parameter (#15304)
So you can access for example sessionId as {{ dialogflow_query.sessionId }} in intent templates.
2018-07-07 11:10:43 +02:00
Ville Skyttä
dabbd7bd63 Upgrade pytest to 3.6.3 (#15332) 2018-07-07 11:06:49 +02:00
Fabian Affolter
b5c7afcf75 Upgrade keyring to 13.2.0 (#15322) 2018-07-07 11:06:00 +02:00
Fabian Affolter
f8f8da959a Upgrade youtube_dl to 2018.07.04 (#15323) 2018-07-07 11:05:44 +02:00
Mattias Welponer
9970965718 Add HomematicIP Cloud Config Flow and Entries loading (#14861)
* Add HomematicIP Cloud to config flow

* Inititial trial for config_flow

* Integrations text files

* Load and write config_flow and init homematicip_cloud

* Split into dedicated files

* Ceanup of text messages

* Working config_flow

* Move imports inside a function

* Enable laoding even no accesspoints are defined

* Revert unnecassary changes in CONFIG_SCHEMA

* Better error handling

* fix flask8

* Migration to async for token generation

* A few fixes

* Simplify config_flow

* Bump version to 9.6 with renamed package

* Requirements file

* First fixes after review

* Implement async_step_import

* Cleanup for Config Flow

* First tests for homematicip_cloud setup

* Remove config_flow tests

* Really remove all things

* Fix comment

* Update picture

* Add support for async_setup_entry to switch and climate platform

* Update path of the config_flow picture

* Refactoring for better tesability

* Further tests implemented

* Move 3th party lib inside function

* Fix lint

* Update requirments_test_all.txt file

* UPdate of requirments_test_all.txt did not work

* Furder cleanup in websocket connection

* Remove a test for the hap

* Revert "Remove a test for the hap"

This reverts commit 968d58cba1.

* First tests implemented for config_flow

* Fix lint

* Rework of client registration process

* Implemented tests for config_flow 100% coverage

* Cleanup

* Cleanup comments and code

* Try to fix import problem

* Add homematicip to the test env requirements
2018-07-06 17:05:34 -04:00
Paulus Schoutsen
a1d8b0e9b3 Merge pull request #15330 from home-assistant/rc
0.73
2018-07-06 16:41:09 -04:00
Paulus Schoutsen
1e7cfc04af Bumped version to 0.73.0 2018-07-06 22:31:09 +02:00
Luke Fritz
0f1bcfd63b Add additional sensors for Arlo Baby camera (#15074)
* Add additional sensors for Arlo Baby camera

* Fix linter errors

* Fix linter error

* Add tests for Arlo sensors

* Fix linter errors

* Bump pyarlo dependency to 0.1.9

* Remove unnecessary AttributeError except

* Fix module reference error in py35

* Fix test

* Address PR review concerns

* Convert to standalone pytest methods

* Fix linter errors

* Fix linter errors

* Fix linter errors

* Fix test

* Remove redundant check, fix async test

* Fix linter error

* Added check for total_cameras sensor, added additional attribute tests

* Add missing docstring
2018-07-06 10:26:03 +02:00
Aaron Bach
f65c3940ae Fix exception when parts of Pollen.com can't be reached (#15296)
Fix exception when parts of Pollen.com can't be reached
2018-07-04 17:30:15 -06:00
Paulus Schoutsen
46de89e1a3 Bumped version to 0.73.0b6 2018-07-04 12:11:52 -04:00
Paulus Schoutsen
852526e10a Bump frontend to 20180704.0 2018-07-04 12:11:39 -04:00
Paulus Schoutsen
91d6d0df84 Bump frontend to 20180704.0 2018-07-04 12:11:24 -04:00
Paulus Schoutsen
cb129bd207 Add system generated users (#15291)
* Add system generated users

* Fix typing
2018-07-04 11:50:08 -04:00
Marcelo Moreira de Mello
a6e9dc81aa Added support to HTTPS URLs on SynologyDSM (#15270)
* Added support to HTTPS URLs on SynologyDSM

* Bumped python-synology to 0.1.1

* Makes lint happy

* Added attribution to Synology and fixed 3rd library version

* Fixed requirements_all.txt

* Makes SynologyDSM defaults to 5001 using SSL
2018-07-04 07:46:01 +02:00
Diogo Gomes
5f7ac09a74 Added Push Camera (#15151)
* Added push camera

* add camera.push

* Address comments and add tests

* auff auff

* trip time made no sense

* travis lint

* Mock dependency

* hound

* long line

* long line

* better mocking

* remove blank image

* no more need to mock dependency

* remove import

* cleanup

* no longer needed

* unused constant

* address @pvizeli review

* add force_update

* Revert "add force_update"

This reverts commit e203785ea8.

* rename parameter
2018-07-04 07:44:47 +02:00
cdce8p
42775142f8 Fix yeelight light brightness integer (#15290) 2018-07-03 20:50:13 -06:00
Aaron Bach
2525fc52b3 Update Tile platform to be async (#15073)
* Updated

* Updated requirements

* Added expired session handling

* Changes

* Member-requested changes

* Bump to 2.0.2

* Bumping requirements

* Better exception handling and tidying

* Move asyncio stuff to HASS built-ins

* Revising re-initi

* Hound

* Hound
2018-07-03 20:41:54 -06:00
Paulus Schoutsen
07dde62e70 Bumped version to 0.73.0b5 2018-07-03 14:58:31 -04:00
Paulus Schoutsen
cb458b7745 Bump frontend to 20180703.1 2018-07-03 14:58:27 -04:00
Paulus Schoutsen
b2df199674 Bump frontend to 20180703.1 2018-07-03 14:51:57 -04:00
Paulus Schoutsen
857c58c4b7 Disable the calendar panel (#15282) 2018-07-03 13:20:42 -04:00
Paulus Schoutsen
b82371f44b Bumped version to 0.73.0b4 2018-07-03 11:11:14 -04:00
Paulus Schoutsen
1c525968d1 Bump frontend to 20180703.0 2018-07-03 11:10:07 -04:00
Paulus Schoutsen
5ec61e4649 Bump frontend to 20180703.0 2018-07-03 11:03:23 -04:00
Andrey
184d0a99c0 Switch to own packaged version of suds-passworddigest (#15261) 2018-07-03 12:43:24 +02:00
Fabian Affolter
232f56de62 Add support for new API (fixes #14911) (#15279) 2018-07-03 12:30:56 +02:00
Diogo Gomes
66e33c7979 Merge pull request #13390 from nielstron/filter-band-pass
Adds a range filter to the Filter Sensor
2018-07-03 11:01:34 +01:00
nielstron
6420ab5535 Remove default none from filter sensor 2018-07-03 11:06:52 +02:00
Fabian Affolter
ed3fe1cc6f Add isort configuration (#15278) 2018-07-03 09:47:14 +02:00
pepeEL
cd1cfd7e8e New device to support option MY in somfy (#15272)
New device to support option MY in somfy
2018-07-03 08:39:42 +02:00
Paul Stenius
31e23ebae2 expose climate current temperature in prometeus metrics (#15232)
* expose climate current temperature in prometeus metrics

* import ATTR_CURRENT_TEMPERATURE from climate instead of const

* remove duplicated ATTR_CURRENT_TEMPERATURE from const

* fix ATTR_CURRENT_TEMPERATURE import
2018-07-02 18:03:46 -04:00
nielstron
fb65276daf Remove math.inf as bounds 2018-07-03 00:00:46 +02:00
Robert Svensson
bedd2d7e41 deCONZ - new sensor attribute 'on' and new sensor GenericFlag (#15247)
* New sensor attribute 'on'
* New sensor GenericFlag
2018-07-02 23:14:38 +02:00
Fabian Affolter
120111ceee Upgrade keyring to 13.1.0 (#15268) 2018-07-02 23:03:56 +02:00
shker
e6390b8e41 Fix python-miio 0.4 compatibility of the xiaomi miio device tracker (#15244) 2018-07-02 22:33:40 +02:00
Paulus Schoutsen
d7fd9247a9 Bumped version to 0.73.0b3 2018-07-02 14:44:15 -04:00
Paulus Schoutsen
0dc155c4d3 Bump frontend to 20180702.1 2018-07-02 14:44:11 -04:00
Paulus Schoutsen
0feb4c5439 Bump frontend to 20180702.1 2018-07-02 14:43:31 -04:00
Diogo Gomes
f3588a8782 Update image_processing async (#15082)
* scan() -> async_job

* added async_scan
2018-07-02 16:57:52 +02:00
William Scanlon
2145ac5e46 Added support for Duke Energy smart meters (#15165)
* Added support for Duke Energy smart meters

* Fixed hound

* Added function docstring

* Moved strings to constants, implemented unique_id, and cleaned up setup.

* Added doc string.

* Fixed review issues.

* Updated pydukenergy to 0.0.6 and set update interval to 2 hours

* Updated requirements_all
2018-07-02 16:55:34 +02:00
Paulus Schoutsen
c39e6b9618 Bumped version to 0.73.0b2 2018-07-02 08:57:26 -04:00
Paulus Schoutsen
855cbc0aed Update frontend to 20180702.0 2018-07-02 08:56:45 -04:00
Paulus Schoutsen
00c366d7ea Update frontend to 20180702.0 2018-07-02 08:56:37 -04:00
Paulus Schoutsen
3c3a53a137 Update translations 2018-07-02 08:53:47 -04:00
Paulus Schoutsen
dd59054003 Update translations 2018-07-02 08:53:33 -04:00
David Worsham
36f566a529 Fix Roomba exception (#15262)
* Fix Roomba exception

* Switch to single quotes
2018-07-02 14:12:25 +02:00
Ville Skyttä
4d93a9fd38 Pass tox posargs to pylint (#15226) 2018-07-02 12:47:20 +03:00
Klaudiusz Staniek
d3df96a8de Added setting cover tilt position in scene (#15255)
## Description:
This feature adds possibly of setting tilt_position in scene for covers.

**Related issue (if applicable):** fixes #<home-assistant issue number goes here>

**Pull request in [home-assistant.github.io](https://github.com/home-assistant/home-assistant.github.io) with documentation (if applicable):** home-assistant/home-assistant.github.io#<home-assistant.github.io PR number goes here>

## Example entry for `configuration.yaml` (if applicable):
```yaml
scene:
  - name: Close Cover Tilt
    entities:
      cover.c_office_north:
        tilt_position: 0

  - name: Open Cover Tilt
    entities:
      cover.c_office_north:
        tilt_position: 100
```

## Checklist:
  - [x] The code change is tested and works locally.
  - [x] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**

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)

If the code communicates with devices, web services, or third-party tools:
  - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]).
  - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]).
  - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.
  - [ ] New files were added to `.coveragerc`.

If the code does not interact with devices:
  - [ ] Tests have been added to verify that the new code works.

[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L14
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L54
2018-07-02 12:44:36 +03:00
Andrey
6c77702dcc Switch to own packaged version of pylgnetcast (#15042)
## Description:

Switch to own packaged version of pylgnetcast

Request to make a pypi package didn't get any response: https://github.com/wokar/pylgnetcast/issues/1

**Related issue (if applicable):** #7069
2018-07-02 10:57:26 +03:00
Steven Conaway
86165750ff Fix typo in Docker files (#15256) 2018-07-02 07:02:09 +02:00
Jason Hu
63b28aa39d By default to use access_token if hass.auth.active (#15212)
* Force to use access_token if hass.auth.active

* Not allow Basic auth with api_password if hass.auth.active

* Block websocket api_password auth when hass.auth.active

* Add legacy_api_password auth provider

* lint

* lint
2018-07-01 21:53:38 -04:00
Paulus Schoutsen
279fd39677 Bumped version to 0.73.0b1 2018-07-01 13:40:55 -04:00
cdce8p
c978281d1e Revert some changes to setup.py (#15248) 2018-07-01 13:40:48 -04:00
Jason Hu
311a44007c Fix an issue when user's nest developer account don't have permission (#15237) 2018-07-01 13:40:48 -04:00
Anders Melchiorsen
47401739ea Make LIFX color/temperature attributes mutually exclusive (#15234) 2018-07-01 13:40:48 -04:00
Jason Hu
11ba7cc8ce Only create front-end client_id once (#15214)
* Only create frontend client_id once

* Check user and client_id before create refresh token

* Lint

* Follow code review comment

* Minor clenaup

* Update doc string
2018-07-01 13:40:47 -04:00
Andy Castille
c3ad30ec87 Rachio webhooks (#15111)
* Make fewer requests to the Rachio API

* BREAKING: Rewrite Rachio component
2018-07-01 13:40:47 -04:00
Jason Hu
a64a66dd62 Only create front-end client_id once (#15214)
* Only create frontend client_id once

* Check user and client_id before create refresh token

* Lint

* Follow code review comment

* Minor clenaup

* Update doc string
2018-07-01 13:36:50 -04:00
Anders Melchiorsen
dffe36761d Make LIFX color/temperature attributes mutually exclusive (#15234) 2018-07-01 13:06:30 -04:00
Jason Hu
0a186650bf Fix an issue when user's nest developer account don't have permission (#15237) 2018-07-01 13:04:12 -04:00
Fabian Affolter
6c77c9d372 Upgrade WazeRouteCalculator to 0.6 (#15251) 2018-07-01 13:02:02 -04:00
Fabian Affolter
4a4b9180d8 Upgrade sqlalchemy to 1.2.9 (#15250) 2018-07-01 13:01:48 -04:00
Paulus Schoutsen
5d6db9a915 Bump frontend to 20180701.0 2018-07-01 13:00:48 -04:00
Paulus Schoutsen
235282e335 Bump frontend to 20180701.0 2018-07-01 13:00:34 -04:00
Ville Skyttä
6f582dcf24 Lint cleanups (#15243)
* Remove some unused imports

* Fix a flake8 E271
2018-07-01 11:57:01 -04:00
Andy Castille
9db8759317 Rachio webhooks (#15111)
* Make fewer requests to the Rachio API

* BREAKING: Rewrite Rachio component
2018-07-01 11:54:51 -04:00
David Thulke
136cc1d44d allow extra slot values in intents (#15246) 2018-07-01 11:51:40 -04:00
cdce8p
4c258ce08b Revert some changes to setup.py (#15248) 2018-07-01 11:48:54 -04:00
Leonardo Brondani Schenkel
3c04b0756f deconz: proper fix light.turn_off with transition (#15227)
Previous commit d4f7dfa successfully fixed the bug in which lights
would not turn off if a transition was specified, however if 'bri' is not
present in the payload of the PUT request set to deCONZ, then any
'transitiontime' ends up being ignored. This commit addresses the
unintended side effect by reintroducing 'bri', resulting in the following
payload:

{ "on": false, "bri": 0, "transitiontime": ... }
2018-07-01 12:32:48 +02:00
Yevgeniy
c0229ebb77 Add precipitations to Openweathermap daily forecast mode (#15240)
* Add precipitations to daily forecast mode

* Remove line breaks
2018-07-01 11:54:24 +02:00
Ville Skyttä
cfe7c0aa01 Upgrade pytest to 3.6.2 (#15241) 2018-07-01 10:40:23 +02:00
Jason Hu
f874efb224 By default to use access_token if hass.auth.active (#15212)
* Force to use access_token if hass.auth.active

* Not allow Basic auth with api_password if hass.auth.active

* Block websocket api_password auth when hass.auth.active

* Add legacy_api_password auth provider

* lint

* lint
2018-06-30 22:31:36 -04:00
cdce8p
3da4642194 Use async syntax for cover platforms (#15230) 2018-06-30 18:10:59 +02:00
Fabian Affolter
0aad056ca7 Fix typos (#15233) 2018-06-30 17:12:00 +02:00
Carl Chan
c5ceb40598 Add additional parameters to NUT UPS sensor (#15066)
* Update nut.py

Added input.frequency and a number of output parameters.

* Update nut.py

Fixed formatting issues
Added "devices" fields

* Separated "device.description" line to two lines.

* Update nut.py

Removed device.* sensors
2018-06-30 14:57:48 +02:00
pepeEL
27a37e2013 Add new RTS device (#15116)
* Add new RTS device

Add new RTS Somfy device as cover-ExteriorVenetianBlindRTSComponent

* add next device

add next device
2018-06-30 14:56:43 +02:00
Leonardo Brondani Schenkel
10d1e81f10 deconz: fix light.turn_off with transition (#15222)
When light.turn_off is invoked with a transition, the following payload was
sent to deCONZ via PUT to /light/N/state:

{ "bri": 0, "transitiontime": transition }

However, on recent versions of deCONZ (latest is 2.05.31 at the time of
writing) this does not turn off the light, just sets it to minimum brightness
level (brightness is clamped to minimum level the light supports without
turning it off).

This commit makes the code send this payload instead:

{ "on": false, "transitiontime": transition }

This works as intended and the light does transition to the 'off' state.
This change was tested with Philips Hue colored lights, IKEA colored lights
and IKEA white spectrum lights: they were all able to be turned off
successfully with the new payload, and none of them could be turned off with
the old payload.
2018-06-30 00:59:10 +02:00
Paulus Schoutsen
56bbadb501 Version bump to 0.73.0b0 2018-06-29 18:06:32 -04:00
Paulus Schoutsen
fa79aead9a Bumped version to 0.74.0b0 2018-06-29 18:04:00 -04:00
Paulus Schoutsen
24fec3e826 Merge remote-tracking branch 'origin/master' into dev 2018-06-29 18:01:40 -04:00
Daniel Shokouhi
2524dca7bf Use cached states for neato when possible (#15218) 2018-06-29 17:27:18 -04:00
Sebastian Muszynski
56f17b8651 Fix 'AirQualityMonitorStatus' object has no attribute ‘time_state’ (#15216) 2018-06-29 17:26:48 -04:00
Vignesh Venkat
49623d2dad Update python-wink to 1.9.1 (#15215)
Fixes a bug for when GE Z-Wave fan speeds are adjusted using the
wink app.
2018-06-29 17:26:06 -04:00
Anders Melchiorsen
66479dc2e5 Update eternalegypt (#15180)
* Update eternalegypt to 0.0.2

* Share websession

* Renames
2018-06-29 17:25:49 -04:00
Paulus Schoutsen
bbbec5a056 Bump frontend to 20180629.1 2018-06-29 17:21:54 -04:00
Hmmbob
94b55efef3 Stop supporting deprecated TLS ciphers (#15217)
* Stop supporting deprecated TLS ciphers

* Lint
2018-06-29 17:18:44 -04:00
Colin O'Dell
fd38caa287 X-Forwarded-For improvements and bug fixes (#15204)
* Use new trusted_proxies setting for X-Forwarded-For whitelist

* Only use the last IP in the header

Per Wikipedia (https://en.wikipedia.org/wiki/X-Forwarded-For#Format):

 > The last IP address is always the IP address that connects to the last proxy,
 > which means it is the most reliable source of information.

* Add two additional tests

* Ignore nonsense header values instead of failing
2018-06-29 16:27:06 -04:00
Sriram Vaidyanathan
c61a652c90 Fixed Indentation error (#15210)
* Fixed Indentation error

* Update xiaomi.py
2018-06-29 16:23:14 +02:00
cdce8p
e3e014bccc Fix zwave climate operation mode mappings (#15162) 2018-06-29 16:09:46 +02:00
Paulus Schoutsen
26590e244c Migrate home assistant auth provider to use storage helper (#15200) 2018-06-29 00:02:45 -04:00
Paulus Schoutsen
39971ee919 Make sure we check access token expiration (#15207)
* Make sure we check access token expiration

* Use correct access token websocket
2018-06-29 00:02:33 -04:00
Paulus Schoutsen
2205090795 Storage auth (#15192)
* Support parallel loading

* Add storage mock

* Store auth

* Fix tests
2018-06-28 22:14:26 -04:00
Alex Barcelo
a277470363 Adding 'namespace' for prometheus metrics (#13738)
* Updating prometheus client version

* Using `entity_filter` as filter mechanism

* New optional `namespace` configuration
2018-06-28 16:49:33 +02:00
Colin O'Dell
19f2bbf52f Only use the X-Forwarded-For header if connection is from a trusted network (#15182)
See https://github.com/home-assistant/home-assistant/issues/14345#issuecomment-400854569
2018-06-28 09:16:11 -04:00
Pascal Vizeli
dbb786c548 DarkSky weather / Fix states (#15174)
* DarkSky weather / Fix states

* fix lint

* fix tests
2018-06-28 12:23:32 +02:00
Daniel Shokouhi
4fbe3bb070 Finalize BotVac D7 Support And Further Reduce Cloud Calls (#15161)
* Finalize BotVac D7 Support And Further Reduce Cloud Calls

* Lint

* Lint Again

* Implement requested changes

* Hound

* Lint
2018-06-27 22:55:27 +02:00
MizterB
9066ac44fe Philips Hue Scene Activation: Simplified scene lookup logic, improved error handling (#15175)
* Simplified scene lookup logic, improved error handling

* Lint
2018-06-27 15:22:29 -04:00
Paulus Schoutsen
742144f401 Warn when using custom components (#15172)
* Warn when using custom components

* Update text
2018-06-27 15:21:32 -04:00
Paulus Schoutsen
c0b6a857f7 Version bump to 20180627.0 2018-06-27 14:20:24 -04:00
Tom Harris
d6dee62c92 Add Mini remote support to insteon_plm (#15152)
* Add mini-remote

* Bump insteonplm version to 0.11.3 to support mini-remotes
2018-06-27 12:19:56 +02:00
Fabian Affolter
41017f10a3 Upgrade sendgrid to 5.4.1 (#15166) 2018-06-27 12:12:02 +02:00
Fabian Affolter
ba50a5c329 Upgrade keyring to 13.0.0 (#15167) 2018-06-27 12:11:41 +02:00
Fabian Affolter
4208bb457d Upgrade youtube_dl to 2018.06.25 (#15168) 2018-06-27 12:11:26 +02:00
Matt Snyder
15af6b1ad9 Address inconsistent behavior on flux_led component (#14713)
* Address inconsistent behavior between different controllers.
Correct issue with comparison that was preventing white value slider from being shown.

* Add white mode for Flux LED

* Call _bulb.turnOn() after bulb properties have been set to prevent immediate on action

* Only use existing brightness if rgb is None to prevent unexpected recalculation of passed rgb values.

* Remove blank line

* Undo change so current brightness is used in all cases.
2018-06-26 21:23:57 +02:00
Robert Kiss
3921dc77a6 Add SSL peer certificate support to HTTP server (#15043)
* adding SSL peer certificate support to HTTP server

* remove unnecessary exception block
2018-06-26 11:44:08 -04:00
Matt LeBrun
0094fd5c34 Add channel changing support to SamsungTV component (#14451)
Add channel changing support to SamsungTV component
2018-06-26 16:22:10 +02:00
Paulus Schoutsen
d58e401812 Merge pull request #15149 from home-assistant/rc
0.72.1
2018-06-25 17:25:44 -04:00
Paulus Schoutsen
c79c94550f Return None to indicate no config found (#15147)
* Return None to indicate no config found

* Fix tests
2018-06-25 17:21:38 -04:00
Paulus Schoutsen
9b950f5192 Bumped version to 0.72.1 2018-06-25 16:59:14 -04:00
Jason Hu
2520fddbdf Bump python-nest to 4.0.3 (#15098)
Resolve network reconnect issue
2018-06-25 16:59:00 -04:00
Paulus Schoutsen
3f21966ec9 Fix cast config (#15143) 2018-06-25 16:58:27 -04:00
Jason Hu
69502163bd Skip nest security state sensor if no Nest Cam exists (#15112) 2018-06-25 16:57:27 -04:00
Aaron Bach
893e0f8db6 Fix socket bug with Yi in 0.72 (#15109)
* Fixes BrokenPipeError exceptions with Yi (#15108)

* Make sure to close the socket
2018-06-25 16:57:26 -04:00
Jason Hu
1c8b52f630 Prevent Nest component setup crash due insufficient permission. (#14966)
* Prevent Nest component setup crash due insufficient permission.

* Trigger CI

* Better error handle and address code review comments

* Lint

* Tiny wording adjust

* Notify user if async_setup_entry failed

* Return False if exception occurred in NestDevice.initialize
2018-06-25 16:57:26 -04:00
Jason Hu
6e4fb7a937 Prevent Nest component setup crash due insufficient permission. (#14966)
* Prevent Nest component setup crash due insufficient permission.

* Trigger CI

* Better error handle and address code review comments

* Lint

* Tiny wording adjust

* Notify user if async_setup_entry failed

* Return False if exception occurred in NestDevice.initialize
2018-06-25 16:06:00 -04:00
Paulus Schoutsen
ab1939f56f Bump frontend to 20180625.0 2018-06-25 16:04:30 -04:00
Paulus Schoutsen
15507df407 Bump frontend to 20180625.0 2018-06-25 16:04:17 -04:00
Paulus Schoutsen
46ea28a4f8 Fix cast config (#15143) 2018-06-25 15:59:05 -04:00
Sriram Vaidyanathan
c8458fd7c5 Update xiaomi.py (#15136)
* Update xiaomi.py

Minor logic fix for Xiaofang cameras.

* Removed whitespace

* Removed whitespace
2018-06-25 13:14:36 -04:00
Jason Hu
e681a7929c Skip nest security state sensor if no Nest Cam exists (#15112) 2018-06-25 13:13:41 -04:00
Martin Hjelmare
b2d37ccef6 Fix mysensors climate supported features (#15110) 2018-06-25 13:06:12 -04:00
Luc Touraille
9dd2c36de4 Update aiofreepybox to fix HTTPS connection issues (#15104)
The previous version of aiofreepybox was not working with custom
domain names, which uses a Let's Encrypt certificates. Also, it
was not working with the default domain name when connecting to
Freebox v6. This should be fixed in aiofreepybox 0.0.4.

See https://github.com/stilllman/freepybox/pull/1,
https://github.com/stilllman/freepybox/pull/3 and
https://github.com/stilllman/freepybox/issues/2 for more info.
2018-06-25 13:05:33 -04:00
Ville Skyttä
b92350fb55 Lint cleanup (#15103)
* Remove unneeded inline pylint disables

* Remove unneeded noqa's

* Use symbol names instead of message ids in inline pylint disables
2018-06-25 13:05:07 -04:00
Jason Hu
6c0fc65eaf Bump python-nest to 4.0.3 (#15098)
Resolve network reconnect issue
2018-06-25 13:04:32 -04:00
Pascal Vizeli
42ba2a68ce Revert "Add language to dark sky weather component" (#15142)
* Revert "Fix #14919. Should throw exception when camera stream closed by frontend (#15028)"

This reverts commit 508d0459a7.

* Revert "Fix pylintrc section order and option placements (#15120)"

This reverts commit dbae410cf4.

* Revert "Add storage helper and migrate config entries (#15045)"

This reverts commit ae51dc08bf.

* Revert "Add language to dark sky weather component (#15130)"

This reverts commit 672a3c7178.
2018-06-25 19:04:07 +02:00
Jason Hu
508d0459a7 Fix #14919. Should throw exception when camera stream closed by frontend (#15028)
* Fix #14919. Should throw exception when camera stream closed by frontend

* Re-trigger CI

* pythonic re-raise
2018-06-25 13:03:39 -04:00
Ville Skyttä
dbae410cf4 Fix pylintrc section order and option placements (#15120) 2018-06-25 12:55:03 -04:00
Paulus Schoutsen
ae51dc08bf Add storage helper and migrate config entries (#15045)
* Add storage helper

* Migrate config entries to use the storage helper

* Make sure tests do not do I/O

* Lint

* Add versions to stored data

* Add more instance variables

* Make migrator load config if nothing to migrate

* Address comments
2018-06-25 12:53:49 -04:00
Pascal Vizeli
672a3c7178 Add language to dark sky weather component (#15130)
* Add language to dark sky weather component

* Update darksky.py
2018-06-25 08:35:44 -06:00
cdce8p
f8bc3411ad PyPi: Fix description and setup.cfg (#15107)
* Fix description and extend use of setup.cfg

* Fix lint
2018-06-25 09:57:26 -04:00
Adam Mills
038168c417 Support for Homekit Controller climate devices (#15057)
* Support for Homekit Controller climate devices

* Handle stale state when operating mode off
2018-06-25 09:45:26 -04:00
dreizehnelf
73034c933e Add discovery support to mqtt climate component. (#15085)
* Add discovery support to mqtt climate component.

* - Fix flake8 error (./homeassistant/components/climate/mqtt.py:130:1: D202 No blank lines allowed after function docstring)
- Fix test error (since climate component was expected not to work - changed it to "lock" component, which also does not have MQTT discovery support yet)

* Fix old assert statement to reflect new lock component usage

* Change invalid MQTT discovery component type from 'lock' to 'timer', since contrary to the documentation the lock component is properly supported when using MQTT discovery.

* Make configuration of invalid MQTT config component a single point of entry to prevent missing the assertion later in the code when changing.

* Add new testcases to cover not-yet-covered code paths in https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/climate/mqtt.py
2018-06-25 15:13:19 +02:00
b3nj1
d3ceb9080c MQTT Alarm Control Panel: add retain option for publishing for cases... (#15134)
* MQTT Alarm Control Panel: add retain option for publishing for cases where receiver is asleep

* MQTT Alarm Control Panel: add retain option for publishing for cases where receiver is asleep

* MQTT Alarm Control Panel: add retain option for publishing for cases where receiver is asleep
2018-06-25 14:04:16 +02:00
Martin Hjelmare
3893d8a876 Reorganize mysensors (#15123)
* Move mysensors.py to package

* Move mysensors component to package

* Split code into multiple modules.

* Update coveragerc
2018-06-25 13:58:16 +02:00
Tom Harris
05924a2868 Bump insteonplm version to 0.11.2 (#15133)
* Bump insteonplm version to 0.11.2

* Gratuitous commit to force travis again.

* Reverse change made to force Travis CI
2018-06-25 13:46:55 +02:00
Aaron Bach
021d08a9c4 Make sure Yi utilizes existing event loop (#15131) 2018-06-24 19:09:08 -06:00
Robert Svensson
5a71a22fb9 deCONZ small improvements (#15128)
* Make sure that bridge id is available for config entry

* Fix so deconz reports proper color values

* Bump dependency to v39
2018-06-24 15:48:59 -06:00
Aaron Bach
6064932e2e Make Pollen.com platform async (#14963)
* Most of the work in place

* Final touches

* Small style updates

* Owner-requested changes

* Member-requested changes
2018-06-24 11:04:31 -06:00
Marcelo Moreira de Mello
9de7034d0e Added attribute attribution to Digital Ocean component (#15114) 2018-06-24 13:36:27 +02:00
Andrey
96d5684a89 Switch to pypi version of pybotvac (#15115) 2018-06-24 11:06:25 +02:00
Aaron Bach
91962e2681 Fix socket bug with Yi in 0.72 (#15109)
* Fixes BrokenPipeError exceptions with Yi (#15108)

* Make sure to close the socket
2018-06-23 13:22:48 -06:00
Paulus Schoutsen
ee31f89049 Merge branch 'master' into dev 2018-06-22 13:40:53 -04:00
Paulus Schoutsen
370c3f28b8 Merge pull request #15088 from home-assistant/rc
0.72
2018-06-22 13:38:44 -04:00
Paulus Schoutsen
66110a7d57 Bump frontend to 20180622.1 2018-06-22 12:48:01 -04:00
Paulus Schoutsen
c419cbb46f Bump frontend to 20180622.1 2018-06-22 12:46:45 -04:00
Paulus Schoutsen
a02d7989d5 Use older syntax for version bump 2018-06-22 11:07:26 -04:00
Paulus Schoutsen
7325847fa9 Bumped version to 0.72.0 2018-06-22 10:24:45 -04:00
Paulus Schoutsen
124495dd84 Update frontend to 20180622.0 2018-06-22 10:24:38 -04:00
Paulus Schoutsen
0c01f3a0fe Update frontend to 20180622.0 2018-06-22 10:24:04 -04:00
Paulus Schoutsen
0ea2d99910 Bumped version to 0.72.0b9 2018-06-21 17:39:02 -04:00
Paulus Schoutsen
6456f66b47 Frontend bump to 20180621.2 2018-06-21 17:38:57 -04:00
Paulus Schoutsen
94eee6d069 Frontend bump to 20180621.2 2018-06-21 17:38:44 -04:00
Paulus Schoutsen
6e5a2a77ab Bumped version to 0.72.0b8 2018-06-21 17:27:08 -04:00
Paulus Schoutsen
35b609dd8b Allow writing commit with version bump 2018-06-21 17:27:01 -04:00
Paulus Schoutsen
0df99f8762 Bump frontend to 20180621.1 2018-06-21 17:22:08 -04:00
Paulus Schoutsen
6781ecf159 Bump frontend to 20180621.1 2018-06-21 17:15:16 -04:00
Paulus Schoutsen
a4b843eb2d Version bump to 0.72.0b7 2018-06-21 15:02:29 -04:00
Daniel Shokouhi
302717e8a1 Update Neato Library And Reduce Cloud Calls (#15072)
* Update Neato library to 0.0.6 and reduce the amount of calls to the cloud

* Remove file commited in error

* Lint
2018-06-21 15:02:13 -04:00
Bob Clough
617647c5fd Fix MQTT Light with RGB and Brightness (#15053)
* Fix MQTT Light with RGB and Brightness

When an MQTT light is given an RGB and Brightness topic, the RGB
is scaled by the brightness *as well* as the brightness being set

This causes 255,0,0 at 50% brightness to be sent as 127,0,0 at 50%
brightness, which ends up as 63,0,0 after the RGB bulb has applied
its brightness scaling.

Fixes the same issue in mqtt, mqtt-json and mqtt-template.

Related Issue: #13725

* Add comment to mqtt_json as well
2018-06-21 15:00:06 -04:00
Tom Harris
4b5d578c08 X10 (#14741)
* Implement X10

* Add X10 after add_device_callback

* Ref device by id not hex and add x10OnOffSwitch name

* X10 services and add sensor device

* Correctly reference X10_HOUSECODE_SCHEMA

* Log adding of X10 devices

* Add X10 All Units Off, All Lights On and All Lights Off devices

* Correct ref to X10 states vs devices

* Add X10 All Units Off, All Lights On and All Lights Off devices

* Correct X10 config

* Debug x10 device additions

* Config x10 from bool to housecode char

* Pass PLM to X10 device create

* Remove PLM to call to add_x10_device

* Unconfuse x10 config and method names

* Correct spelling of x10_all_lights_off_housecode

* Bump insteonplm to 0.10.0 to support X10
2018-06-21 15:00:06 -04:00
Bob Clough
bfc55137ea Fix MQTT Light with RGB and Brightness (#15053)
* Fix MQTT Light with RGB and Brightness

When an MQTT light is given an RGB and Brightness topic, the RGB
is scaled by the brightness *as well* as the brightness being set

This causes 255,0,0 at 50% brightness to be sent as 127,0,0 at 50%
brightness, which ends up as 63,0,0 after the RGB bulb has applied
its brightness scaling.

Fixes the same issue in mqtt, mqtt-json and mqtt-template.

Related Issue: #13725

* Add comment to mqtt_json as well
2018-06-21 14:59:02 -04:00
Paulus Schoutsen
e98e7e2751 Update frontend to 20180621.0 2018-06-21 14:57:19 -04:00
Paulus Schoutsen
b687de879c Update frontend to 20180621.0 2018-06-21 14:57:08 -04:00
Martin Hjelmare
4048ad36a8 Add script to run monkeytype typing on test suite (#14440)
* The monkeytype script takes an optional argument to specify a test
  module or directory to run. Otherwise the whole test suite will run.
* Add monkeytype sqlite db to gitignore.
2018-06-21 15:06:05 +02:00
hanzoh
8c2f0e3b30 Homematic: Add optional port for resolvenames via JSON (#15029)
* Add optional JSON port
2018-06-21 14:52:02 +02:00
Daniel Shokouhi
6cabbd2592 Update Neato Library And Reduce Cloud Calls (#15072)
* Update Neato library to 0.0.6 and reduce the amount of calls to the cloud

* Remove file commited in error

* Lint
2018-06-20 21:46:15 -04:00
Daniel Perna
8d22754a06 Update pyhomematic to 0.1.44 (#15069)
* Update __init__.py

* Update requirements_all.txt
2018-06-20 21:44:50 -04:00
Tom Harris
be6d1b5e94 X10 (#14741)
* Implement X10

* Add X10 after add_device_callback

* Ref device by id not hex and add x10OnOffSwitch name

* X10 services and add sensor device

* Correctly reference X10_HOUSECODE_SCHEMA

* Log adding of X10 devices

* Add X10 All Units Off, All Lights On and All Lights Off devices

* Correct ref to X10 states vs devices

* Add X10 All Units Off, All Lights On and All Lights Off devices

* Correct X10 config

* Debug x10 device additions

* Config x10 from bool to housecode char

* Pass PLM to X10 device create

* Remove PLM to call to add_x10_device

* Unconfuse x10 config and method names

* Correct spelling of x10_all_lights_off_housecode

* Bump insteonplm to 0.10.0 to support X10
2018-06-20 21:44:05 -04:00
Paulus Schoutsen
c84f1d7d33 Version bump to 0.72.0b6 2018-06-20 15:13:33 -04:00
Paulus Schoutsen
49845d9398 Rename experimental UI to lovelace (#15065)
* Rename experimental UI to lovelace

* Bump frontend to 20180620.0
2018-06-20 15:13:22 -04:00
Paulus Schoutsen
895306f822 Rename experimental UI to lovelace (#15065)
* Rename experimental UI to lovelace

* Bump frontend to 20180620.0
2018-06-20 15:13:08 -04:00
Thibault Cohen
a729742757 Fix tplink max/min kelvin for temperature adjustment (#15020) 2018-06-20 19:29:36 +02:00
William Scanlon
6bc03ee763 Python wink update (#15048)
* Updated python-wink to 1.9.0

* Added support for groups of Wink shades.
2018-06-20 09:41:28 -04:00
Dejan Dakić
75580dfade Upgraded librouteros since it has support for authenticatoin in new RouterOS. (#15056) 2018-06-20 09:15:40 +02:00
Marcelo Moreira de Mello
1f8699d9b4 Upgrade pyarlo to 0.1.8 to support Arlo Baby monitor (#15060) 2018-06-20 07:50:38 +02:00
gstorer
fca5d55b43 Update pywemo to version 0.4.28. (#15052) 2018-06-19 11:16:31 -04:00
Paulus Schoutsen
659616a4eb Version bump to 0.72.0b5 2018-06-19 10:58:57 -04:00
Paulus Schoutsen
3b4f7b4f5d Update frontend to 20180619.0 2018-06-19 10:56:44 -04:00
Paulus Schoutsen
62432ced90 Update frontend to 20180619.0 2018-06-19 10:56:33 -04:00
Raoul Teeuwen
27873b4457 Update condition.py (#15021)
* Update condition.py

Added code that writes to warning-log what entity causes a problem when 'value cannot be processed as a number', making troubleshooting easier.

* Make a one-line warning
2018-06-19 13:26:52 +02:00
Andrey
1e7333eeb6 Switch to own packaged version of pyflic (#15041) 2018-06-19 10:31:21 +02:00
Andrey
7cd620d30f Switch upstream adafruit package (#15038) 2018-06-19 10:30:43 +02:00
Fabian Affolter
7a180ac205 Update loopenergy link to docs (#15050) 2018-06-19 09:56:29 +02:00
Hate-Usernames
153ccda853 Patch save_json (#15046) 2018-06-18 21:34:36 -04:00
Jeff Irion
067e4f6d9a Show running apps as sources for Fire TV (#15033)
* Show running apps as sources for Fire TV

* Fix unnecessary 'else' after 'return' (no-else-return)

* Remove 'pylint: disable=unused-argument'

* cleanup
2018-06-18 18:13:50 +02:00
Fabian Affolter
9d6ce609f9 Upgrade requests to 2.19.1 (#15019) 2018-06-18 18:13:21 +02:00
Paulus Schoutsen
9800b74a6d Version bump to 0.72.0b4 2018-06-18 10:00:47 -04:00
Paulus Schoutsen
ef5b2a2492 Version bump to 0.72.0b3 2018-06-18 10:00:24 -04:00
Pascal Vizeli
60179a1cbb Bugfix empty entity lists (#15035)
* Bugfix empty entity lists

* Add tests

* Update test_entity_platform.py

* Update entity_platform.py
2018-06-18 09:59:58 -04:00
Paulus Schoutsen
e0cea2d18d Make zone entries work without radius (#15032) 2018-06-18 09:59:58 -04:00
Fabian Affolter
e29dfa8609 Upgrade aiohttp to 3.3.2 (#15025) 2018-06-18 09:59:57 -04:00
Martin Hjelmare
ef39bca52e Fix linode I/O in state property (#15010)
* Fix linode I/O in state property

* Move update of all attrs to update
2018-06-18 09:58:58 -04:00
Paulus Schoutsen
5a3ea74a26 Bump frontend to 20180618.0 2018-06-18 09:58:35 -04:00
Paulus Schoutsen
8869617890 Bump frontend to 20180618.0 2018-06-18 09:58:16 -04:00
Pascal Vizeli
62f970e486 Bugfix empty entity lists (#15035)
* Bugfix empty entity lists

* Add tests

* Update test_entity_platform.py

* Update entity_platform.py
2018-06-18 09:22:52 -04:00
Martin Hjelmare
f9a21dbfda Fix linode I/O in state property (#15010)
* Fix linode I/O in state property

* Move update of all attrs to update
2018-06-18 09:21:41 -04:00
Pascal Vizeli
86c6b4d8e3 Fix panel URL authentication for Hass.io (#15024)
* Update http.py

* Update http.py

* fix tests

* Update test_http.py
2018-06-18 07:26:41 +02:00
Jeff Irion
7bfa81c592 Improve volume support for Vizio Smartcast (#14981)
* Improve volume support for Vizio Smartcast

* Vizio: avoid an error when 'self._device.get_current_volume()' returns 'None'

* Improve volume support for Vizio Smartcast

* Vizio: avoid an error when 'self._device.get_current_volume()' returns 'None'

* First line should end with a period
2018-06-17 22:06:53 -06:00
gstorer
d07e40c483 Expose Wemo component availability to home assistant (#14995)
* Expose Wemo component availability to home assistant

* Do not add availability feature to dimmer - it works differently

* Brain fade, deleted completely the wrong thing.

Revert "Do not add availability feature to dimmer - it works differently"

This reverts commit f64e717981.

* (2nd attempt) Do not add availability feature to dimmer - it works differently
2018-06-17 22:05:14 -06:00
Kees Schollaart
0e7e58f172 Update PostNL unit of measure to align with UPS (#15023)
I'm using both the UPS and PostNL package trackers. I'd like to have the unit of measure to be the same, now they appear in two different graphs in the history view. 

If we prefer ```package(s)``` over ```package``` then I'll do a PR for [this line](https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/sensor/ups.py#L81)
2018-06-17 21:57:08 -06:00
Paulus Schoutsen
cbdfc95cc8 Make zone entries work without radius (#15032) 2018-06-17 21:55:35 -06:00
Paulus Schoutsen
1642502a70 Update translations 2018-06-17 23:05:10 -04:00
Paulus Schoutsen
33ebd99068 Update translations 2018-06-17 23:04:52 -04:00
Fabian Affolter
9c17e95fc5 Upgrade aiohttp to 3.3.2 (#15025) 2018-06-17 20:24:11 -04:00
Matt Schmitt
1533bc1e1f Add support for Homekit battery service (#14288) 2018-06-17 20:54:34 +02:00
Pascal Vizeli
da3695dccc Update test_http.py 2018-06-17 20:37:46 +02:00
Pascal Vizeli
40c8f5f70e Fix panel URL authentication for Hass.io (#15024)
* Update http.py

* Update http.py

* fix tests

* Update test_http.py
2018-06-17 20:34:47 +02:00
Fabian Affolter
3ceee66e1b Remove typing (#15018) 2018-06-17 19:13:39 +02:00
Andrey
6b908b6f4e Switch nuimo to a hopefully working pypi version (#15006) 2018-06-17 09:41:49 -04:00
Fabian Affolter
a74b081d44 Upgrade youtube_dl to 2018.06.14 (#15013) 2018-06-17 09:41:06 -04:00
Fabian Affolter
bc8093c73b Upgrade numpy to 1.14.5 (#15015) 2018-06-17 09:39:27 -04:00
Fabian Affolter
ca2712506b Update to hole to 0.3.0 (#15014) 2018-06-17 09:38:56 -04:00
Fabian Affolter
c871e8da5d Upgrade netdisco to 1.5.0 (#15016) 2018-06-17 09:38:00 -04:00
Matt Schmitt
722c27f1e2 HomeKit style clean up (#14793) 2018-06-17 13:37:44 +02:00
Andrey
e3fcf46566 Switch to own packaged version of braviarc (#15009) 2018-06-17 13:07:10 +02:00
Andrey
1117371b31 Switch to own packaged version of anel_pwrctrl (#15011) 2018-06-17 13:05:25 +02:00
Petro31
addca54118 Add entity support to Waze Travel Time (#14934)
Current version only supports latitude and longitude or an address for the origin and destination fields. This update allows those fields to use entity IDs of device_tracker, zone, and sensor.
2018-06-17 07:13:47 +02:00
Paulus Schoutsen
471d6e45eb Version bump to 0.72.0b2 2018-06-16 22:37:13 -04:00
Paulus Schoutsen
7238205adb Frontend bump to 20180617.0 2018-06-16 22:36:51 -04:00
Paulus Schoutsen
3db5d5bbf9 Frontend bump to 20180617.0 2018-06-16 22:35:19 -04:00
Paulus Schoutsen
65970a2248 Version bump to 0.72.0b1 2018-06-16 17:36:35 -04:00
Paulus Schoutsen
5d82f48c02 Add experimental UI backend (#15002)
* Add experimental UI

* Add test

* Lint
2018-06-16 17:36:10 -04:00
Teemu R
8e185bc300 Bump pyhs100 version (#15001)
Fixes #13925
2018-06-16 17:36:10 -04:00
Andrey
a013908115 Switch to own packaged version of spotipy (#14997) 2018-06-16 17:36:09 -04:00
Sebastian Muszynski
bdf6257640 Remove load power attribute for channel USB (#14996)
* Remove load power attribute for channel USB

* Fix format
2018-06-16 17:36:09 -04:00
Paulus Schoutsen
1f50e335fa Bump frontend to 20180616.0 2018-06-16 17:35:52 -04:00
Paulus Schoutsen
1375adfeab Bump frontend to 20180616.0 2018-06-16 17:32:49 -04:00
Paulus Schoutsen
00cbdffa12 Add experimental UI backend (#15002)
* Add experimental UI

* Add test

* Lint
2018-06-16 17:12:03 -04:00
Sebastian Muszynski
c5f012c85a Remove load power attribute for channel USB (#14996)
* Remove load power attribute for channel USB

* Fix format
2018-06-16 15:53:25 -04:00
Andrey
656eae288e Switch to own packaged version of spotipy (#14997) 2018-06-16 15:52:23 -04:00
Teemu R
5898307715 Bump pyhs100 version (#15001)
Fixes #13925
2018-06-16 15:52:03 -04:00
Paulus Schoutsen
9b0efdc8c8 Version bump to 0.73.0.dev0 2018-06-16 10:51:55 -04:00
Paulus Schoutsen
87f9f17335 Version bump to 0.72.0b0 2018-06-16 10:51:07 -04:00
Paulus Schoutsen
f101f6b7cb Merge remote-tracking branch 'origin/master' into dev 2018-06-16 10:50:43 -04:00
Jason Hu
abf07b60f0 Refactoring camera component to use async/await syntax. (#14990)
* Refactoring camera component to use async/await syntax

Also updated camera demo platform to encourage use of async

* Code review
2018-06-16 10:49:11 -04:00
Paulus Schoutsen
0b114f0755 Do not mount deps folder when running in virtual env (#14993)
* Do not mount deps folder when inside virtual env

* Add tests

* Fix package test
2018-06-16 10:48:41 -04:00
Marcelo Moreira de Mello
3ee8f58fdf Upgraded RainCloudy to version 0.0.5 (#14986) 2018-06-16 06:58:18 -04:00
Marcelo Moreira de Mello
ff4da05267 Upgraded python-amcrest to 1.2.3 (#14988) 2018-06-16 06:57:58 -04:00
Marcelo Moreira de Mello
17308a2730 Upgraded PyArlo to 0.1.7 (#14987) 2018-06-16 06:57:27 -04:00
cdce8p
7d9bce2153 Fix extended package support (#14980)
* Fix package recurive merge bug

* Fixed extended package support
2018-06-16 06:55:32 -04:00
Marcelo Moreira de Mello
2839f0ff5f Upgrade ring_doorbell to 0.2.1 to fix oauth issues (#14984)
* Upgraded to ring_doorbell to 0.2.1 to fix oauth issues

* Updated unittest to cover Ring oauth
2018-06-16 08:58:39 +02:00
Johan Bloemberg
2ec295a6f8 Add availability to Rflink entities. (#14977) 2018-06-16 00:26:48 +02:00
Ville Skyttä
4bd7a7eee3 Remove inline pylint disables for messages disabled in pylintrc (#14978) 2018-06-16 00:15:46 +02:00
c727
d0cbbe6141 Return ISO formated datetime in forecast (#14975)
* Return ISO formated datetime in forecast

* Lint
2018-06-15 17:09:01 -04:00
John Mihalic
9efa31ef9f Eight Sleep add REM type, Update async syntax, Catch API quirks (#14937) 2018-06-15 15:24:09 -04:00
Paulus Schoutsen
8a777f6e78 Show notification when user configures Nest client_id/secret (#14970)
* Show notification when user configures Nest client_id/secret

* Lint
2018-06-15 15:19:58 -04:00
Robert Svensson
ac13a2736b Deconz make groups configurable (#14704)
* Make groups configurable

* Config flow and tests in place

* Fix too long line
2018-06-15 14:31:22 -04:00
Albert Lee
940577e105 Fix binary_sensor.skybell state update when there are no events (#14927) 2018-06-15 14:30:35 -04:00
Sriram Vaidyanathan
c917470836 Xiaomi Cameras - multiple models (#14244)
* Added support for Xiaofang Camera

* Added entry for Xiaofang 1080p camera

* Code fix

* Minor comment fix

* Updated coveragerc for Xiaomi cameras

* Added Xiaomi Camera

Added Xiaomi Camera to accommodate multiple models like Yi, Xiaofang, etc.

* Minor code fix

* Minor code fix

* Added model property

* Update xiaomi.py

* Minor code fix

* Update xiaomi.py

* Update xiaomi.py

* Minor code fix

* Package requirement fix due to Version conflict

* To fix conflicts

* Update package_constraints.txt

* Minor fix

* Update xiaomi.py

* Update xiaomi.py

Changes made per comment

* Update xiaomi.py

* Don't update on add.
2018-06-15 14:27:52 -04:00
Paulus Schoutsen
47a344f3a1 Bump frontend to 20180615.0 2018-06-15 13:46:31 -04:00
Paulus Schoutsen
f744a29d9d Add calendar panel, add tests (#14973) 2018-06-15 13:37:46 -04:00
Thibault Cohen
3cd4cb741c Add Calendar API endpoint to get events (#14702)
* Add Calendar API endpoint to get events

* Set default event color

* Fix PR comments

* Fix PR comments

* Fix PR comments

* Remote local.py file

* Use iso 8601

* Fix lint

* Fix PR comments

* Fix PR comments

* Add Support for todoist and demo calendar

* Todoist events are allday events

* Add calendar demo api endpoint test

* Register only one api endpoint for calendar

* Rename demo calendar
2018-06-15 11:16:31 -04:00
Paulus Schoutsen
1128104281 Adhere to scan_interval in platforms when setup via config entry (#14969) 2018-06-15 16:59:13 +02:00
Aaron Bach
d6d685a483 Fix smappee component - "Error on device update" (#14883) 2018-06-14 19:35:53 -06:00
Paulus Schoutsen
2c6e6c2a6f Add config entry for Sonos + Cast (#14955)
* Add config entry for Sonos

* Lint

* Use add_job

* Add Cast config entry

* Lint

* Rename DOMAIN import

* Mock pychromecast in test
2018-06-14 15:17:54 -04:00
Benedict Aas
c8e0de19b6 add relative time option to simulated sensors (#14038)
By default simulated sensors are relative to when they're activated,
instead we make this togglable with this new option 'relative_to_epoch',
and instead they become relative to 1970-01-01 00:00:00.
2018-06-14 14:06:49 -04:00
Paulus Schoutsen
b2440a6d95 Fix tests (#14959)
* Fix tests

* Lint
2018-06-14 11:57:09 -04:00
ruohan.chen
c36c3f0d64 Add support for ZhongHong HVAC Controllers (#14552)
* first blood for ZhongHong HVAC Controller

* add requirements

* requirements_all.txt updated

* add zhong_hong.py to coveragerc

* add comments

* unique_id add platform name

* zhong_hong_hvac version bump to 1.0.1

* improve some coding style to match the project standard

* zhong_hong_hvac version bump to 1.0.4

* zhong_hong_hvac version require 1.0.7

* update requirements by script/gen_requirements_all.py

* zhong_hong_hvac version bump to 1.0.8

* fix startup problem

* remove unused import

* zhong_hong_hvac version bump to 1.0.9

- operation_mode: cold -> cool

* start hub listen event when all climate entities is ready

* use dispatcher to setup hub

* var name change

SIGNAL_DEVICE_SETTED_UP -> SIGNAL_DEVICE_ADDED

* async problem fix

* bugfix: set_operation_mode forget to use upper case

* stringify the exception instead of print full stack of traceback

* avoid to call str(exception) explicity

* remove unnecessary try...except clause

* remove unused import
2018-06-14 09:47:17 -04:00
Aaron Bach
0e7d284c83 Make AirVisual platform async + other adjustments (#14943)
* Changes complete

* Updated requirements

* Add support for scan_interval

* Small style update

* Owner-requested changes
2018-06-14 09:30:47 -04:00
Nick Whyte
cdd111df49 Add sensor.nsw_fuel_station component (#14757)
* Add sensor.nsw_fuel_station component

* bump dependency

* PR Changes

* flake8

* Use MockPrice

* Fix requirements

* Fix tests

* line length

* wip

* Handle errors and show persistent notification

* update tests

* Address @MartinHjelmare's comments

* Fetch station name from API

* Update tests

* Update requirements

* Address comments
2018-06-14 13:56:04 +02:00
Robin
cccd0deb65 Fix Facebox face data parsing (#14951)
* Adds parse_faces

* Update facebox.py
2018-06-13 21:02:46 +02:00
Paulus Schoutsen
e014a84215 Nest config flow (#14921)
* Move nest to dir based component

* Add config flow for Nest

* Load Nest platforms via config entry

* Add tests for Nest config flow

* Import existing access tokens as config entries

* Lint

* Update coverage

* Update translation

* Fix tests

* Address strings

* Use python-nest token resolution

* Lint

* Do not do I/O inside constructor

* Lint

* Update test requirements
2018-06-13 11:14:52 -04:00
Aaron Bach
d549e26a9b Make Yi platform async (#14944)
* Conversion complete

* Updated requirements

* Got rid of 3.6-specific syntax

* Removed more 3.6-specific syntax

* Contributor-requested changes
2018-06-13 11:00:33 -04:00
Marius
08adfd87f7 Add unique_id for mqtt binary sensor (#14929)
* Added unique_id for mqtt binary sensor

* Added missing mqtt message fire in test
2018-06-13 16:20:38 +02:00
Vignesh Venkat
65b0ec6615 Update python-wink to 1.8.0 (#14894)
* wink: Update to python-wink 1.8.0

This pulls in a patch to expose the GE Z-Wave in wall fan switch
as a fan component instead of a light dimmer switch component.

* Update requirements_all.txt
2018-06-13 07:09:42 -04:00
Ville Skyttä
cb646e48d0 Upgrade pylint to 1.9.2 (#14916) 2018-06-13 07:08:39 -04:00
ArrayLabs
fecce206a9 Myq update from 0.0.8 to 0.0.11 (#14947)
* Update requirements_all.txt

Update myq from 0.0.8 to 0.0.11

* Update myq.py

Update myq from 0.0.8 to 0.0.11
2018-06-13 08:02:27 +02:00
Aaron Bach
176ef411de Add scan_interval to RainMachine (#14945) 2018-06-13 07:30:06 +02:00
Pawel
2ac23c8be6 Epson projector support (#14841)
* Epson projector support. Version based on external library

* Epson projector support. Version based on external library

* modified epson according to MartinHjelmare review.
Added description of cmode to services.yaml

* renamed EPSON_SCHEMA to epson_schema

* removed method of getting cmode property

* removed unnecessary checks
change name of cmode service

* renamed SERVICE_ATTR_CMODE to SERVICE_SELECT_CMODE
2018-06-13 07:28:59 +02:00
Hate-Usernames
a373793029 pytradfri 5.5.1: Improved 3rd party bulb support (#14887)
* Bump pytradfri version

* Update light component

* Add tests

* lint

* Docstring typos

* Blank line

* lint

* 5.5.1

* Fix tests on py3.5
2018-06-13 07:17:52 +02:00
Paulus Schoutsen
3153b0c8fc Bump frontend to 20180613.0 2018-06-12 21:20:23 -04:00
Ing. Jaroslav Šafka
89d008d1f3 Fix snapcast uuid to be more unique (#14925)
Current uuid is ok when using only 1 snapserver
New uuid is needed when using multiple snapserver

Because the client can connect to more snapservers and
then uuid based on client MAC is not enough
2018-06-12 15:46:53 +02:00
Christoph Gerneth
6755ae2605 Add support for KIWI Door Locks (#14485)
* initial commit for kiwi door locks

bugfixes

improved attribute display

flake8

more style adjustments

* added session handling

flake8

* added requirements_all

reordered imports and flake8

attempt to pelase a very picky linter

also pleasing pylint now :)

* re-try the build

* added kiwi.py to .coveragerc

* reorganized datetime handling and attribute naming

* created pypi package for door lock library

* updated requirements_all.txt

* code review changes

* added async lock state reset for locking state

* refactored lat/lon attribute updates

* initial locked state changed from undefined to locked

* refactored is_locked property check

* handling authentication exception in setup_platform

* added more check in setup_platform

* code review changes: return type in setup_platform

* fixed logging issue

* event handling in main thread

* updated kiwiki-client to version 0.1.1

* renamed alias e to exc
2018-06-12 12:36:02 +02:00
Jason Hu
c18033ba85 Use cv.time_period instead of cv.time_period_str (#14938) 2018-06-12 09:32:13 +02:00
Marcelo Moreira de Mello
cdc5388dc9 Refactored Arlo component and enhanced Arlo API queries and times (#14823)
* start arlo refactoring

* Refactored Arlo Hub to avoid uncessary and duplicated GETs to Arlo API

* Refactored Arlo camera component to avoid duplicate queries

* Added debug and error messages when video is not found

* Transformed Arlo Control Panel to Sync

* Makes linter happy

* Uses total_seconds() for scan_interval

* Added callback and fixed scan_interval issue

* Disable multiple tries and supported custom modes set in Arlo

* Bump PyArlo version to 0.1.4

* Makes lint happy

* Removed ArloHub object and added some tweaks

* Fixed hub_refresh method

* Makes lint happy

* Ajusted async syntax and added callbacks decorators

* Bump PyArlo version to 0.1.6 to include some enhacements

* Refined code
2018-06-12 08:01:26 +02:00
Ong Vairoj
be4776d039 Add more test cases for samsungtv (#14900)
More test cases to cover retry logic added in 58a1c3839
2018-06-12 07:33:21 +02:00
Jason Hu
30111ea417 Upgrade python-nest, add security_state sensor, nest.set_mode service set ETA as well (#14901) 2018-06-12 07:28:16 +02:00
Erik Eriksson
576c806e86 Update mqtt_eventstream.py (#14923)
* Update mqtt_eventstream.py

Remove a line setting an internal state mqtt_eventstream.initialized to True since:
1. No other platform is doing this
2. This will create an annoying entity/item in the user interface which the user will have to explicitly hide

* Update mqtt_eventstream.py
2018-06-11 15:29:04 +02:00
Matt Snyder
1c561eaf0d Add support for multiple Doorbird stations (#13994) 2018-06-10 19:02:44 +02:00
Ben Lebherz
1da30032a0 Add support for the Unitymedia Horizon HD Recorder (#14275)
* added new platform for the Unitymedia Horizon HD Recorder

* improve connection handling of the horizon platform

* remove unneeded parameters and fix spelling in the horizon platform

* abort or raise exception if connection to the device could not be established

* remove channel/source list and SELECT_SOURCE feature

* remove useless type check after cast and use a try block instead

* abort or raise exception if reconnect to device fails

* remove protocol specific code and restructure sending logic accordingly

* fix indentation to be pep8 complaint

* remove unused methods/properties

* fix unnecessary pylint commands and use a return to abert outside of setup_platform

* directly access config values
2018-06-10 15:38:55 +02:00
Nate Clark
d5bbb6ffd2 Add api_host option to Konnected config (#14896) 2018-06-10 13:50:25 +02:00
Dan Klaffenbach
b4e5695bbd Bump to denonavr 0.7.3 (#14907)
Closes #14792

See #14794
2018-06-10 13:00:14 +02:00
Yevgeniy
716ab0433f Added daily and hourly modes to Openweathermap (#14875)
* Added daily and hourly modes

Added wind speed and bearing to forecast

* Fix mixed spaces and tabs

* Fix lint

* Fix pylint

* Revert one attribution, order alphabetically
2018-06-10 12:35:10 +02:00
Fabian Affolter
7d9ef97bda Upgrade pylast to 2.3.0 (#14888) 2018-06-10 11:38:35 +02:00
Fabian Affolter
703b4354e0 Upgrade python-mystrom to 0.4.4 (#14889) 2018-06-10 11:38:23 +02:00
Fabian Affolter
ce0ca7ff90 Upgrade sendgrid to 5.4.0 (#14891) 2018-06-10 11:38:11 +02:00
Fabian Affolter
54e87836f6 Upgrade psutil to 5.4.6 (#14892) 2018-06-10 11:37:58 +02:00
Fabian Affolter
5f4aa6d2ba Upgrade python_opendata_transport to 0.1.3 (#14905) 2018-06-10 11:37:44 +02:00
Fabian Affolter
dc447a75c6 Upgrade pyuptimerobot to 0.0.5 2018-06-10 10:58:45 +02:00
Joakim Sørensen
ce7e9e36dd Add Uptime Robot sensor (#14631)
* Added Uptime Robot sensor

* added newline at the end and corrected doclink

* Added changes form @cdce8p

* Convert to binary_sensor

* updated requirements

* moved to correct dir

* Update uptimerobot.py
2018-06-10 10:28:53 +02:00
Aaron Bach
8aca2e84dc Make RainMachine async (#14879)
* Make RainMachine async

* Updated requirements

* Dispatcher adjustments

* Small verbiage change

* Member-requested changes

* Style consistency

* Updated requirements
2018-06-10 10:23:07 +02:00
Fabian Affolter
f3e55ce330 Allow different identifiers for the CPU temperature (fixes #10104) (#14898) 2018-06-10 08:39:48 +02:00
Ing. Jaroslav Šafka
20caeb5383 Add entity registry support to media_player.snapcast (#14895)
Unique id for client is generated from prefix 'snapcast_client_'
and MAC address

Unique id for group is generated from prefix 'snapcast_group_'
and UUID provided by snapcast library
2018-06-10 08:31:42 +02:00
hanzoh
bc0d0751b9 Add missing mapping of RotaryHandleSensorIP states (#14885) 2018-06-09 16:12:42 +02:00
John Arild Berentsen
5393b073fe Discover Qubino ZMHTDx smart meter switches (#14884) 2018-06-09 15:34:36 +02:00
Ong Vairoj
d7b7370c82 Samsung TV can't turn off after idle period (#14587)
When Samsung TV is idle for a period of time after issued a command,
subsequent 'turn_off' command won't turn off the TV. The issue is seen
in Samsung models with websocket as discussed in #12302.

== Reproducible Steps
1. Turn on TV (either via HA or directly).
2. Issue some commands e.g. volume ups / downs.
3. Wait for ~1 minute.
4. Issue turn_off command via HA. TV won't turn off.
5. Issue subsequent turn off commands won't turn off TV still.
6. However, issue some other commands e.g. volume ups / downs multiple
times in a row and then turn_off will turn off the TV.

== Root Cause
The underlying websocket connection opened by samsungctl get closed
after some idle time. There was no retry mechanism so issued commands
would intermittently fail but the subsequent one would succeed when
`_remote` get recreated. With `turn_off()`, however, there is an
additional call to `self.get_remote().close()` which indirectly caused
new connection to be created and then closed immediately. This causes the
component to stuck in failure mode when turn_off command is repeatly
issued.

== The Fix
Recreate the connection and retry the command if connection is closed
to avoid silent failures due to connection closed. Also set `_remote`
to None after calling close() to put it in correct state.

This fix eliminates intermittent command failure and failure mode in
turn_off().
2018-06-09 09:22:34 -04:00
vandenberghev
5f65f67f1e Removed semicolon 2018-06-09 12:37:06 +02:00
Malte Franken
f242418986 UVC camera platform handling unavailable NVR or cameras better (#14864)
* fixed tests: using correct camera configuration now and error handling tests must be separated out to ensure that the setup_component call is actually executed

* better error handling during setup; raising PlatformNotReady in likely recoverable cases; added tests
2018-06-09 07:22:17 +02:00
Jason Hu
d3d9d9ebf2 Add color_status sensor for Nest Protect (#14868) 2018-06-09 07:16:11 +02:00
Paulus Schoutsen
8ceb57752b Merge pull request #14876 from home-assistant/rc
0.71.0
2018-06-08 18:07:39 -04:00
Paulus Schoutsen
bd1af8c3d8 Version bump to 0.71.0 2018-06-08 16:57:59 -04:00
Paulus Schoutsen
6af995026b Add support for new hass.io panel (#14873) 2018-06-08 16:50:19 -04:00
Anders Melchiorsen
b657cff6ba Add netgear_lte component (#14687)
* Add netgear_lte component

* Improvements after review

* Allow multiple notify targets

* Require default notify target
2018-06-08 07:46:34 +02:00
Mal Curtis
e3fba79126 Disable volume control for Onkyo when unavailable (Closes: #14774) (#14863) 2018-06-08 07:45:21 +02:00
Diogo Gomes
bb0068908d Fix unit conversion (#14730)
* reviewing this code min_temp and max_temp are always present and always in celsius

* revert change (preserve unit conversion)

* revert (due to unit conversion)

* self

* clean

* cleaner
2018-06-07 22:44:07 -04:00
Robert Svensson
0748466ffc Zone - Hass configuration name is optional (#14449)
* Hass configuration name is optional

* Check explicitly if name is none

* Reverted back to old logic for zones configured in configuration.yaml, where many zones can have the same name

* New test to verify use case of allowing multiple zones having the same name

* Fix too long line
2018-06-07 17:06:13 -04:00
Dale Higgs
fe018fd58c Add set_default_level to logger (#14703)
* Add set_default_service to logger

* Fix 2-line lint error

* Add set_default_level to services.yaml

* Add tests for set_default_level

* Remove function and add else when setting default
2018-06-07 17:03:04 -04:00
Steve Edson
10317a0f71 Rename Hive hub friendly name (#14747)
Right now, "Hub Status" is very generic, I didn't know what component it was coming from, and the only way to tell was searching the source code to find the reference.
2018-06-07 16:57:07 -04:00
Alexei Chetroi
87d55834be zha: handle "step_with_on_off" cluster command in LevelListener. (#14756) 2018-06-07 16:56:07 -04:00
Sebastian Muszynski
d4cc806cd5 Fix door/window sensor support of the Xiaomi Aqara LAN protocol V2 (Closes: #14775) (#14777) 2018-06-07 16:55:18 -04:00
Sergiy Maysak
1a7e8c88a3 Wireless tags platform (#13495)
* Initial version of wirelesstags platform support.

* Pinned wirelesstagpy, generated requirements_all.

* Fixed temperature units in imperial units systems, make binary events more tags specific.

* Lowercased tag name during entity_id generation.

* Fixed: tag_id template for tag_binary_events, support of light sensor for homebridge.

* Minor style cleanup.

* Removed state, define_name, icon. Reworked async arm/disarm update. Removed static attrs. Introduced available property. Custom events contains components name now. Cleaned dedundant items from schema definition.

* Removed comment and beep duration from attributes. Minor cleanup of documentation comment.

* Ignoring Wemo switches linked in iOS app.

* Reworked passing data from platform to components using signals.
2018-06-07 16:30:20 -04:00
Paulus Schoutsen
90a51160c4 Don't run unnecessary methods in executor pool (#14853)
* Don't run unnecessary methods in executor pool

* Lint

* Lint 2
2018-06-07 15:31:21 -04:00
Fabian Affolter
50321a29b5 Catch ConnectionError (fixes #14241) (#14748) 2018-06-07 20:25:26 +02:00
Paulus Schoutsen
67d137cfd5 Store config entry id in entity registry (#14851)
* Store config entry id in entity registry

* Lint
2018-06-07 20:23:09 +02:00
Philip Rosenberg-Watt
bb4d1773d3 Add min_temp and max_temp to MQTT climate device (#14690)
* Add min_temp and max_temp to MQTT climate device

* Add unit tests

* Remove blank line

* Fix unit tests & temp return values

* PEP-8 fixes

* Remove unused import
2018-06-07 13:50:12 -04:00
Fabian Affolter
a6c1192bfc Upgrade aiohttp to 3.3.0 (#14766) 2018-06-07 13:49:14 -04:00
Matthew Treinish
d14d2fe588 Add IBM Watson IoT Platform component (#13664)
This commit adds a new history component for the IBM Watson IoT
Platform. The IBM Watson IoT Platform allows for tracking of devices
and analytics on top of the device data. This new component allows
users to have home assistant automatically populate a watson iot
platform board with device data from devices managed by home assistant.
2018-06-07 13:43:51 -04:00
starkillerOG
f696331563 Add general sound mode support (#14729)
* Media player: general sound mode support

* General sound mode support

* White spaces

* Add sound mode support to demo media player

* white space

* remove unnessesary code
2018-06-07 10:57:45 -04:00
Anders Melchiorsen
6b2b92a732 Improvements to LIFX reliability (#14848)
* Improve fault tolerance of LIFX initialization

* Update aiolifx to 0.6.3

* Use list comprehension
2018-06-07 10:06:29 -04:00
Fabian Affolter
83ce9450f7 Upgrade Mastodon.py to 1.3.0 (#14858) 2018-06-07 10:04:58 -04:00
Paulus Schoutsen
0b405c33c4 Update Hue flow title (#14852) 2018-06-07 16:00:42 +02:00
Paulus Schoutsen
bf74cab7af Fix non awaited test (#14854) 2018-06-07 15:58:54 +02:00
Paulus Schoutsen
d8adb4bdb0 Bump frontend to 20180607.0 2018-06-06 22:42:01 -04:00
nielstron
33990badcd Fixed Rangefilter constructor call 2018-06-06 20:40:56 +02:00
nielstron
8061f15aec Removal of windows size and precision for range filter 2018-06-06 20:40:55 +02:00
nielstron
25f7c31911 Fixed wrong bound assignment on values below the lower bound 2018-06-06 20:40:54 +02:00
nielstron
bb98331ba4 Fix doctring newline and handle ha.state string-being 2018-06-06 20:40:53 +02:00
nielstron
07d139b3a8 Fix wrong comparison 2018-06-06 20:40:53 +02:00
nielstron
f4ef8fd1bc Changes for new FilterState construct 2018-06-06 20:40:52 +02:00
nielstron
ba836c2e36 Fix indent 2018-06-06 20:40:52 +02:00
nielstron
a0ab356936 Renamed to range filter 2018-06-06 20:40:51 +02:00
nielstron
734a83c657 Removed default values and fixed description in sensor.filter 2018-06-06 20:40:50 +02:00
nielstron
b42f4012d1 Fixed test 2018-06-06 20:40:50 +02:00
nielstron
8501312292 Reordered attribute order 2018-06-06 20:40:49 +02:00
nielstron
3faed2edc1 Add test for new band_pass filter 2018-06-06 20:40:49 +02:00
nielstron
bc70619b17 Added bandpass filter
Allows values in a given range
2018-06-06 20:40:48 +02:00
Sebastian Muszynski
bef15264b7 Ignore the mistaken long_both_click event of the 86sw (Closes: #14802) (#14808) 2018-06-06 19:51:59 +02:00
Roman
6d26915c69 Feature/gearbest library update (Closes: #14813) (#14833) 2018-06-06 11:38:50 +02:00
Paulus Schoutsen
fa2e6ada26 Route themes and translations over websocket (#14828) 2018-06-06 10:12:43 +02:00
Paulus Schoutsen
a6880c452f Migrate entity registry to using websocket (#14830)
* Migrate to using websocket

* Lint
2018-06-06 10:08:36 +02:00
Diogo Gomes
4bccb0d2a1 Merge pull request #14831 from home-assistant/sim-sensor
Limit sensor.simulated to 3 decimals (fixes #14773)
2018-06-05 21:06:11 +01:00
Luc Touraille
103639455c Add Freebox device tracker (#12727)
* Add a device tracker for Freebox routers

* Automatic setup of Freebox device tracker based on discovery

* Make the Freebox device tracker asynchronous
2018-06-05 20:38:50 +02:00
Thomas Krüger
549abd9c7e Improved Fritz!Box thermostat support (#14789) 2018-06-05 20:06:25 +02:00
Fabian Affolter
f1aba5511f Limit to 3 decimals (fixes #14773) 2018-06-05 19:44:41 +02:00
Simon Nørager Sørensen
21d05a8b4d Fixes an issue in Xiaomi TV platform that would some TVs not sleep correctly (#14829) 2018-06-05 19:13:16 +02:00
Mischa Gruber
cb6c869c2f Action parameter doesn't longer have to be the first parameter (#14815)
* Action parameter doesn't longer have to be the first parameter

* Minified code upon suggestion
2018-06-05 18:15:34 +02:00
Hugo Dupras
640e499964 netatmo api is now in pip as pyatmo (#14824)
Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com>
2018-06-05 11:55:53 -04:00
Paulus Schoutsen
b3b4f7468d Further cleanup frontend (#14805)
* Remove registering panels

* Remove unused image

* Lint
2018-06-05 10:50:16 -04:00
Paulus Schoutsen
ad9621ebe5 Use hass iconset (#14185) 2018-06-05 10:49:54 -04:00
Sebastian Muszynski
e370d523ec Bump python-miio version (Closes: #13749) (#14796)
* Bump python-miio version

* Fix Xiaomi Power Strip V1 support (Closes: #13749)
2018-06-04 23:50:18 +02:00
vandenberghev
61a41bb8fc Fix issue #14426: [homeassistant.components.sensor] smappee: Error on device update!
https://github.com/home-assistant/home-assistant/issues/14426
2018-06-04 20:08:17 +02:00
vandenberghev
2da6d3c223 Merge branch 'dev' of https://github.com/home-assistant/home-assistant into dev
# Conflicts:
#	homeassistant/components/sensor/smappee.py
2018-06-04 19:44:04 +02:00
Fabian Affolter
816efa02d1 Use pihole module to get data (#14809) 2018-06-04 18:49:26 +02:00
Fabian Affolter
bd1b1a9ff9 Update syntax (#14812) 2018-06-04 14:44:55 +02:00
quthla
1d23f7f900 Allow Kodi live streams to be recognized as paused (#14623) 2018-06-04 12:24:28 +01:00
Sebastian Muszynski
39843a73de Add additional 86sw model identifier of the LAN protocol V2 (#14799) 2018-06-04 07:39:50 +02:00
Diogo Gomes
aec425d1f6 Weather Platform - IPMA (#14716)
* initial commit

* lint

* update with pyipma

* Added test

* Added test

* lint

* missing dep

* address comments

* lint

* make sure list is iterable

* don't bother with list

* mock dependency

* no need to add test requirements

* last correction
2018-06-03 23:01:48 +02:00
Paulus Schoutsen
855ed2b4e4 Version bump to 0.72.0.dev0 2018-06-03 16:54:23 -04:00
Vincent Van Den Berghe
29e659cf4c Fixed SI units for current consumption 2018-03-13 22:20:56 +01:00
1221 changed files with 27132 additions and 8420 deletions

View File

@@ -61,6 +61,11 @@ omit =
homeassistant/components/coinbase.py
homeassistant/components/sensor/coinbase.py
homeassistant/components/cast/*
homeassistant/components/*/cast.py
homeassistant/components/cloudflare.py
homeassistant/components/comfoconnect.py
homeassistant/components/*/comfoconnect.py
@@ -97,7 +102,7 @@ omit =
homeassistant/components/*/envisalink.py
homeassistant/components/fritzbox.py
homeassistant/components/*/fritzbox.py
homeassistant/components/switch/fritzbox.py
homeassistant/components/eufy.py
homeassistant/components/*/eufy.py
@@ -189,18 +194,21 @@ omit =
homeassistant/components/mychevy.py
homeassistant/components/*/mychevy.py
homeassistant/components/mysensors.py
homeassistant/components/mysensors/*
homeassistant/components/*/mysensors.py
homeassistant/components/neato.py
homeassistant/components/*/neato.py
homeassistant/components/nest.py
homeassistant/components/nest/__init__.py
homeassistant/components/*/nest.py
homeassistant/components/netatmo.py
homeassistant/components/*/netatmo.py
homeassistant/components/netgear_lte.py
homeassistant/components/*/netgear_lte.py
homeassistant/components/octoprint.py
homeassistant/components/*/octoprint.py
@@ -243,12 +251,18 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
homeassistant/components/sisyphus.py
homeassistant/components/*/sisyphus.py
homeassistant/components/skybell.py
homeassistant/components/*/skybell.py
homeassistant/components/smappee.py
homeassistant/components/*/smappee.py
homeassistant/components/sonos/__init__.py
homeassistant/components/*/sonos.py
homeassistant/components/tado.py
homeassistant/components/*/tado.py
@@ -311,6 +325,9 @@ omit =
homeassistant/components/wink/*
homeassistant/components/*/wink.py
homeassistant/components/wirelesstag.py
homeassistant/components/*/wirelesstag.py
homeassistant/components/xiaomi_aqara.py
homeassistant/components/*/xiaomi_aqara.py
@@ -329,6 +346,12 @@ omit =
homeassistant/components/zoneminder.py
homeassistant/components/*/zoneminder.py
homeassistant/components/tuya.py
homeassistant/components/*/tuya.py
homeassistant/components/spider.py
homeassistant/components/*/spider.py
homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/canary.py
homeassistant/components/alarm_control_panel/concord232.py
@@ -348,6 +371,7 @@ omit =
homeassistant/components/binary_sensor/ping.py
homeassistant/components/binary_sensor/rest.py
homeassistant/components/binary_sensor/tapsaff.py
homeassistant/components/binary_sensor/uptimerobot.py
homeassistant/components/browser.py
homeassistant/components/calendar/caldav.py
homeassistant/components/calendar/todoist.py
@@ -363,6 +387,7 @@ omit =
homeassistant/components/camera/rpi_camera.py
homeassistant/components/camera/synology.py
homeassistant/components/camera/xeoma.py
homeassistant/components/camera/xiaomi.py
homeassistant/components/camera/yi.py
homeassistant/components/climate/econet.py
homeassistant/components/climate/ephember.py
@@ -378,6 +403,9 @@ omit =
homeassistant/components/climate/sensibo.py
homeassistant/components/climate/touchline.py
homeassistant/components/climate/venstar.py
homeassistant/components/climate/zhong_hong.py
homeassistant/components/cover/aladdin_connect.py
homeassistant/components/cover/brunt.py
homeassistant/components/cover/garadget.py
homeassistant/components/cover/gogogate2.py
homeassistant/components/cover/homematic.py
@@ -397,6 +425,7 @@ omit =
homeassistant/components/device_tracker/bt_home_hub_5.py
homeassistant/components/device_tracker/cisco_ios.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/freebox.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/google_maps.py
homeassistant/components/device_tracker/gpslogger.py
@@ -440,6 +469,7 @@ omit =
homeassistant/components/light/decora_wifi.py
homeassistant/components/light/decora.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/futurenow.py
homeassistant/components/light/greenwave.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
@@ -462,6 +492,7 @@ omit =
homeassistant/components/light/yeelightsunflower.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/kiwi.py
homeassistant/components/lock/lockitron.py
homeassistant/components/lock/nello.py
homeassistant/components/lock/nuki.py
@@ -472,7 +503,6 @@ omit =
homeassistant/components/media_player/aquostv.py
homeassistant/components/media_player/bluesound.py
homeassistant/components/media_player/braviatv.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/channels.py
homeassistant/components/media_player/clementine.py
homeassistant/components/media_player/cmus.py
@@ -481,10 +511,12 @@ omit =
homeassistant/components/media_player/directv.py
homeassistant/components/media_player/dunehd.py
homeassistant/components/media_player/emby.py
homeassistant/components/media_player/epson.py
homeassistant/components/media_player/firetv.py
homeassistant/components/media_player/frontier_silicon.py
homeassistant/components/media_player/gpmdp.py
homeassistant/components/media_player/gstreamer.py
homeassistant/components/media_player/horizon.py
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/lg_netcast.py
@@ -506,7 +538,6 @@ omit =
homeassistant/components/media_player/russound_rnet.py
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/songpal.py
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
@@ -595,6 +626,7 @@ omit =
homeassistant/components/sensor/domain_expiry.py
homeassistant/components/sensor/dte_energy_bridge.py
homeassistant/components/sensor/dublin_bus_transport.py
homeassistant/components/sensor/duke_energy.py
homeassistant/components/sensor/dwd_weather_warnings.py
homeassistant/components/sensor/ebox.py
homeassistant/components/sensor/eddystone_temperature.py
@@ -634,6 +666,7 @@ omit =
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/luftdaten.py
homeassistant/components/sensor/lyft.py
homeassistant/components/sensor/magicseaweed.py
homeassistant/components/sensor/metoffice.py
homeassistant/components/sensor/miflora.py
homeassistant/components/sensor/mitemp_bt.py
@@ -644,6 +677,7 @@ omit =
homeassistant/components/sensor/nederlandse_spoorwegen.py
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/nsw_fuel_station.py
homeassistant/components/sensor/nut.py
homeassistant/components/sensor/nzbget.py
homeassistant/components/sensor/ohmconnect.py
@@ -748,6 +782,7 @@ omit =
homeassistant/components/tts/picotts.py
homeassistant/components/vacuum/mqtt.py
homeassistant/components/vacuum/roomba.py
homeassistant/components/watson_iot.py
homeassistant/components/weather/bom.py
homeassistant/components/weather/buienradar.py
homeassistant/components/weather/darksky.py

3
.gitignore vendored
View File

@@ -107,3 +107,6 @@ desktop.ini
# Secrets
.lokalise_token
# monkeytype
monkeytype.sqlite3

2
.isort.cfg Normal file
View File

@@ -0,0 +1,2 @@
[settings]
multi_line_output=4

View File

@@ -16,11 +16,17 @@ matrix:
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
# - python: "3.6-dev"
# env: TOXENV=py36
# allow_failures:
# - python: "3.5"
# env: TOXENV=typing
- python: "3.7"
env: TOXENV=py37
dist: xenial
- python: "3.8-dev"
env: TOXENV=py38
dist: xenial
if: branch = dev AND type = push
allow_failures:
- python: "3.8-dev"
env: TOXENV=py38
dist: xenial
cache:
directories:

View File

@@ -70,6 +70,7 @@ homeassistant/components/sensor/filter.py @dgomes
homeassistant/components/sensor/gearbest.py @HerrHofrat
homeassistant/components/sensor/irish_rail_transport.py @ttroy50
homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel
homeassistant/components/sensor/nsw_fuel_station.py @nickw444
homeassistant/components/sensor/pollen.py @bachya
homeassistant/components/sensor/qnap.py @colinodell
homeassistant/components/sensor/sma.py @kellerza

View File

@@ -1,6 +1,6 @@
# Contributing to Home Assistant
Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spent a couple of hours and help to integrate them?
Everybody is invited and welcome to contribute to Home Assistant. There is a lot to do...if you are not a developer perhaps you would like to help with the documentation on [home-assistant.io](https://home-assistant.io/)? If you are a developer and have devices in your home which aren't working with Home Assistant yet, why not spend a couple of hours and help to integrate them?
The process is straight-forward.

View File

@@ -8,7 +8,7 @@ import subprocess
import sys
import threading
from typing import Optional, List, Dict, Any # noqa #pylint: disable=unused-import
from typing import List, Dict, Any # noqa pylint: disable=unused-import
from homeassistant import monkey_patch
@@ -20,7 +20,7 @@ from homeassistant.const import (
)
def attempt_use_uvloop():
def attempt_use_uvloop() -> None:
"""Attempt to use uvloop."""
import asyncio
@@ -241,7 +241,7 @@ def cmdline() -> List[str]:
def setup_and_run_hass(config_dir: str,
args: argparse.Namespace) -> Optional[int]:
args: argparse.Namespace) -> int:
"""Set up HASS and run."""
from homeassistant import bootstrap
@@ -274,17 +274,17 @@ def setup_and_run_hass(config_dir: str,
log_no_color=args.log_no_color)
if hass is None:
return None
return -1
if args.open_ui:
# Imported here to avoid importing asyncio before monkey patch
from homeassistant.util.async_ import run_callback_threadsafe
def open_browser(event):
"""Open the webinterface in a browser."""
if hass.config.api is not None:
def open_browser(_: Any) -> None:
"""Open the web interface in a browser."""
if hass.config.api is not None: # type: ignore
import webbrowser
webbrowser.open(hass.config.api.base_url)
webbrowser.open(hass.config.api.base_url) # type: ignore
run_callback_threadsafe(
hass.loop,

View File

@@ -1,503 +0,0 @@
"""Provide an authentication layer for Home Assistant."""
import asyncio
import binascii
from collections import OrderedDict
from datetime import datetime, timedelta
import os
import importlib
import logging
import uuid
import attr
import voluptuous as vol
from voluptuous.humanize import humanize_error
from homeassistant import data_entry_flow, requirements
from homeassistant.core import callback
from homeassistant.const import CONF_TYPE, CONF_NAME, CONF_ID
from homeassistant.util.decorator import Registry
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
AUTH_PROVIDERS = Registry()
AUTH_PROVIDER_SCHEMA = vol.Schema({
vol.Required(CONF_TYPE): str,
vol.Optional(CONF_NAME): str,
# Specify ID if you have two auth providers for same type.
vol.Optional(CONF_ID): str,
}, extra=vol.ALLOW_EXTRA)
ACCESS_TOKEN_EXPIRATION = timedelta(minutes=30)
DATA_REQS = 'auth_reqs_processed'
def generate_secret(entropy: int = 32) -> str:
"""Generate a secret.
Backport of secrets.token_hex from Python 3.6
Event loop friendly.
"""
return binascii.hexlify(os.urandom(entropy)).decode('ascii')
class AuthProvider:
"""Provider of user authentication."""
DEFAULT_TITLE = 'Unnamed auth provider'
initialized = False
def __init__(self, hass, store, config):
"""Initialize an auth provider."""
self.hass = hass
self.store = store
self.config = config
@property
def id(self): # pylint: disable=invalid-name
"""Return id of the auth provider.
Optional, can be None.
"""
return self.config.get(CONF_ID)
@property
def type(self):
"""Return type of the provider."""
return self.config[CONF_TYPE]
@property
def name(self):
"""Return the name of the auth provider."""
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
async def async_credentials(self):
"""Return all credentials of this provider."""
return await self.store.credentials_for_provider(self.type, self.id)
@callback
def async_create_credentials(self, data):
"""Create credentials."""
return Credentials(
auth_provider_type=self.type,
auth_provider_id=self.id,
data=data,
)
# Implement by extending class
async def async_initialize(self):
"""Initialize the auth provider.
Optional.
"""
async def async_credential_flow(self):
"""Return the data flow for logging in with auth provider."""
raise NotImplementedError
async def async_get_or_create_credentials(self, flow_result):
"""Get credentials based on the flow result."""
raise NotImplementedError
async def async_user_meta_for_credentials(self, credentials):
"""Return extra user metadata for credentials.
Will be used to populate info when creating a new user.
"""
return {}
@attr.s(slots=True)
class User:
"""A user."""
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
is_owner = attr.ib(type=bool, default=False)
is_active = attr.ib(type=bool, default=False)
name = attr.ib(type=str, default=None)
# For persisting and see if saved?
# store = attr.ib(type=AuthStore, default=None)
# List of credentials of a user.
credentials = attr.ib(type=list, default=attr.Factory(list))
# Tokens associated with a user.
refresh_tokens = attr.ib(type=dict, default=attr.Factory(dict))
def as_dict(self):
"""Convert user object to a dictionary."""
return {
'id': self.id,
'is_owner': self.is_owner,
'is_active': self.is_active,
'name': self.name,
}
@attr.s(slots=True)
class RefreshToken:
"""RefreshToken for a user to grant new access tokens."""
user = attr.ib(type=User)
client_id = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
created_at = attr.ib(type=datetime, default=attr.Factory(dt_util.utcnow))
access_token_expiration = attr.ib(type=timedelta,
default=ACCESS_TOKEN_EXPIRATION)
token = attr.ib(type=str,
default=attr.Factory(lambda: generate_secret(64)))
access_tokens = attr.ib(type=list, default=attr.Factory(list))
@attr.s(slots=True)
class AccessToken:
"""Access token to access the API.
These will only ever be stored in memory and not be persisted.
"""
refresh_token = attr.ib(type=RefreshToken)
created_at = attr.ib(type=datetime, default=attr.Factory(dt_util.utcnow))
token = attr.ib(type=str,
default=attr.Factory(generate_secret))
@property
def expires(self):
"""Return datetime when this token expires."""
return self.created_at + self.refresh_token.access_token_expiration
@attr.s(slots=True)
class Credentials:
"""Credentials for a user on an auth provider."""
auth_provider_type = attr.ib(type=str)
auth_provider_id = attr.ib(type=str)
# Allow the auth provider to store data to represent their auth.
data = attr.ib(type=dict)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
is_new = attr.ib(type=bool, default=True)
@attr.s(slots=True)
class Client:
"""Client that interacts with Home Assistant on behalf of a user."""
name = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
secret = attr.ib(type=str, default=attr.Factory(generate_secret))
redirect_uris = attr.ib(type=list, default=attr.Factory(list))
async def load_auth_provider_module(hass, provider):
"""Load an auth provider."""
try:
module = importlib.import_module(
'homeassistant.auth_providers.{}'.format(provider))
except ImportError:
_LOGGER.warning('Unable to find auth provider %s', provider)
return None
if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'):
return module
processed = hass.data.get(DATA_REQS)
if processed is None:
processed = hass.data[DATA_REQS] = set()
elif provider in processed:
return module
req_success = await requirements.async_process_requirements(
hass, 'auth provider {}'.format(provider), module.REQUIREMENTS)
if not req_success:
return None
return module
async def auth_manager_from_config(hass, provider_configs):
"""Initialize an auth manager from config."""
store = AuthStore(hass)
if provider_configs:
providers = await asyncio.gather(
*[_auth_provider_from_config(hass, store, config)
for config in provider_configs])
else:
providers = []
# So returned auth providers are in same order as config
provider_hash = OrderedDict()
for provider in providers:
if provider is None:
continue
key = (provider.type, provider.id)
if key in provider_hash:
_LOGGER.error(
'Found duplicate provider: %s. Please add unique IDs if you '
'want to have the same provider twice.', key)
continue
provider_hash[key] = provider
manager = AuthManager(hass, store, provider_hash)
return manager
async def _auth_provider_from_config(hass, store, config):
"""Initialize an auth provider from a config."""
provider_name = config[CONF_TYPE]
module = await load_auth_provider_module(hass, provider_name)
if module is None:
return None
try:
config = module.CONFIG_SCHEMA(config)
except vol.Invalid as err:
_LOGGER.error('Invalid configuration for auth provider %s: %s',
provider_name, humanize_error(config, err))
return None
return AUTH_PROVIDERS[provider_name](hass, store, config)
class AuthManager:
"""Manage the authentication for Home Assistant."""
def __init__(self, hass, store, providers):
"""Initialize the auth manager."""
self._store = store
self._providers = providers
self.login_flow = data_entry_flow.FlowManager(
hass, self._async_create_login_flow,
self._async_finish_login_flow)
self.access_tokens = {}
@property
def async_auth_providers(self):
"""Return a list of available auth providers."""
return self._providers.values()
async def async_get_user(self, user_id):
"""Retrieve a user."""
return await self._store.async_get_user(user_id)
async def async_get_or_create_user(self, credentials):
"""Get or create a user."""
return await self._store.async_get_or_create_user(
credentials, self._async_get_auth_provider(credentials))
async def async_link_user(self, user, credentials):
"""Link credentials to an existing user."""
await self._store.async_link_user(user, credentials)
async def async_remove_user(self, user):
"""Remove a user."""
await self._store.async_remove_user(user)
async def async_create_refresh_token(self, user, client_id):
"""Create a new refresh token for a user."""
return await self._store.async_create_refresh_token(user, client_id)
async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
return await self._store.async_get_refresh_token(token)
@callback
def async_create_access_token(self, refresh_token):
"""Create a new access token."""
access_token = AccessToken(refresh_token)
self.access_tokens[access_token.token] = access_token
return access_token
@callback
def async_get_access_token(self, token):
"""Get an access token."""
return self.access_tokens.get(token)
async def async_create_client(self, name, *, redirect_uris=None,
no_secret=False):
"""Create a new client."""
return await self._store.async_create_client(
name, redirect_uris, no_secret)
async def async_get_client(self, client_id):
"""Get a client."""
return await self._store.async_get_client(client_id)
async def _async_create_login_flow(self, handler, *, source, data):
"""Create a login flow."""
auth_provider = self._providers[handler]
if not auth_provider.initialized:
auth_provider.initialized = True
await auth_provider.async_initialize()
return await auth_provider.async_credential_flow()
async def _async_finish_login_flow(self, result):
"""Result of a credential login flow."""
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
return None
auth_provider = self._providers[result['handler']]
return await auth_provider.async_get_or_create_credentials(
result['data'])
@callback
def _async_get_auth_provider(self, credentials):
"""Helper to get auth provider from a set of credentials."""
auth_provider_key = (credentials.auth_provider_type,
credentials.auth_provider_id)
return self._providers[auth_provider_key]
class AuthStore:
"""Stores authentication info.
Any mutation to an object should happen inside the auth store.
The auth store is lazy. It won't load the data from disk until a method is
called that needs it.
"""
def __init__(self, hass):
"""Initialize the auth store."""
self.hass = hass
self.users = None
self.clients = None
self._load_lock = asyncio.Lock(loop=hass.loop)
async def credentials_for_provider(self, provider_type, provider_id):
"""Return credentials for specific auth provider type and id."""
if self.users is None:
await self.async_load()
return [
credentials
for user in self.users.values()
for credentials in user.credentials
if (credentials.auth_provider_type == provider_type and
credentials.auth_provider_id == provider_id)
]
async def async_get_user(self, user_id):
"""Retrieve a user."""
if self.users is None:
await self.async_load()
return self.users.get(user_id)
async def async_get_or_create_user(self, credentials, auth_provider):
"""Get or create a new user for given credentials.
If link_user is passed in, the credentials will be linked to the passed
in user if the credentials are new.
"""
if self.users is None:
await self.async_load()
# New credentials, store in user
if credentials.is_new:
info = await auth_provider.async_user_meta_for_credentials(
credentials)
# Make owner and activate user if it's the first user.
if self.users:
is_owner = False
is_active = False
else:
is_owner = True
is_active = True
new_user = User(
is_owner=is_owner,
is_active=is_active,
name=info.get('name'),
)
self.users[new_user.id] = new_user
await self.async_link_user(new_user, credentials)
return new_user
for user in self.users.values():
for creds in user.credentials:
if (creds.auth_provider_type == credentials.auth_provider_type
and creds.auth_provider_id ==
credentials.auth_provider_id):
return user
raise ValueError('We got credentials with ID but found no user')
async def async_link_user(self, user, credentials):
"""Add credentials to an existing user."""
user.credentials.append(credentials)
await self.async_save()
credentials.is_new = False
async def async_remove_user(self, user):
"""Remove a user."""
self.users.pop(user.id)
await self.async_save()
async def async_create_refresh_token(self, user, client_id):
"""Create a new token for a user."""
refresh_token = RefreshToken(user, client_id)
user.refresh_tokens[refresh_token.token] = refresh_token
await self.async_save()
return refresh_token
async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
if self.users is None:
await self.async_load()
for user in self.users.values():
refresh_token = user.refresh_tokens.get(token)
if refresh_token is not None:
return refresh_token
return None
async def async_create_client(self, name, redirect_uris, no_secret):
"""Create a new client."""
if self.clients is None:
await self.async_load()
kwargs = {
'name': name,
'redirect_uris': redirect_uris
}
if no_secret:
kwargs['secret'] = None
client = Client(**kwargs)
self.clients[client.id] = client
await self.async_save()
return client
async def async_get_client(self, client_id):
"""Get a client."""
if self.clients is None:
await self.async_load()
return self.clients.get(client_id)
async def async_load(self):
"""Load the users."""
async with self._load_lock:
self.users = {}
self.clients = {}
async def async_save(self):
"""Save users."""
pass

View File

@@ -0,0 +1,243 @@
"""Provide an authentication layer for Home Assistant."""
import asyncio
import logging
from collections import OrderedDict
from homeassistant import data_entry_flow
from homeassistant.core import callback
from . import models
from . import auth_store
from .providers import auth_provider_from_config
_LOGGER = logging.getLogger(__name__)
async def auth_manager_from_config(hass, provider_configs):
"""Initialize an auth manager from config."""
store = auth_store.AuthStore(hass)
if provider_configs:
providers = await asyncio.gather(
*[auth_provider_from_config(hass, store, config)
for config in provider_configs])
else:
providers = []
# So returned auth providers are in same order as config
provider_hash = OrderedDict()
for provider in providers:
if provider is None:
continue
key = (provider.type, provider.id)
if key in provider_hash:
_LOGGER.error(
'Found duplicate provider: %s. Please add unique IDs if you '
'want to have the same provider twice.', key)
continue
provider_hash[key] = provider
manager = AuthManager(hass, store, provider_hash)
return manager
class AuthManager:
"""Manage the authentication for Home Assistant."""
def __init__(self, hass, store, providers):
"""Initialize the auth manager."""
self._store = store
self._providers = providers
self.login_flow = data_entry_flow.FlowManager(
hass, self._async_create_login_flow,
self._async_finish_login_flow)
self._access_tokens = OrderedDict()
@property
def active(self):
"""Return if any auth providers are registered."""
return bool(self._providers)
@property
def support_legacy(self):
"""
Return if legacy_api_password auth providers are registered.
Should be removed when we removed legacy_api_password auth providers.
"""
for provider_type, _ in self._providers:
if provider_type == 'legacy_api_password':
return True
return False
@property
def auth_providers(self):
"""Return a list of available auth providers."""
return list(self._providers.values())
async def async_get_users(self):
"""Retrieve all users."""
return await self._store.async_get_users()
async def async_get_user(self, user_id):
"""Retrieve a user."""
return await self._store.async_get_user(user_id)
async def async_create_system_user(self, name):
"""Create a system user."""
return await self._store.async_create_user(
name=name,
system_generated=True,
is_active=True,
)
async def async_create_user(self, name):
"""Create a user."""
kwargs = {
'name': name,
'is_active': True,
}
if await self._user_should_be_owner():
kwargs['is_owner'] = True
return await self._store.async_create_user(**kwargs)
async def async_get_or_create_user(self, credentials):
"""Get or create a user."""
if not credentials.is_new:
for user in await self._store.async_get_users():
for creds in user.credentials:
if creds.id == credentials.id:
return user
raise ValueError('Unable to find the user.')
auth_provider = self._async_get_auth_provider(credentials)
if auth_provider is None:
raise RuntimeError('Credential with unknown provider encountered')
info = await auth_provider.async_user_meta_for_credentials(
credentials)
return await self._store.async_create_user(
credentials=credentials,
name=info.get('name'),
is_active=info.get('is_active', False)
)
async def async_link_user(self, user, credentials):
"""Link credentials to an existing user."""
await self._store.async_link_user(user, credentials)
async def async_remove_user(self, user):
"""Remove a user."""
tasks = [
self.async_remove_credentials(credentials)
for credentials in user.credentials
]
if tasks:
await asyncio.wait(tasks)
await self._store.async_remove_user(user)
async def async_activate_user(self, user):
"""Activate a user."""
await self._store.async_activate_user(user)
async def async_deactivate_user(self, user):
"""Deactivate a user."""
if user.is_owner:
raise ValueError('Unable to deactive the owner')
await self._store.async_deactivate_user(user)
async def async_remove_credentials(self, credentials):
"""Remove credentials."""
provider = self._async_get_auth_provider(credentials)
if (provider is not None and
hasattr(provider, 'async_will_remove_credentials')):
await provider.async_will_remove_credentials(credentials)
await self._store.async_remove_credentials(credentials)
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new refresh token for a user."""
if not user.is_active:
raise ValueError('User is not active')
if user.system_generated and client_id is not None:
raise ValueError(
'System generated users cannot have refresh tokens connected '
'to a client.')
if not user.system_generated and client_id is None:
raise ValueError('Client is required to generate a refresh token.')
return await self._store.async_create_refresh_token(user, client_id)
async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
return await self._store.async_get_refresh_token(token)
@callback
def async_create_access_token(self, refresh_token):
"""Create a new access token."""
access_token = models.AccessToken(refresh_token=refresh_token)
self._access_tokens[access_token.token] = access_token
return access_token
@callback
def async_get_access_token(self, token):
"""Get an access token."""
tkn = self._access_tokens.get(token)
if tkn is None:
_LOGGER.debug('Attempt to get non-existing access token')
return None
if tkn.expired or not tkn.refresh_token.user.is_active:
if tkn.expired:
_LOGGER.debug('Attempt to get expired access token')
else:
_LOGGER.debug('Attempt to get access token for inactive user')
self._access_tokens.pop(token)
return None
return tkn
async def _async_create_login_flow(self, handler, *, source, data):
"""Create a login flow."""
auth_provider = self._providers[handler]
return await auth_provider.async_credential_flow()
async def _async_finish_login_flow(self, result):
"""Result of a credential login flow."""
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
return None
auth_provider = self._providers[result['handler']]
return await auth_provider.async_get_or_create_credentials(
result['data'])
@callback
def _async_get_auth_provider(self, credentials):
"""Helper to get auth provider from a set of credentials."""
auth_provider_key = (credentials.auth_provider_type,
credentials.auth_provider_id)
return self._providers.get(auth_provider_key)
async def _user_should_be_owner(self):
"""Determine if user should be owner.
A user should be an owner if it is the first non-system user that is
being created.
"""
for user in await self._store.async_get_users():
if not user.system_generated:
return False
return True

View File

@@ -0,0 +1,240 @@
"""Storage for auth models."""
from collections import OrderedDict
from datetime import timedelta
from homeassistant.util import dt as dt_util
from . import models
STORAGE_VERSION = 1
STORAGE_KEY = 'auth'
class AuthStore:
"""Stores authentication info.
Any mutation to an object should happen inside the auth store.
The auth store is lazy. It won't load the data from disk until a method is
called that needs it.
"""
def __init__(self, hass):
"""Initialize the auth store."""
self.hass = hass
self._users = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
async def async_get_users(self):
"""Retrieve all users."""
if self._users is None:
await self.async_load()
return list(self._users.values())
async def async_get_user(self, user_id):
"""Retrieve a user by id."""
if self._users is None:
await self.async_load()
return self._users.get(user_id)
async def async_create_user(self, name, is_owner=None, is_active=None,
system_generated=None, credentials=None):
"""Create a new user."""
if self._users is None:
await self.async_load()
kwargs = {
'name': name
}
if is_owner is not None:
kwargs['is_owner'] = is_owner
if is_active is not None:
kwargs['is_active'] = is_active
if system_generated is not None:
kwargs['system_generated'] = system_generated
new_user = models.User(**kwargs)
self._users[new_user.id] = new_user
if credentials is None:
await self.async_save()
return new_user
# Saving is done inside the link.
await self.async_link_user(new_user, credentials)
return new_user
async def async_link_user(self, user, credentials):
"""Add credentials to an existing user."""
user.credentials.append(credentials)
await self.async_save()
credentials.is_new = False
async def async_remove_user(self, user):
"""Remove a user."""
self._users.pop(user.id)
await self.async_save()
async def async_activate_user(self, user):
"""Activate a user."""
user.is_active = True
await self.async_save()
async def async_deactivate_user(self, user):
"""Activate a user."""
user.is_active = False
await self.async_save()
async def async_remove_credentials(self, credentials):
"""Remove credentials."""
for user in self._users.values():
found = None
for index, cred in enumerate(user.credentials):
if cred is credentials:
found = index
break
if found is not None:
user.credentials.pop(found)
break
await self.async_save()
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new token for a user."""
refresh_token = models.RefreshToken(user=user, client_id=client_id)
user.refresh_tokens[refresh_token.token] = refresh_token
await self.async_save()
return refresh_token
async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
if self._users is None:
await self.async_load()
for user in self._users.values():
refresh_token = user.refresh_tokens.get(token)
if refresh_token is not None:
return refresh_token
return None
async def async_load(self):
"""Load the users."""
data = await self._store.async_load()
# Make sure that we're not overriding data if 2 loads happened at the
# same time
if self._users is not None:
return
users = OrderedDict()
if data is None:
self._users = users
return
for user_dict in data['users']:
users[user_dict['id']] = models.User(**user_dict)
for cred_dict in data['credentials']:
users[cred_dict['user_id']].credentials.append(models.Credentials(
id=cred_dict['id'],
is_new=False,
auth_provider_type=cred_dict['auth_provider_type'],
auth_provider_id=cred_dict['auth_provider_id'],
data=cred_dict['data'],
))
refresh_tokens = OrderedDict()
for rt_dict in data['refresh_tokens']:
token = models.RefreshToken(
id=rt_dict['id'],
user=users[rt_dict['user_id']],
client_id=rt_dict['client_id'],
created_at=dt_util.parse_datetime(rt_dict['created_at']),
access_token_expiration=timedelta(
seconds=rt_dict['access_token_expiration']),
token=rt_dict['token'],
)
refresh_tokens[token.id] = token
users[rt_dict['user_id']].refresh_tokens[token.token] = token
for ac_dict in data['access_tokens']:
refresh_token = refresh_tokens[ac_dict['refresh_token_id']]
token = models.AccessToken(
refresh_token=refresh_token,
created_at=dt_util.parse_datetime(ac_dict['created_at']),
token=ac_dict['token'],
)
refresh_token.access_tokens.append(token)
self._users = users
async def async_save(self):
"""Save users."""
users = [
{
'id': user.id,
'is_owner': user.is_owner,
'is_active': user.is_active,
'name': user.name,
'system_generated': user.system_generated,
}
for user in self._users.values()
]
credentials = [
{
'id': credential.id,
'user_id': user.id,
'auth_provider_type': credential.auth_provider_type,
'auth_provider_id': credential.auth_provider_id,
'data': credential.data,
}
for user in self._users.values()
for credential in user.credentials
]
refresh_tokens = [
{
'id': refresh_token.id,
'user_id': user.id,
'client_id': refresh_token.client_id,
'created_at': refresh_token.created_at.isoformat(),
'access_token_expiration':
refresh_token.access_token_expiration.total_seconds(),
'token': refresh_token.token,
}
for user in self._users.values()
for refresh_token in user.refresh_tokens.values()
]
access_tokens = [
{
'id': user.id,
'refresh_token_id': refresh_token.id,
'created_at': access_token.created_at.isoformat(),
'token': access_token.token,
}
for user in self._users.values()
for refresh_token in user.refresh_tokens.values()
for access_token in refresh_token.access_tokens
]
data = {
'users': users,
'credentials': credentials,
'access_tokens': access_tokens,
'refresh_tokens': refresh_tokens,
}
await self._store.async_save(data, delay=1)

View File

@@ -0,0 +1,4 @@
"""Constants for the auth module."""
from datetime import timedelta
ACCESS_TOKEN_EXPIRATION = timedelta(minutes=30)

View File

@@ -0,0 +1,75 @@
"""Auth models."""
from datetime import datetime, timedelta
import uuid
import attr
from homeassistant.util import dt as dt_util
from .const import ACCESS_TOKEN_EXPIRATION
from .util import generate_secret
@attr.s(slots=True)
class User:
"""A user."""
name = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
is_owner = attr.ib(type=bool, default=False)
is_active = attr.ib(type=bool, default=False)
system_generated = attr.ib(type=bool, default=False)
# List of credentials of a user.
credentials = attr.ib(type=list, default=attr.Factory(list), cmp=False)
# Tokens associated with a user.
refresh_tokens = attr.ib(type=dict, default=attr.Factory(dict), cmp=False)
@attr.s(slots=True)
class RefreshToken:
"""RefreshToken for a user to grant new access tokens."""
user = attr.ib(type=User)
client_id = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
created_at = attr.ib(type=datetime, default=attr.Factory(dt_util.utcnow))
access_token_expiration = attr.ib(type=timedelta,
default=ACCESS_TOKEN_EXPIRATION)
token = attr.ib(type=str,
default=attr.Factory(lambda: generate_secret(64)))
access_tokens = attr.ib(type=list, default=attr.Factory(list), cmp=False)
@attr.s(slots=True)
class AccessToken:
"""Access token to access the API.
These will only ever be stored in memory and not be persisted.
"""
refresh_token = attr.ib(type=RefreshToken)
created_at = attr.ib(type=datetime, default=attr.Factory(dt_util.utcnow))
token = attr.ib(type=str,
default=attr.Factory(generate_secret))
@property
def expired(self):
"""Return if this token has expired."""
expires = self.created_at + self.refresh_token.access_token_expiration
return dt_util.utcnow() > expires
@attr.s(slots=True)
class Credentials:
"""Credentials for a user on an auth provider."""
auth_provider_type = attr.ib(type=str)
auth_provider_id = attr.ib(type=str)
# Allow the auth provider to store data to represent their auth.
data = attr.ib(type=dict)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
is_new = attr.ib(type=bool, default=True)

View File

@@ -0,0 +1,143 @@
"""Auth providers for Home Assistant."""
import importlib
import logging
import voluptuous as vol
from voluptuous.humanize import humanize_error
from homeassistant import requirements
from homeassistant.core import callback
from homeassistant.const import CONF_TYPE, CONF_NAME, CONF_ID
from homeassistant.util.decorator import Registry
from homeassistant.auth.models import Credentials
_LOGGER = logging.getLogger(__name__)
DATA_REQS = 'auth_prov_reqs_processed'
AUTH_PROVIDERS = Registry()
AUTH_PROVIDER_SCHEMA = vol.Schema({
vol.Required(CONF_TYPE): str,
vol.Optional(CONF_NAME): str,
# Specify ID if you have two auth providers for same type.
vol.Optional(CONF_ID): str,
}, extra=vol.ALLOW_EXTRA)
async def auth_provider_from_config(hass, store, config):
"""Initialize an auth provider from a config."""
provider_name = config[CONF_TYPE]
module = await load_auth_provider_module(hass, provider_name)
if module is None:
return None
try:
config = module.CONFIG_SCHEMA(config)
except vol.Invalid as err:
_LOGGER.error('Invalid configuration for auth provider %s: %s',
provider_name, humanize_error(config, err))
return None
return AUTH_PROVIDERS[provider_name](hass, store, config)
async def load_auth_provider_module(hass, provider):
"""Load an auth provider."""
try:
module = importlib.import_module(
'homeassistant.auth.providers.{}'.format(provider))
except ImportError:
_LOGGER.warning('Unable to find auth provider %s', provider)
return None
if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'):
return module
processed = hass.data.get(DATA_REQS)
if processed is None:
processed = hass.data[DATA_REQS] = set()
elif provider in processed:
return module
req_success = await requirements.async_process_requirements(
hass, 'auth provider {}'.format(provider), module.REQUIREMENTS)
if not req_success:
return None
processed.add(provider)
return module
class AuthProvider:
"""Provider of user authentication."""
DEFAULT_TITLE = 'Unnamed auth provider'
def __init__(self, hass, store, config):
"""Initialize an auth provider."""
self.hass = hass
self.store = store
self.config = config
@property
def id(self): # pylint: disable=invalid-name
"""Return id of the auth provider.
Optional, can be None.
"""
return self.config.get(CONF_ID)
@property
def type(self):
"""Return type of the provider."""
return self.config[CONF_TYPE]
@property
def name(self):
"""Return the name of the auth provider."""
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
async def async_credentials(self):
"""Return all credentials of this provider."""
users = await self.store.async_get_users()
return [
credentials
for user in users
for credentials in user.credentials
if (credentials.auth_provider_type == self.type and
credentials.auth_provider_id == self.id)
]
@callback
def async_create_credentials(self, data):
"""Create credentials."""
return Credentials(
auth_provider_type=self.type,
auth_provider_id=self.id,
data=data,
)
# Implement by extending class
async def async_credential_flow(self):
"""Return the data flow for logging in with auth provider."""
raise NotImplementedError
async def async_get_or_create_credentials(self, flow_result):
"""Get credentials based on the flow result."""
raise NotImplementedError
async def async_user_meta_for_credentials(self, credentials):
"""Return extra user metadata for credentials.
Will be used to populate info when creating a new user.
Values to populate:
- name: string
- is_active: boolean
"""
return {}

View File

@@ -3,18 +3,33 @@ import base64
from collections import OrderedDict
import hashlib
import hmac
from typing import Dict # noqa: F401 pylint: disable=unused-import
import voluptuous as vol
from homeassistant import auth, data_entry_flow
from homeassistant import data_entry_flow
from homeassistant.const import CONF_ID
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import json
from homeassistant.auth.util import generate_secret
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
STORAGE_VERSION = 1
STORAGE_KEY = 'auth_provider.homeassistant'
PATH_DATA = '.users.json'
def _disallow_id(conf):
"""Disallow ID in config."""
if CONF_ID in conf:
raise vol.Invalid(
'ID is not allowed for the homeassistant auth provider.')
CONFIG_SCHEMA = auth.AUTH_PROVIDER_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
return conf
CONFIG_SCHEMA = vol.All(AUTH_PROVIDER_SCHEMA, _disallow_id)
class InvalidAuth(HomeAssistantError):
@@ -31,14 +46,22 @@ class InvalidUser(HomeAssistantError):
class Data:
"""Hold the user data."""
def __init__(self, path, data):
def __init__(self, hass):
"""Initialize the user data store."""
self.path = path
self.hass = hass
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
self._data = None
async def async_load(self):
"""Load stored data."""
data = await self._store.async_load()
if data is None:
data = {
'salt': auth.generate_secret(),
'salt': generate_secret(),
'users': []
}
self._data = data
@property
@@ -46,12 +69,12 @@ class Data:
"""Return users."""
return self._data['users']
def validate_login(self, username, password):
def validate_login(self, username: str, password: str) -> None:
"""Validate a username and password.
Raises InvalidAuth if auth invalid.
"""
password = self.hash_password(password)
hashed = self.hash_password(password)
found = None
@@ -62,71 +85,90 @@ class Data:
if found is None:
# Do one more compare to make timing the same as if user was found.
hmac.compare_digest(password, password)
hmac.compare_digest(hashed, hashed)
raise InvalidAuth
if not hmac.compare_digest(password,
if not hmac.compare_digest(hashed,
base64.b64decode(found['password'])):
raise InvalidAuth
def hash_password(self, password, for_storage=False):
def hash_password(self, password: str, for_storage: bool = False) -> bytes:
"""Encode a password."""
hashed = hashlib.pbkdf2_hmac(
'sha512', password.encode(), self._data['salt'].encode(), 100000)
if for_storage:
hashed = base64.b64encode(hashed).decode()
hashed = base64.b64encode(hashed)
return hashed
def add_user(self, username, password):
"""Add a user."""
def add_auth(self, username: str, password: str) -> None:
"""Add a new authenticated user/pass."""
if any(user['username'] == username for user in self.users):
raise InvalidUser
self.users.append({
'username': username,
'password': self.hash_password(password, True),
'password': self.hash_password(password, True).decode(),
})
def change_password(self, username, new_password):
"""Update the password of a user.
@callback
def async_remove_auth(self, username: str) -> None:
"""Remove authentication."""
index = None
for i, user in enumerate(self.users):
if user['username'] == username:
index = i
break
if index is None:
raise InvalidUser
self.users.pop(index)
def change_password(self, username: str, new_password: str) -> None:
"""Update the password.
Raises InvalidUser if user cannot be found.
"""
for user in self.users:
if user['username'] == username:
user['password'] = self.hash_password(new_password, True)
user['password'] = self.hash_password(
new_password, True).decode()
break
else:
raise InvalidUser
def save(self):
async def async_save(self):
"""Save data."""
json.save_json(self.path, self._data)
await self._store.async_save(self._data)
def load_data(path):
"""Load auth data."""
return Data(path, json.load_json(path, None))
@auth.AUTH_PROVIDERS.register('homeassistant')
class HassAuthProvider(auth.AuthProvider):
@AUTH_PROVIDERS.register('homeassistant')
class HassAuthProvider(AuthProvider):
"""Auth provider based on a local storage of users in HASS config dir."""
DEFAULT_TITLE = 'Home Assistant Local'
data = None
async def async_initialize(self):
"""Initialize the auth provider."""
if self.data is not None:
return
self.data = Data(self.hass)
await self.data.async_load()
async def async_credential_flow(self):
"""Return a flow to login."""
return LoginFlow(self)
async def async_validate_login(self, username, password):
async def async_validate_login(self, username: str, password: str):
"""Helper to validate a username and password."""
def validate():
"""Validate creds."""
data = self._auth_data()
data.validate_login(username, password)
if self.data is None:
await self.async_initialize()
await self.hass.async_add_job(validate)
await self.hass.async_add_executor_job(
self.data.validate_login, username, password)
async def async_get_or_create_credentials(self, flow_result):
"""Get credentials based on the flow result."""
@@ -141,9 +183,24 @@ class HassAuthProvider(auth.AuthProvider):
'username': username
})
def _auth_data(self):
"""Return the auth provider data."""
return load_data(self.hass.config.path(PATH_DATA))
async def async_user_meta_for_credentials(self, credentials):
"""Get extra info for this credential."""
return {
'name': credentials.data['username'],
'is_active': True,
}
async def async_will_remove_credentials(self, credentials):
"""When credentials get removed, also remove the auth."""
if self.data is None:
await self.async_initialize()
try:
self.data.async_remove_auth(credentials.data['username'])
await self.data.async_save()
except InvalidUser:
# Can happen if somehow we didn't clean up a credential
pass
class LoginFlow(data_entry_flow.FlowHandler):
@@ -170,7 +227,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
data=user_input
)
schema = OrderedDict()
schema = OrderedDict() # type: Dict[str, type]
schema['username'] = str
schema['password'] = str

View File

@@ -5,9 +5,11 @@ import hmac
import voluptuous as vol
from homeassistant.exceptions import HomeAssistantError
from homeassistant import auth, data_entry_flow
from homeassistant import data_entry_flow
from homeassistant.core import callback
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
USER_SCHEMA = vol.Schema({
vol.Required('username'): str,
@@ -16,7 +18,7 @@ USER_SCHEMA = vol.Schema({
})
CONFIG_SCHEMA = auth.AUTH_PROVIDER_SCHEMA.extend({
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
vol.Required('users'): [USER_SCHEMA]
}, extra=vol.PREVENT_EXTRA)
@@ -25,8 +27,8 @@ class InvalidAuthError(HomeAssistantError):
"""Raised when submitting invalid authentication."""
@auth.AUTH_PROVIDERS.register('insecure_example')
class ExampleAuthProvider(auth.AuthProvider):
@AUTH_PROVIDERS.register('insecure_example')
class ExampleAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
async def async_credential_flow(self):
@@ -73,14 +75,16 @@ class ExampleAuthProvider(auth.AuthProvider):
Will be used to populate info when creating a new user.
"""
username = credentials.data['username']
info = {
'is_active': True,
}
for user in self.config['users']:
if user['username'] == username:
return {
'name': user.get('name')
}
info['name'] = user.get('name')
break
return {}
return info
class LoginFlow(data_entry_flow.FlowHandler):

View File

@@ -0,0 +1,110 @@
"""
Support Legacy API password auth provider.
It will be removed when auth system production ready
"""
from collections import OrderedDict
import hmac
import voluptuous as vol
from homeassistant.exceptions import HomeAssistantError
from homeassistant import data_entry_flow
from homeassistant.core import callback
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
USER_SCHEMA = vol.Schema({
vol.Required('username'): str,
})
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
LEGACY_USER = 'homeassistant'
class InvalidAuthError(HomeAssistantError):
"""Raised when submitting invalid authentication."""
@AUTH_PROVIDERS.register('legacy_api_password')
class LegacyApiPasswordAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
DEFAULT_TITLE = 'Legacy API Password'
async def async_credential_flow(self):
"""Return a flow to login."""
return LoginFlow(self)
@callback
def async_validate_login(self, password):
"""Helper to validate a username and password."""
if not hasattr(self.hass, 'http'):
raise ValueError('http component is not loaded')
if self.hass.http.api_password is None:
raise ValueError('http component is not configured using'
' api_password')
if not hmac.compare_digest(self.hass.http.api_password.encode('utf-8'),
password.encode('utf-8')):
raise InvalidAuthError
async def async_get_or_create_credentials(self, flow_result):
"""Return LEGACY_USER always."""
for credential in await self.async_credentials():
if credential.data['username'] == LEGACY_USER:
return credential
return self.async_create_credentials({
'username': LEGACY_USER
})
async def async_user_meta_for_credentials(self, credentials):
"""
Set name as LEGACY_USER always.
Will be used to populate info when creating a new user.
"""
return {
'name': LEGACY_USER,
'is_active': True,
}
class LoginFlow(data_entry_flow.FlowHandler):
"""Handler for the login flow."""
def __init__(self, auth_provider):
"""Initialize the login flow."""
self._auth_provider = auth_provider
async def async_step_init(self, user_input=None):
"""Handle the step of the form."""
errors = {}
if user_input is not None:
try:
self._auth_provider.async_validate_login(
user_input['password'])
except InvalidAuthError:
errors['base'] = 'invalid_auth'
if not errors:
return self.async_create_entry(
title=self._auth_provider.name,
data={}
)
schema = OrderedDict()
schema['password'] = str
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(schema),
errors=errors,
)

View File

@@ -0,0 +1,13 @@
"""Auth utils."""
import binascii
import os
def generate_secret(entropy: int = 32) -> str:
"""Generate a secret.
Backport of secrets.token_hex from Python 3.6
Event loop friendly.
"""
return binascii.hexlify(os.urandom(entropy)).decode('ascii')

View File

@@ -1 +0,0 @@
"""Auth providers for Home Assistant."""

View File

@@ -1,5 +1,4 @@
"""Provide methods to bootstrap a Home Assistant instance."""
import asyncio
import logging
import logging.handlers
import os
@@ -17,7 +16,7 @@ from homeassistant.components import persistent_notification
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE
from homeassistant.setup import async_setup_component
from homeassistant.util.logging import AsyncHandler
from homeassistant.util.package import async_get_user_site, get_user_site
from homeassistant.util.package import async_get_user_site, is_virtual_env
from homeassistant.util.yaml import clear_secret_cache
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.signal import async_register_signal_handling
@@ -29,9 +28,8 @@ ERROR_LOG_FILENAME = 'home-assistant.log'
# hass.data key for logging information.
DATA_LOGGING = 'logging'
FIRST_INIT_COMPONENT = set((
'system_log', 'recorder', 'mqtt', 'mqtt_eventstream', 'logger',
'introduction', 'frontend', 'history'))
FIRST_INIT_COMPONENT = {'system_log', 'recorder', 'mqtt', 'mqtt_eventstream',
'logger', 'introduction', 'frontend', 'history'}
def from_config_dict(config: Dict[str, Any],
@@ -53,8 +51,9 @@ def from_config_dict(config: Dict[str, Any],
if config_dir is not None:
config_dir = os.path.abspath(config_dir)
hass.config.config_dir = config_dir
hass.loop.run_until_complete(
async_mount_local_lib_path(config_dir, hass.loop))
if not is_virtual_env():
hass.loop.run_until_complete(
async_mount_local_lib_path(config_dir))
# run task
hass = hass.loop.run_until_complete(
@@ -95,7 +94,8 @@ async def async_from_config_dict(config: Dict[str, Any],
conf_util.async_log_exception(ex, 'homeassistant', core_config, hass)
return None
await hass.async_add_job(conf_util.process_ha_config_upgrade, hass)
await hass.async_add_executor_job(
conf_util.process_ha_config_upgrade, hass)
hass.config.skip_pip = skip_pip
if skip_pip:
@@ -123,7 +123,6 @@ async def async_from_config_dict(config: Dict[str, Any],
components.update(hass.config_entries.async_domains())
# setup components
# pylint: disable=not-an-iterable
res = await core_components.async_setup(hass, config)
if not res:
_LOGGER.error("Home Assistant core failed to initialize. "
@@ -138,7 +137,7 @@ async def async_from_config_dict(config: Dict[str, Any],
for component in components:
if component not in FIRST_INIT_COMPONENT:
continue
hass.async_add_job(async_setup_component(hass, component, config))
hass.async_create_task(async_setup_component(hass, component, config))
await hass.async_block_till_done()
@@ -146,7 +145,7 @@ async def async_from_config_dict(config: Dict[str, Any],
for component in components:
if component in FIRST_INIT_COMPONENT:
continue
hass.async_add_job(async_setup_component(hass, component, config))
hass.async_create_task(async_setup_component(hass, component, config))
await hass.async_block_till_done()
@@ -163,7 +162,8 @@ def from_config_file(config_path: str,
skip_pip: bool = True,
log_rotate_days: Any = None,
log_file: Any = None,
log_no_color: bool = False):
log_no_color: bool = False)\
-> Optional[core.HomeAssistant]:
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter if given,
@@ -188,7 +188,8 @@ async def async_from_config_file(config_path: str,
skip_pip: bool = True,
log_rotate_days: Any = None,
log_file: Any = None,
log_no_color: bool = False):
log_no_color: bool = False)\
-> Optional[core.HomeAssistant]:
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter.
@@ -197,13 +198,15 @@ async def async_from_config_file(config_path: str,
# Set config dir to directory holding config file
config_dir = os.path.abspath(os.path.dirname(config_path))
hass.config.config_dir = config_dir
await async_mount_local_lib_path(config_dir, hass.loop)
if not is_virtual_env():
await async_mount_local_lib_path(config_dir)
async_enable_logging(hass, verbose, log_rotate_days, log_file,
log_no_color)
try:
config_dict = await hass.async_add_job(
config_dict = await hass.async_add_executor_job(
conf_util.load_yaml_config_file, config_path)
except HomeAssistantError as err:
_LOGGER.error("Error loading %s: %s", config_path, err)
@@ -211,16 +214,15 @@ async def async_from_config_file(config_path: str,
finally:
clear_secret_cache()
hass = await async_from_config_dict(
return await async_from_config_dict(
config_dict, hass, enable_log=False, skip_pip=skip_pip)
return hass
@core.callback
def async_enable_logging(hass: core.HomeAssistant,
verbose: bool = False,
log_rotate_days=None,
log_file=None,
log_rotate_days: Optional[int] = None,
log_file: Optional[str] = None,
log_no_color: bool = False) -> None:
"""Set up the logging.
@@ -289,9 +291,9 @@ def async_enable_logging(hass: core.HomeAssistant,
async_handler = AsyncHandler(hass.loop, err_handler)
async def async_stop_async_handler(event):
async def async_stop_async_handler(_: Any) -> None:
"""Cleanup async handler."""
logging.getLogger('').removeHandler(async_handler)
logging.getLogger('').removeHandler(async_handler) # type: ignore
await async_handler.async_close(blocking=True)
hass.bus.async_listen_once(
@@ -308,23 +310,13 @@ def async_enable_logging(hass: core.HomeAssistant,
"Unable to setup error log %s (access denied)", err_log_path)
def mount_local_lib_path(config_dir: str) -> str:
"""Add local library to Python Path."""
deps_dir = os.path.join(config_dir, 'deps')
lib_dir = get_user_site(deps_dir)
if lib_dir not in sys.path:
sys.path.insert(0, lib_dir)
return deps_dir
async def async_mount_local_lib_path(config_dir: str,
loop: asyncio.AbstractEventLoop) -> str:
async def async_mount_local_lib_path(config_dir: str) -> str:
"""Add local library to Python Path.
This function is a coroutine.
"""
deps_dir = os.path.join(config_dir, 'deps')
lib_dir = await async_get_user_site(deps_dir, loop=loop)
lib_dir = await async_get_user_site(deps_dir)
if lib_dir not in sys.path:
sys.path.insert(0, lib_dir)
return deps_dir

View File

@@ -167,7 +167,7 @@ def async_setup(hass, config):
def async_handle_core_service(call):
"""Service handler for handling core services."""
if call.service == SERVICE_HOMEASSISTANT_STOP:
hass.async_add_job(hass.async_stop())
hass.async_create_task(hass.async_stop())
return
try:
@@ -183,7 +183,7 @@ def async_setup(hass, config):
return
if call.service == SERVICE_HOMEASSISTANT_RESTART:
hass.async_add_job(hass.async_stop(RESTART_EXIT_CODE))
hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE))
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)

View File

@@ -85,7 +85,7 @@ ABODE_PLATFORMS = [
]
class AbodeSystem(object):
class AbodeSystem:
"""Abode System class."""
def __init__(self, username, password, cache,

View File

@@ -110,7 +110,7 @@ NotificationItem = namedtuple(
)
class AdsHub(object):
class AdsHub:
"""Representation of an ADS connection."""
def __init__(self, ads_client):

View File

@@ -121,7 +121,7 @@ def alarm_arm_custom_bypass(hass, code=None, entity_id=None):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
component = hass.data[DOMAIN] = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
yield from component.async_setup(config)
@@ -154,6 +154,16 @@ def async_setup(hass, config):
return True
async def async_setup_entry(hass, entry):
"""Setup a config entry."""
return await hass.data[DOMAIN].async_setup_entry(entry)
async def async_unload_entry(hass, entry):
"""Unload a config entry."""
return await hass.data[DOMAIN].async_unload_entry(entry)
# pylint: disable=no-self-use
class AlarmControlPanel(Entity):
"""An abstract class for alarm control devices."""
@@ -177,7 +187,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_disarm, code)
return self.hass.async_add_executor_job(self.alarm_disarm, code)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
@@ -188,7 +198,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_arm_home, code)
return self.hass.async_add_executor_job(self.alarm_arm_home, code)
def alarm_arm_away(self, code=None):
"""Send arm away command."""
@@ -199,7 +209,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_arm_away, code)
return self.hass.async_add_executor_job(self.alarm_arm_away, code)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
@@ -210,7 +220,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_arm_night, code)
return self.hass.async_add_executor_job(self.alarm_arm_night, code)
def alarm_trigger(self, code=None):
"""Send alarm trigger command."""
@@ -221,7 +231,7 @@ class AlarmControlPanel(Entity):
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(self.alarm_trigger, code)
return self.hass.async_add_executor_job(self.alarm_trigger, code)
def alarm_arm_custom_bypass(self, code=None):
"""Send arm custom bypass command."""
@@ -232,7 +242,8 @@ class AlarmControlPanel(Entity):
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)
return self.hass.async_add_executor_job(
self.alarm_arm_custom_bypass, code)
@property
def state_attributes(self):

View File

@@ -83,7 +83,7 @@ class AlarmDotCom(alarm.AlarmControlPanel):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
@@ -92,9 +92,9 @@ class AlarmDotCom(alarm.AlarmControlPanel):
"""Return the state of the device."""
if self._alarm.state.lower() == 'disarmed':
return STATE_ALARM_DISARMED
elif self._alarm.state.lower() == 'armed stay':
if self._alarm.state.lower() == 'armed stay':
return STATE_ALARM_ARMED_HOME
elif self._alarm.state.lower() == 'armed away':
if self._alarm.state.lower() == 'armed away':
return STATE_ALARM_ARMED_AWAY
return STATE_UNKNOWN

View File

@@ -4,15 +4,17 @@ Support for Arlo Alarm Control Panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.arlo/
"""
import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.components.alarm_control_panel import (
AlarmControlPanel, PLATFORM_SCHEMA)
from homeassistant.components.arlo import (DATA_ARLO, CONF_ATTRIBUTION)
from homeassistant.components.arlo import (
DATA_ARLO, CONF_ATTRIBUTION, SIGNAL_UPDATE_ARLO)
from homeassistant.const import (
ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED)
@@ -36,21 +38,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
@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 Arlo Alarm Control Panels."""
data = hass.data[DATA_ARLO]
arlo = hass.data[DATA_ARLO]
if not data.base_stations:
if not arlo.base_stations:
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:
for base_station in arlo.base_stations:
base_stations.append(ArloBaseStation(base_station, home_mode_name,
away_mode_name))
async_add_devices(base_stations, True)
add_devices(base_stations, True)
class ArloBaseStation(AlarmControlPanel):
@@ -68,6 +69,16 @@ class ArloBaseStation(AlarmControlPanel):
"""Return icon."""
return ICON
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_UPDATE_ARLO, self._update_callback)
@callback
def _update_callback(self):
"""Call update method."""
self.async_schedule_update_ha_state(True)
@property
def state(self):
"""Return the state of the device."""
@@ -75,30 +86,22 @@ class ArloBaseStation(AlarmControlPanel):
def update(self):
"""Update the state of the device."""
# PyArlo sometimes returns None for mode. So retry 3 times before
# returning None.
num_retries = 3
i = 0
while i < num_retries:
mode = self._base_station.mode
if mode:
self._state = self._get_state_from_mode(mode)
return
i += 1
self._state = None
_LOGGER.debug("Updating Arlo Alarm Control Panel %s", self.name)
mode = self._base_station.mode
if mode:
self._state = self._get_state_from_mode(mode)
else:
self._state = None
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
self._base_station.mode = DISARMED
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code=None):
"""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):
async def async_alarm_arm_home(self, code=None):
"""Send arm home command. Uses custom mode."""
self._base_station.mode = self._home_mode_name
@@ -119,10 +122,10 @@ class ArloBaseStation(AlarmControlPanel):
"""Convert Arlo mode to Home Assistant state."""
if mode == ARMED:
return STATE_ALARM_ARMED_AWAY
elif mode == DISARMED:
if mode == DISARMED:
return STATE_ALARM_DISARMED
elif mode == self._home_mode_name:
if mode == self._home_mode_name:
return STATE_ALARM_ARMED_HOME
elif mode == self._away_mode_name:
if mode == self._away_mode_name:
return STATE_ALARM_ARMED_AWAY
return None
return mode

View File

@@ -55,9 +55,9 @@ class CanaryAlarm(AlarmControlPanel):
mode = location.mode
if mode.name == LOCATION_MODE_AWAY:
return STATE_ALARM_ARMED_AWAY
elif mode.name == LOCATION_MODE_HOME:
if mode.name == LOCATION_MODE_HOME:
return STATE_ALARM_ARMED_HOME
elif mode.name == LOCATION_MODE_NIGHT:
if mode.name == LOCATION_MODE_NIGHT:
return STATE_ALARM_ARMED_NIGHT
return None

View File

@@ -5,7 +5,7 @@ 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.components.alarm_control_panel import manual
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,

View File

@@ -0,0 +1,84 @@
"""
Support for HomematicIP alarm control panel.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.homematicip_cloud/
"""
import logging
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED)
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.homematicip_cloud import (
HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN,
HMIPC_HAPID)
DEPENDENCIES = ['homematicip_cloud']
_LOGGER = logging.getLogger(__name__)
HMIP_ZONE_AWAY = 'EXTERNAL'
HMIP_ZONE_HOME = 'INTERNAL'
async def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the HomematicIP alarm control devices."""
pass
async def async_setup_entry(hass, config_entry, async_add_devices):
"""Set up the HomematicIP alarm control panel from a config entry."""
from homematicip.aio.group import AsyncSecurityZoneGroup
home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home
devices = []
for group in home.groups:
if isinstance(group, AsyncSecurityZoneGroup):
devices.append(HomematicipSecurityZone(home, group))
if devices:
async_add_devices(devices)
class HomematicipSecurityZone(HomematicipGenericDevice, AlarmControlPanel):
"""Representation of an HomematicIP security zone group."""
def __init__(self, home, device):
"""Initialize the security zone group."""
device.modelType = 'Group-SecurityZone'
device.windowState = ''
super().__init__(home, device)
@property
def state(self):
"""Return the state of the device."""
from homematicip.base.enums import WindowState
if self._device.active:
if (self._device.sabotage or self._device.motionDetected or
self._device.windowState == WindowState.OPEN):
return STATE_ALARM_TRIGGERED
active = self._home.get_security_zones_activation()
if active == (True, True):
return STATE_ALARM_ARMED_AWAY
if active == (False, True):
return STATE_ALARM_ARMED_HOME
return STATE_ALARM_DISARMED
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
await self._home.set_security_zones_activation(False, False)
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
await self._home.set_security_zones_activation(True, False)
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
await self._home.set_security_zones_activation(True, True)

View File

@@ -128,7 +128,7 @@ class IFTTTAlarmPanel(alarm.AlarmControlPanel):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'

View File

@@ -205,7 +205,7 @@ class ManualAlarm(alarm.AlarmControlPanel):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'

View File

@@ -19,7 +19,7 @@ from homeassistant.const import (
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.components.mqtt as mqtt
from homeassistant.components import mqtt
from homeassistant.helpers.event import async_track_state_change
from homeassistant.core import callback
@@ -241,7 +241,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'

View File

@@ -12,7 +12,7 @@ import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.mqtt as mqtt
from homeassistant.components import mqtt
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN,
@@ -20,7 +20,7 @@ from homeassistant.const import (
from homeassistant.components.mqtt import (
CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS,
MqttAvailability)
CONF_RETAIN, MqttAvailability)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
@@ -49,11 +49,15 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the MQTT Alarm Control Panel platform."""
if discovery_info is not None:
config = PLATFORM_SCHEMA(discovery_info)
async_add_devices([MqttAlarm(
config.get(CONF_NAME),
config.get(CONF_STATE_TOPIC),
config.get(CONF_COMMAND_TOPIC),
config.get(CONF_QOS),
config.get(CONF_RETAIN),
config.get(CONF_PAYLOAD_DISARM),
config.get(CONF_PAYLOAD_ARM_HOME),
config.get(CONF_PAYLOAD_ARM_AWAY),
@@ -66,9 +70,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
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, availability_topic,
payload_available, payload_not_available):
def __init__(self, name, state_topic, command_topic, qos, retain,
payload_disarm, 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)
@@ -77,6 +81,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
self._state_topic = state_topic
self._command_topic = command_topic
self._qos = qos
self._retain = retain
self._payload_disarm = payload_disarm
self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away
@@ -121,7 +126,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
@@ -134,7 +139,8 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
if not self._validate_code(code, 'disarming'):
return
mqtt.async_publish(
self.hass, self._command_topic, self._payload_disarm, self._qos)
self.hass, self._command_topic, self._payload_disarm, self._qos,
self._retain)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
@@ -145,7 +151,8 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
if not self._validate_code(code, 'arming home'):
return
mqtt.async_publish(
self.hass, self._command_topic, self._payload_arm_home, self._qos)
self.hass, self._command_topic, self._payload_arm_home, self._qos,
self._retain)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
@@ -156,7 +163,8 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
if not self._validate_code(code, 'arming away'):
return
mqtt.async_publish(
self.hass, self._command_topic, self._payload_arm_away, self._qos)
self.hass, self._command_topic, self._payload_arm_away, self._qos,
self._retain)
def _validate_code(self, code, state):
"""Validate given code."""

View File

@@ -9,23 +9,22 @@ import re
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA, AlarmControlPanel)
from homeassistant.const import (
CONF_CODE, CONF_NAME, CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['simplisafe-python==1.0.5']
REQUIREMENTS = ['simplisafe-python==2.0.2']
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'SimpliSafe'
DOMAIN = 'simplisafe'
NOTIFICATION_ID = 'simplisafe_notification'
NOTIFICATION_TITLE = 'SimpliSafe Setup'
ATTR_ALARM_ACTIVE = "alarm_active"
ATTR_TEMPERATURE = "temperature"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PASSWORD): cv.string,
@@ -37,36 +36,27 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the SimpliSafe platform."""
from simplipy.api import SimpliSafeApiInterface, get_systems
from simplipy.api import SimpliSafeApiInterface, SimpliSafeAPIException
name = config.get(CONF_NAME)
code = config.get(CONF_CODE)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
simplisafe = SimpliSafeApiInterface()
status = simplisafe.set_credentials(username, password)
if status:
hass.data[DOMAIN] = simplisafe
locations = get_systems(simplisafe)
for location in locations:
add_devices([SimpliSafeAlarm(location, name, code)])
else:
message = 'Failed to log into SimpliSafe. Check credentials.'
_LOGGER.error(message)
hass.components.persistent_notification.create(
message,
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
try:
simplisafe = SimpliSafeApiInterface(username, password)
except SimpliSafeAPIException:
_LOGGER.error("Failed to setup SimpliSafe")
return
def logout(event):
"""Logout of the SimpliSafe API."""
hass.data[DOMAIN].logout()
systems = []
hass.bus.listen(EVENT_HOMEASSISTANT_STOP, logout)
for system in simplisafe.get_systems():
systems.append(SimpliSafeAlarm(system, name, code))
add_devices(systems)
class SimpliSafeAlarm(alarm.AlarmControlPanel):
class SimpliSafeAlarm(AlarmControlPanel):
"""Representation of a SimpliSafe alarm."""
def __init__(self, simplisafe, name, code):
@@ -75,31 +65,37 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
self._name = name
self._code = str(code) if code else None
@property
def unique_id(self):
"""Return the unique ID."""
return self.simplisafe.location_id
@property
def name(self):
"""Return the name of the device."""
if self._name is not None:
return self._name
return 'Alarm {}'.format(self.simplisafe.location_id())
return 'Alarm {}'.format(self.simplisafe.location_id)
@property
def code_format(self):
"""Return one or more digits/characters."""
if self._code is None:
return None
elif isinstance(self._code, str) and re.search('^\\d+$', self._code):
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
return 'Any'
@property
def state(self):
"""Return the state of the device."""
status = self.simplisafe.state()
if status == 'off':
status = self.simplisafe.state
if status.lower() == 'off':
state = STATE_ALARM_DISARMED
elif status == 'home':
elif status.lower() == 'home' or status.lower() == 'home_count':
state = STATE_ALARM_ARMED_HOME
elif status == 'away':
elif (status.lower() == 'away' or status.lower() == 'exitDelay' or
status.lower() == 'away_count'):
state = STATE_ALARM_ARMED_AWAY
else:
state = STATE_UNKNOWN
@@ -108,14 +104,13 @@ class SimpliSafeAlarm(alarm.AlarmControlPanel):
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'alarm': self.simplisafe.alarm(),
'co': self.simplisafe.carbon_monoxide(),
'fire': self.simplisafe.fire(),
'flood': self.simplisafe.flood(),
'last_event': self.simplisafe.last_event(),
'temperature': self.simplisafe.temperature(),
}
attributes = {}
attributes[ATTR_ALARM_ACTIVE] = self.simplisafe.alarm_active
if self.simplisafe.temperature is not None:
attributes[ATTR_TEMPERATURE] = self.simplisafe.temperature
return attributes
def update(self):
"""Update alarm status."""

View File

@@ -34,6 +34,8 @@ CONF_ZONE_NAME = 'name'
CONF_ZONE_TYPE = 'type'
CONF_ZONE_RFID = 'rfid'
CONF_ZONES = 'zones'
CONF_RELAY_ADDR = 'relayaddr'
CONF_RELAY_CHAN = 'relaychan'
DEFAULT_DEVICE_TYPE = 'socket'
DEFAULT_DEVICE_HOST = 'localhost'
@@ -53,6 +55,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'
SIGNAL_REL_MESSAGE = 'alarmdecoder.rel_message'
DEVICE_SOCKET_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICE_TYPE): 'socket',
@@ -71,7 +74,11 @@ ZONE_SCHEMA = vol.Schema({
vol.Required(CONF_ZONE_NAME): cv.string,
vol.Optional(CONF_ZONE_TYPE,
default=DEFAULT_ZONE_TYPE): vol.Any(DEVICE_CLASSES_SCHEMA),
vol.Optional(CONF_ZONE_RFID): cv.string})
vol.Optional(CONF_ZONE_RFID): cv.string,
vol.Inclusive(CONF_RELAY_ADDR, 'relaylocation',
'Relay address and channel must exist together'): cv.byte,
vol.Inclusive(CONF_RELAY_CHAN, 'relaylocation',
'Relay address and channel must exist together'): cv.byte})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
@@ -153,6 +160,11 @@ def setup(hass, config):
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_ZONE_RESTORE, zone)
def handle_rel_message(sender, message):
"""Handle relay message from AlarmDecoder."""
hass.helpers.dispatcher.dispatcher_send(
SIGNAL_REL_MESSAGE, message)
controller = False
if device_type == 'socket':
host = device.get(CONF_DEVICE_HOST)
@@ -171,6 +183,7 @@ def setup(hass, config):
controller.on_zone_fault += zone_fault_callback
controller.on_zone_restore += zone_restore_callback
controller.on_close += handle_closed_connection
controller.on_relay_changed += handle_rel_message
hass.data[DATA_AD] = controller

View File

@@ -68,7 +68,7 @@ def turn_on(hass, entity_id):
def async_turn_on(hass, entity_id):
"""Async reset the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data))
@@ -81,7 +81,7 @@ def turn_off(hass, entity_id):
def async_turn_off(hass, entity_id):
"""Async acknowledge the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data))
@@ -94,7 +94,7 @@ def toggle(hass, entity_id):
def async_toggle(hass, entity_id):
"""Async toggle acknowledgement of alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_add_job(
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data))
@@ -217,7 +217,7 @@ class Alert(ToggleEntity):
else:
yield from self._schedule_notify()
self.hass.async_add_job(self.async_update_ha_state)
self.async_schedule_update_ha_state()
@asyncio.coroutine
def end_alerting(self):
@@ -228,7 +228,7 @@ class Alert(ToggleEntity):
self._firing = False
if self._done_message and self._send_done_message:
yield from self._notify_done_message()
self.hass.async_add_job(self.async_update_ha_state)
self.async_schedule_update_ha_state()
@asyncio.coroutine
def _schedule_notify(self):

View File

@@ -210,7 +210,7 @@ def resolve_slot_synonyms(key, request):
return resolved_value
class AlexaResponse(object):
class AlexaResponse:
"""Help generating the response for Alexa."""
def __init__(self, hass, intent_info):

View File

@@ -55,7 +55,7 @@ HANDLERS = Registry()
ENTITY_ADAPTERS = Registry()
class _DisplayCategory(object):
class _DisplayCategory:
"""Possible display categories for Discovery response.
https://developer.amazon.com/docs/device-apis/alexa-discovery.html#display-categories
@@ -107,7 +107,6 @@ class _DisplayCategory(object):
THERMOSTAT = "THERMOSTAT"
# Indicates the endpoint is a television.
# pylint: disable=invalid-name
TV = "TV"
@@ -154,7 +153,7 @@ class _UnsupportedProperty(Exception):
"""This entity does not support the requested Smart Home API property."""
class _AlexaEntity(object):
class _AlexaEntity:
"""An adaptation of an entity, expressed in Alexa's terms.
The API handlers should manipulate entities only through this interface.
@@ -209,7 +208,7 @@ class _AlexaEntity(object):
raise NotImplementedError
class _AlexaInterface(object):
class _AlexaInterface:
def __init__(self, entity):
self.entity = entity
@@ -271,11 +270,14 @@ class _AlexaInterface(object):
"""Return properties serialized for an API response."""
for prop in self.properties_supported():
prop_name = prop['name']
yield {
'name': prop_name,
'namespace': self.name(),
'value': self.get_property(prop_name),
}
# pylint: disable=assignment-from-no-return
prop_value = self.get_property(prop_name)
if prop_value is not None:
yield {
'name': prop_name,
'namespace': self.name(),
'value': prop_value,
}
class _AlexaPowerController(_AlexaInterface):
@@ -313,7 +315,7 @@ class _AlexaLockController(_AlexaInterface):
if self.entity.state == STATE_LOCKED:
return 'LOCKED'
elif self.entity.state == STATE_UNLOCKED:
if self.entity.state == STATE_UNLOCKED:
return 'UNLOCKED'
return 'JAMMED'
@@ -439,14 +441,17 @@ class _AlexaThermostatController(_AlexaInterface):
unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT]
temp = None
if name == 'targetSetpoint':
temp = self.entity.attributes.get(ATTR_TEMPERATURE)
temp = self.entity.attributes.get(climate.ATTR_TEMPERATURE)
elif name == 'lowerSetpoint':
temp = self.entity.attributes.get(climate.ATTR_TARGET_TEMP_LOW)
elif name == 'upperSetpoint':
temp = self.entity.attributes.get(climate.ATTR_TARGET_TEMP_HIGH)
if temp is None:
else:
raise _UnsupportedProperty(name)
if temp is None:
return None
return {
'value': float(temp),
'scale': API_TEMP_UNITS[unit],
@@ -610,7 +615,7 @@ class _SensorCapabilities(_AlexaEntity):
yield _AlexaTemperatureSensor(self.entity)
class _Cause(object):
class _Cause:
"""Possible causes for property changes.
https://developer.amazon.com/docs/smarthome/state-reporting-for-a-smart-home-skill.html#cause-object
@@ -1474,9 +1479,6 @@ async def async_api_set_thermostat_mode(hass, config, request, entity):
mode = mode if isinstance(mode, str) else mode['value']
operation_list = entity.attributes.get(climate.ATTR_OPERATION_LIST)
# Work around a pylint false positive due to
# https://github.com/PyCQA/pylint/issues/1830
# pylint: disable=stop-iteration-return
ha_mode = next(
(k for k, v in API_THERMOSTAT_MODES.items() if v == mode),
None

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['amcrest==1.2.2']
REQUIREMENTS = ['amcrest==1.2.3']
DEPENDENCIES = ['ffmpeg']
_LOGGER = logging.getLogger(__name__)
@@ -164,7 +164,7 @@ def setup(hass, config):
return True
class AmcrestDevice(object):
class AmcrestDevice:
"""Representation of a base Amcrest discovery device."""
def __init__(self, camera, name, authentication, ffmpeg_arguments,

View File

@@ -214,11 +214,11 @@ def async_setup(hass, config):
CONF_PASSWORD: password
})
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'camera', 'mjpeg', mjpeg_camera, config))
if sensors:
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'sensor', DOMAIN, {
CONF_NAME: name,
CONF_HOST: host,
@@ -226,7 +226,7 @@ def async_setup(hass, config):
}, config))
if switches:
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'switch', DOMAIN, {
CONF_NAME: name,
CONF_HOST: host,
@@ -234,7 +234,7 @@ def async_setup(hass, config):
}, config))
if motion:
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'binary_sensor', DOMAIN, {
CONF_HOST: host,
CONF_NAME: name,

View File

@@ -58,7 +58,7 @@ def setup(hass, config):
return True
class APCUPSdData(object):
class APCUPSdData:
"""Stores the data retrieved from APCUPSd.
For each entity to use, acts as the single point responsible for fetching

View File

@@ -81,7 +81,6 @@ class APIEventStream(HomeAssistantView):
async def get(self, request):
"""Provide a streaming interface for the event bus."""
# pylint: disable=no-self-use
hass = request.app['hass']
stop_obj = object()
to_write = asyncio.Queue(loop=hass.loop)
@@ -221,7 +220,8 @@ class APIEntityStateView(HomeAssistantView):
is_new_state = hass.states.get(entity_id) is None
# Write state
hass.states.async_set(entity_id, new_state, attributes, force_update)
hass.states.async_set(entity_id, new_state, attributes, force_update,
self.context(request))
# Read the state back for our response
status_code = HTTP_CREATED if is_new_state else 200
@@ -280,7 +280,8 @@ class APIEventView(HomeAssistantView):
event_data[key] = state
request.app['hass'].bus.async_fire(
event_type, event_data, ha.EventOrigin.remote)
event_type, event_data, ha.EventOrigin.remote,
self.context(request))
return self.json_message("Event {} fired.".format(event_type))
@@ -317,7 +318,8 @@ class APIDomainServicesView(HomeAssistantView):
"Data should be valid JSON.", HTTP_BAD_REQUEST)
with AsyncTrackStates(hass) as changed_states:
await hass.services.async_call(domain, service, data, True)
await hass.services.async_call(
domain, service, data, True, self.context(request))
return self.json(changed_states)

View File

@@ -45,7 +45,7 @@ NOTIFICATION_AUTH_TITLE = 'Apple TV Authentication'
NOTIFICATION_SCAN_ID = 'apple_tv_scan_notification'
NOTIFICATION_SCAN_TITLE = 'Apple TV Scan'
T = TypeVar('T')
T = TypeVar('T') # pylint: disable=invalid-name
# This version of ensure_list interprets an empty dict as no value
@@ -218,10 +218,10 @@ def _setup_atv(hass, atv_config):
ATTR_POWER: power
}
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'media_player', DOMAIN, atv_config))
hass.async_add_job(discovery.async_load_platform(
hass.async_create_task(discovery.async_load_platform(
hass, 'remote', DOMAIN, atv_config))

View File

@@ -62,7 +62,7 @@ def setup(hass, config):
return True
class ArduinoBoard(object):
class ArduinoBoard:
"""Representation of an Arduino board."""
def __init__(self, port):

View File

@@ -5,14 +5,18 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/arlo/
"""
import logging
from datetime import timedelta
import voluptuous as vol
from requests.exceptions import HTTPError, ConnectTimeout
from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, CONF_SCAN_INTERVAL)
from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.dispatcher import dispatcher_send
REQUIREMENTS = ['pyarlo==0.1.2']
REQUIREMENTS = ['pyarlo==0.2.0']
_LOGGER = logging.getLogger(__name__)
@@ -25,10 +29,16 @@ DOMAIN = 'arlo'
NOTIFICATION_ID = 'arlo_notification'
NOTIFICATION_TITLE = 'Arlo Component Setup'
SCAN_INTERVAL = timedelta(seconds=60)
SIGNAL_UPDATE_ARLO = "arlo_update"
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL):
cv.time_period,
}),
}, extra=vol.ALLOW_EXTRA)
@@ -38,6 +48,7 @@ def setup(hass, config):
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
scan_interval = conf.get(CONF_SCAN_INTERVAL)
try:
from pyarlo import PyArlo
@@ -45,7 +56,17 @@ def setup(hass, config):
arlo = PyArlo(username, password, preload=False)
if not arlo.is_connected:
return False
# assign refresh period to base station thread
arlo_base_station = next((
station for station in arlo.base_stations), None)
if arlo_base_station is None:
return False
arlo_base_station.refresh_rate = scan_interval.total_seconds()
hass.data[DATA_ARLO] = arlo
except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Netgear Arlo: %s", str(ex))
hass.components.persistent_notification.create(
@@ -55,4 +76,17 @@ def setup(hass, config):
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
def hub_refresh(event_time):
"""Call ArloHub to refresh information."""
_LOGGER.info("Updating Arlo Hub component")
hass.data[DATA_ARLO].update(update_cameras=True,
update_base_station=True)
dispatcher_send(hass, SIGNAL_UPDATE_ARLO)
# register service
hass.services.register(DOMAIN, 'update', hub_refresh)
# register scan interval for ArloHub
track_time_interval(hass, hub_refresh, scan_interval)
return True

View File

@@ -48,7 +48,7 @@ def setup(hass, config):
return True
class AsteriskData(object):
class AsteriskData:
"""Store Asterisk mailbox data."""
def __init__(self, hass, host, port, password):

View File

@@ -123,9 +123,9 @@ def setup_august(hass, config, api, authenticator):
discovery.load_platform(hass, component, DOMAIN, {}, config)
return True
elif state == AuthenticationState.BAD_PASSWORD:
if state == AuthenticationState.BAD_PASSWORD:
return False
elif state == AuthenticationState.REQUIRES_VALIDATION:
if state == AuthenticationState.REQUIRES_VALIDATION:
request_configuration(hass, config, api, authenticator)
return True

View File

@@ -1,62 +1,5 @@
"""Component to allow users to login and get tokens.
All requests will require passing in a valid client ID and secret via HTTP
Basic Auth.
# GET /auth/providers
Return a list of auth providers. Example:
[
{
"name": "Local",
"id": null,
"type": "local_provider",
}
]
# POST /auth/login_flow
Create a login flow. Will return the first step of the flow.
Pass in parameter 'handler' to specify the auth provider to use. Auth providers
are identified by type and id.
{
"handler": ["local_provider", null]
}
Return value will be a step in a data entry flow. See the docs for data entry
flow for details.
{
"data_schema": [
{"name": "username", "type": "string"},
{"name": "password", "type": "string"}
],
"errors": {},
"flow_id": "8f7e42faab604bcab7ac43c44ca34d58",
"handler": ["insecure_example", null],
"step_id": "init",
"type": "form"
}
# POST /auth/login_flow/{flow_id}
Progress the flow. Most flows will be 1 page, but could optionally add extra
login challenges, like TFA. Once the flow has finished, the returned step will
have type "create_entry" and "result" key will contain an authorization code.
{
"flow_id": "8f7e42faab604bcab7ac43c44ca34d58",
"handler": ["insecure_example", null],
"result": "411ee2f916e648d691e937ae9344681e",
"source": "user",
"title": "Example",
"type": "create_entry",
"version": 1
}
# POST /auth/token
This is an OAuth2 endpoint for granting tokens. We currently support the grant
@@ -104,21 +47,27 @@ a limited expiration.
"""
import logging
import uuid
from datetime import timedelta
import aiohttp.web
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.core import callback
from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, FlowManagerResourceView)
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components import websocket_api
from homeassistant.components.http.ban import log_invalid_auth
from homeassistant.components.http.data_validator import RequestDataValidator
from .client import verify_client
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import callback
from homeassistant.util import dt as dt_util
from . import indieauth
from . import login_flow
DOMAIN = 'auth'
DEPENDENCIES = ['http']
WS_TYPE_CURRENT_USER = 'auth/current_user'
SCHEMA_WS_CURRENT_USER = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_CURRENT_USER,
})
_LOGGER = logging.getLogger(__name__)
@@ -126,129 +75,58 @@ async def async_setup(hass, config):
"""Component to allow users to login."""
store_credentials, retrieve_credentials = _create_cred_store()
hass.http.register_view(AuthProvidersView)
hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow))
hass.http.register_view(
LoginFlowResourceView(hass.auth.login_flow, store_credentials))
hass.http.register_view(GrantTokenView(retrieve_credentials))
hass.http.register_view(LinkUserView(retrieve_credentials))
hass.components.websocket_api.async_register_command(
WS_TYPE_CURRENT_USER, websocket_current_user,
SCHEMA_WS_CURRENT_USER
)
await login_flow.async_setup(hass, store_credentials)
return True
class AuthProvidersView(HomeAssistantView):
"""View to get available auth providers."""
url = '/auth/providers'
name = 'api:auth:providers'
requires_auth = False
@verify_client
async def get(self, request, client):
"""Get available auth providers."""
return self.json([{
'name': provider.name,
'id': provider.id,
'type': provider.type,
} for provider in request.app['hass'].auth.async_auth_providers])
class LoginFlowIndexView(FlowManagerIndexView):
"""View to create a config flow."""
url = '/auth/login_flow'
name = 'api:auth:login_flow'
requires_auth = False
async def get(self, request):
"""Do not allow index of flows in progress."""
return aiohttp.web.Response(status=405)
# pylint: disable=arguments-differ
@verify_client
@RequestDataValidator(vol.Schema({
vol.Required('handler'): vol.Any(str, list),
vol.Required('redirect_uri'): str,
}))
async def post(self, request, client, data):
"""Create a new login flow."""
if data['redirect_uri'] not in client.redirect_uris:
return self.json_message('invalid redirect uri', )
# pylint: disable=no-value-for-parameter
return await super().post(request)
class LoginFlowResourceView(FlowManagerResourceView):
"""View to interact with the flow manager."""
url = '/auth/login_flow/{flow_id}'
name = 'api:auth:login_flow:resource'
requires_auth = False
def __init__(self, flow_mgr, store_credentials):
"""Initialize the login flow resource view."""
super().__init__(flow_mgr)
self._store_credentials = store_credentials
# pylint: disable=arguments-differ
async def get(self, request):
"""Do not allow getting status of a flow in progress."""
return self.json_message('Invalid flow specified', 404)
# pylint: disable=arguments-differ
@verify_client
@RequestDataValidator(vol.Schema(dict), allow_empty=True)
async def post(self, request, client, flow_id, data):
"""Handle progressing a login flow request."""
try:
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
except vol.Invalid:
return self.json_message('User input malformed', 400)
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
return self.json(self._prepare_result_json(result))
result.pop('data')
result['result'] = self._store_credentials(client.id, result['result'])
return self.json(result)
class GrantTokenView(HomeAssistantView):
"""View to grant tokens."""
url = '/auth/token'
name = 'api:auth:token'
requires_auth = False
cors_allowed = True
def __init__(self, retrieve_credentials):
"""Initialize the grant token view."""
self._retrieve_credentials = retrieve_credentials
@verify_client
async def post(self, request, client):
@log_invalid_auth
async def post(self, request):
"""Grant a token."""
hass = request.app['hass']
data = await request.post()
grant_type = data.get('grant_type')
if grant_type == 'authorization_code':
return await self._async_handle_auth_code(
hass, client.id, data)
return await self._async_handle_auth_code(hass, data)
elif grant_type == 'refresh_token':
return await self._async_handle_refresh_token(
hass, client.id, data)
if grant_type == 'refresh_token':
return await self._async_handle_refresh_token(hass, data)
return self.json({
'error': 'unsupported_grant_type',
}, status_code=400)
async def _async_handle_auth_code(self, hass, client_id, data):
async def _async_handle_auth_code(self, hass, data):
"""Handle authorization code request."""
client_id = data.get('client_id')
if client_id is None or not indieauth.verify_client_id(client_id):
return self.json({
'error': 'invalid_request',
'error_description': 'Invalid client id',
}, status_code=400)
code = data.get('code')
if code is None:
@@ -261,9 +139,17 @@ class GrantTokenView(HomeAssistantView):
if credentials is None:
return self.json({
'error': 'invalid_request',
'error_description': 'Invalid code',
}, status_code=400)
user = await hass.auth.async_get_or_create_user(credentials)
if not user.is_active:
return self.json({
'error': 'access_denied',
'error_description': 'User is not active',
}, status_code=403)
refresh_token = await hass.auth.async_create_refresh_token(user,
client_id)
access_token = hass.auth.async_create_access_token(refresh_token)
@@ -276,8 +162,15 @@ class GrantTokenView(HomeAssistantView):
int(refresh_token.access_token_expiration.total_seconds()),
})
async def _async_handle_refresh_token(self, hass, client_id, data):
async def _async_handle_refresh_token(self, hass, data):
"""Handle authorization code request."""
client_id = data.get('client_id')
if client_id is not None and not indieauth.verify_client_id(client_id):
return self.json({
'error': 'invalid_request',
'error_description': 'Invalid client id',
}, status_code=400)
token = data.get('refresh_token')
if token is None:
@@ -287,11 +180,16 @@ class GrantTokenView(HomeAssistantView):
refresh_token = await hass.auth.async_get_refresh_token(token)
if refresh_token is None or refresh_token.client_id != client_id:
if refresh_token is None:
return self.json({
'error': 'invalid_grant',
}, status_code=400)
if refresh_token.client_id != client_id:
return self.json({
'error': 'invalid_request',
}, status_code=400)
access_token = hass.auth.async_create_access_token(refresh_token)
return self.json({
@@ -340,12 +238,46 @@ def _create_cred_store():
def store_credentials(client_id, credentials):
"""Store credentials and return a code to retrieve it."""
code = uuid.uuid4().hex
temp_credentials[(client_id, code)] = credentials
temp_credentials[(client_id, code)] = (dt_util.utcnow(), credentials)
return code
@callback
def retrieve_credentials(client_id, code):
"""Retrieve credentials."""
return temp_credentials.pop((client_id, code), None)
key = (client_id, code)
if key not in temp_credentials:
return None
created, credentials = temp_credentials.pop(key)
# OAuth 4.2.1
# The authorization code MUST expire shortly after it is issued to
# mitigate the risk of leaks. A maximum authorization code lifetime of
# 10 minutes is RECOMMENDED.
if dt_util.utcnow() - created < timedelta(minutes=10):
return credentials
return None
return store_credentials, retrieve_credentials
@callback
def websocket_current_user(hass, connection, msg):
"""Return the current user."""
user = connection.request.get('hass_user')
if user is None:
connection.to_write.put_nowait(websocket_api.error_message(
msg['id'], 'no_user', 'Not authenticated as a user'))
return
connection.to_write.put_nowait(websocket_api.result_message(msg['id'], {
'id': user.id,
'name': user.name,
'is_owner': user.is_owner,
'credentials': [{'auth_provider_type': c.auth_provider_type,
'auth_provider_id': c.auth_provider_id}
for c in user.credentials]
}))

View File

@@ -1,79 +0,0 @@
"""Helpers to resolve client ID/secret."""
import base64
from functools import wraps
import hmac
import aiohttp.hdrs
def verify_client(method):
"""Decorator to verify client id/secret on requests."""
@wraps(method)
async def wrapper(view, request, *args, **kwargs):
"""Verify client id/secret before doing request."""
client = await _verify_client(request)
if client is None:
return view.json({
'error': 'invalid_client',
}, status_code=401)
return await method(
view, request, *args, **kwargs, client=client)
return wrapper
async def _verify_client(request):
"""Method to verify the client id/secret in consistent time.
By using a consistent time for looking up client id and comparing the
secret, we prevent attacks by malicious actors trying different client ids
and are able to derive from the time it takes to process the request if
they guessed the client id correctly.
"""
if aiohttp.hdrs.AUTHORIZATION not in request.headers:
return None
auth_type, auth_value = \
request.headers.get(aiohttp.hdrs.AUTHORIZATION).split(' ', 1)
if auth_type != 'Basic':
return None
decoded = base64.b64decode(auth_value).decode('utf-8')
try:
client_id, client_secret = decoded.split(':', 1)
except ValueError:
# If no ':' in decoded
client_id, client_secret = decoded, None
return await async_secure_get_client(
request.app['hass'], client_id, client_secret)
async def async_secure_get_client(hass, client_id, client_secret):
"""Get a client id/secret in consistent time."""
client = await hass.auth.async_get_client(client_id)
if client is None:
if client_secret is not None:
# Still do a compare so we run same time as if a client was found.
hmac.compare_digest(client_secret.encode('utf-8'),
client_secret.encode('utf-8'))
return None
if client.secret is None:
return client
elif client_secret is None:
# Still do a compare so we run same time as if a secret was passed.
hmac.compare_digest(client.secret.encode('utf-8'),
client.secret.encode('utf-8'))
return None
elif hmac.compare_digest(client_secret.encode('utf-8'),
client.secret.encode('utf-8')):
return client
return None

View File

@@ -0,0 +1,130 @@
"""Helpers to resolve client ID/secret."""
from ipaddress import ip_address, ip_network
from urllib.parse import urlparse
# IP addresses of loopback interfaces
ALLOWED_IPS = (
ip_address('127.0.0.1'),
ip_address('::1'),
)
# RFC1918 - Address allocation for Private Internets
ALLOWED_NETWORKS = (
ip_network('10.0.0.0/8'),
ip_network('172.16.0.0/12'),
ip_network('192.168.0.0/16'),
)
def verify_redirect_uri(client_id, redirect_uri):
"""Verify that the client and redirect uri match."""
try:
client_id_parts = _parse_client_id(client_id)
except ValueError:
return False
redirect_parts = _parse_url(redirect_uri)
# IndieAuth 4.2.2 allows for redirect_uri to be on different domain
# but needs to be specified in link tag when fetching `client_id`.
# This is not implemented.
# Verify redirect url and client url have same scheme and domain.
return (
client_id_parts.scheme == redirect_parts.scheme and
client_id_parts.netloc == redirect_parts.netloc
)
def verify_client_id(client_id):
"""Verify that the client id is valid."""
try:
_parse_client_id(client_id)
return True
except ValueError:
return False
def _parse_url(url):
"""Parse a url in parts and canonicalize according to IndieAuth."""
parts = urlparse(url)
# Canonicalize a url according to IndieAuth 3.2.
# SHOULD convert the hostname to lowercase
parts = parts._replace(netloc=parts.netloc.lower())
# If a URL with no path component is ever encountered,
# it MUST be treated as if it had the path /.
if parts.path == '':
parts = parts._replace(path='/')
return parts
def _parse_client_id(client_id):
"""Test if client id is a valid URL according to IndieAuth section 3.2.
https://indieauth.spec.indieweb.org/#client-identifier
"""
parts = _parse_url(client_id)
# Client identifier URLs
# MUST have either an https or http scheme
if parts.scheme not in ('http', 'https'):
raise ValueError()
# MUST contain a path component
# Handled by url canonicalization.
# MUST NOT contain single-dot or double-dot path segments
if any(segment in ('.', '..') for segment in parts.path.split('/')):
raise ValueError(
'Client ID cannot contain single-dot or double-dot path segments')
# MUST NOT contain a fragment component
if parts.fragment != '':
raise ValueError('Client ID cannot contain a fragment')
# MUST NOT contain a username or password component
if parts.username is not None:
raise ValueError('Client ID cannot contain username')
if parts.password is not None:
raise ValueError('Client ID cannot contain password')
# MAY contain a port
try:
# parts raises ValueError when port cannot be parsed as int
parts.port
except ValueError:
raise ValueError('Client ID contains invalid port')
# Additionally, hostnames
# MUST be domain names or a loopback interface and
# MUST NOT be IPv4 or IPv6 addresses except for IPv4 127.0.0.1
# or IPv6 [::1]
# We are not goint to follow the spec here. We are going to allow
# any internal network IP to be used inside a client id.
address = None
try:
netloc = parts.netloc
# Strip the [, ] from ipv6 addresses before parsing
if netloc[0] == '[' and netloc[-1] == ']':
netloc = netloc[1:-1]
address = ip_address(netloc)
except ValueError:
# Not an ip address
pass
if (address is None or
address in ALLOWED_IPS or
any(address in network for network in ALLOWED_NETWORKS)):
return parts
raise ValueError('Hostname should be a domain name or local IP address')

View File

@@ -0,0 +1,172 @@
"""HTTP views handle login flow.
# GET /auth/providers
Return a list of auth providers. Example:
[
{
"name": "Local",
"id": null,
"type": "local_provider",
}
]
# POST /auth/login_flow
Create a login flow. Will return the first step of the flow.
Pass in parameter 'client_id' and 'redirect_url' validate by indieauth.
Pass in parameter 'handler' to specify the auth provider to use. Auth providers
are identified by type and id.
{
"client_id": "https://hassbian.local:8123/",
"handler": ["local_provider", null],
"redirect_url": "https://hassbian.local:8123/"
}
Return value will be a step in a data entry flow. See the docs for data entry
flow for details.
{
"data_schema": [
{"name": "username", "type": "string"},
{"name": "password", "type": "string"}
],
"errors": {},
"flow_id": "8f7e42faab604bcab7ac43c44ca34d58",
"handler": ["insecure_example", null],
"step_id": "init",
"type": "form"
}
# POST /auth/login_flow/{flow_id}
Progress the flow. Most flows will be 1 page, but could optionally add extra
login challenges, like TFA. Once the flow has finished, the returned step will
have type "create_entry" and "result" key will contain an authorization code.
{
"flow_id": "8f7e42faab604bcab7ac43c44ca34d58",
"handler": ["insecure_example", null],
"result": "411ee2f916e648d691e937ae9344681e",
"source": "user",
"title": "Example",
"type": "create_entry",
"version": 1
}
"""
import aiohttp.web
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.http.ban import process_wrong_login, \
log_invalid_auth
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, FlowManagerResourceView)
from . import indieauth
async def async_setup(hass, store_credentials):
"""Component to allow users to login."""
hass.http.register_view(AuthProvidersView)
hass.http.register_view(LoginFlowIndexView(hass.auth.login_flow))
hass.http.register_view(
LoginFlowResourceView(hass.auth.login_flow, store_credentials))
class AuthProvidersView(HomeAssistantView):
"""View to get available auth providers."""
url = '/auth/providers'
name = 'api:auth:providers'
requires_auth = False
async def get(self, request):
"""Get available auth providers."""
return self.json([{
'name': provider.name,
'id': provider.id,
'type': provider.type,
} for provider in request.app['hass'].auth.auth_providers])
class LoginFlowIndexView(FlowManagerIndexView):
"""View to create a config flow."""
url = '/auth/login_flow'
name = 'api:auth:login_flow'
requires_auth = False
async def get(self, request):
"""Do not allow index of flows in progress."""
return aiohttp.web.Response(status=405)
@RequestDataValidator(vol.Schema({
vol.Required('client_id'): str,
vol.Required('handler'): vol.Any(str, list),
vol.Required('redirect_uri'): str,
}))
@log_invalid_auth
async def post(self, request, data):
"""Create a new login flow."""
if not indieauth.verify_redirect_uri(data['client_id'],
data['redirect_uri']):
return self.json_message('invalid client id or redirect uri', 400)
# pylint: disable=no-value-for-parameter
return await super().post(request)
class LoginFlowResourceView(FlowManagerResourceView):
"""View to interact with the flow manager."""
url = '/auth/login_flow/{flow_id}'
name = 'api:auth:login_flow:resource'
requires_auth = False
def __init__(self, flow_mgr, store_credentials):
"""Initialize the login flow resource view."""
super().__init__(flow_mgr)
self._store_credentials = store_credentials
async def get(self, request, flow_id):
"""Do not allow getting status of a flow in progress."""
return self.json_message('Invalid flow specified', 404)
@RequestDataValidator(vol.Schema({
'client_id': str
}, extra=vol.ALLOW_EXTRA))
@log_invalid_auth
async def post(self, request, flow_id, data):
"""Handle progressing a login flow request."""
client_id = data.pop('client_id')
if not indieauth.verify_client_id(client_id):
return self.json_message('Invalid client id', 400)
try:
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
except vol.Invalid:
return self.json_message('User input malformed', 400)
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
# @log_invalid_auth does not work here since it returns HTTP 200
# need manually log failed login attempts
if result['errors'] is not None and \
result['errors'].get('base') == 'invalid_auth':
await process_wrong_login(request)
return self.json(self._prepare_result_json(result))
result.pop('data')
result['result'] = self._store_credentials(client_id, result['result'])
return self.json(result)

View File

@@ -297,7 +297,7 @@ class AutomationEntity(ToggleEntity):
return
# HomeAssistant is starting up
elif self.hass.state == CoreState.not_running:
if self.hass.state == CoreState.not_running:
@asyncio.coroutine
def async_enable_automation(event):
"""Start automation on startup."""

View File

@@ -44,7 +44,7 @@ def async_trigger(hass, config, action):
# Automation are enabled while hass is starting up, fire right away
# Check state because a config reload shouldn't trigger it.
elif hass.state == CoreState.starting:
if hass.state == CoreState.starting:
hass.async_run_job(action, {
'trigger': {
'platform': 'homeassistant',

View File

@@ -10,7 +10,7 @@ import json
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.mqtt as mqtt
from homeassistant.components import mqtt
from homeassistant.const import (CONF_PLATFORM, CONF_PAYLOAD)
import homeassistant.helpers.config_validation as cv

View File

@@ -145,7 +145,7 @@ def request_configuration(hass, config, name, host, serialnumber):
def setup(hass, config):
"""Set up for Axis devices."""
def _shutdown(call): # pylint: disable=unused-argument
def _shutdown(call):
"""Stop the event stream on shutdown."""
for serialnumber, device in AXIS_DEVICES.items():
_LOGGER.info("Stopping event stream for %s.", serialnumber)
@@ -272,8 +272,7 @@ class AxisDeviceEvent(Entity):
def _update_callback(self):
"""Update the sensor's state, if needed."""
self.update()
self.schedule_update_ha_state()
self.schedule_update_ha_state(True)
@property
def name(self):

View File

@@ -16,11 +16,10 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = 'bbb_gpio'
# pylint: disable=no-member
def setup(hass, config):
"""Set up the BeagleBone Black GPIO component."""
# pylint: disable=import-error
import Adafruit_BBIO.GPIO as GPIO
from Adafruit_BBIO import GPIO
def cleanup_gpio(event):
"""Stuff to do before stopping."""
@@ -34,41 +33,39 @@ def setup(hass, config):
return True
# noqa: F821
def setup_output(pin):
"""Set up a GPIO as output."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
# pylint: disable=import-error
from Adafruit_BBIO import GPIO
GPIO.setup(pin, GPIO.OUT)
def setup_input(pin, pull_mode):
"""Set up a GPIO as input."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
GPIO.setup(pin, GPIO.IN, # noqa: F821
GPIO.PUD_DOWN if pull_mode == 'DOWN' # noqa: F821
else GPIO.PUD_UP) # noqa: F821
# pylint: disable=import-error
from Adafruit_BBIO import GPIO
GPIO.setup(pin, GPIO.IN,
GPIO.PUD_DOWN if pull_mode == 'DOWN'
else GPIO.PUD_UP)
def write_output(pin, value):
"""Write a value to a GPIO."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
# pylint: disable=import-error
from Adafruit_BBIO import GPIO
GPIO.output(pin, value)
def read_input(pin):
"""Read a value from a GPIO."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
# pylint: disable=import-error
from Adafruit_BBIO import GPIO
return GPIO.input(pin) is GPIO.HIGH
def edge_detect(pin, event_callback, bounce):
"""Add detection for RISING and FALLING events."""
# pylint: disable=import-error,undefined-variable
import Adafruit_BBIO.GPIO as GPIO
# pylint: disable=import-error
from Adafruit_BBIO import GPIO
GPIO.add_event_detect(
pin, GPIO.BOTH, callback=event_callback, bouncetime=bounce)

View File

@@ -67,7 +67,6 @@ async def async_unload_entry(hass, entry):
return await hass.data[DOMAIN].async_unload_entry(entry)
# pylint: disable=no-self-use
class BinarySensorDevice(Entity):
"""Represent a binary sensor."""

View File

@@ -11,7 +11,8 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
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)
SIGNAL_RFX_MESSAGE, SIGNAL_REL_MESSAGE, CONF_RELAY_ADDR,
CONF_RELAY_CHAN)
DEPENDENCIES = ['alarmdecoder']
@@ -37,8 +38,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
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)
relay_addr = device_config_data.get(CONF_RELAY_ADDR)
relay_chan = device_config_data.get(CONF_RELAY_CHAN)
device = AlarmDecoderBinarySensor(
zone_num, zone_name, zone_type, zone_rfid)
zone_num, zone_name, zone_type, zone_rfid, relay_addr, relay_chan)
devices.append(device)
add_devices(devices)
@@ -49,7 +52,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class AlarmDecoderBinarySensor(BinarySensorDevice):
"""Representation of an AlarmDecoder binary sensor."""
def __init__(self, zone_number, zone_name, zone_type, zone_rfid):
def __init__(self, zone_number, zone_name, zone_type, zone_rfid,
relay_addr, relay_chan):
"""Initialize the binary_sensor."""
self._zone_number = zone_number
self._zone_type = zone_type
@@ -57,6 +61,8 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
self._name = zone_name
self._rfid = zone_rfid
self._rfstate = None
self._relay_addr = relay_addr
self._relay_chan = relay_chan
@asyncio.coroutine
def async_added_to_hass(self):
@@ -70,6 +76,9 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_RFX_MESSAGE, self._rfx_message_callback)
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_REL_MESSAGE, self._rel_message_callback)
@property
def name(self):
"""Return the name of the entity."""
@@ -122,3 +131,12 @@ class AlarmDecoderBinarySensor(BinarySensorDevice):
if self._rfid and message and message.serial_number == self._rfid:
self._rfstate = message.value
self.schedule_update_ha_state()
def _rel_message_callback(self, message):
"""Update relay state."""
if (self._relay_addr == message.address and
self._relay_chan == message.channel):
_LOGGER.debug("Relay %d:%d value:%d", message.address,
message.channel, message.value)
self._state = message.value
self.schedule_update_ha_state()

View File

@@ -89,7 +89,7 @@ class ArestBinarySensor(BinarySensorDevice):
self.arest.update()
class ArestData(object):
class ArestData:
"""Class for handling the data retrieval for pins."""
def __init__(self, resource, pin):

View File

@@ -99,7 +99,7 @@ class AuroraSensor(BinarySensorDevice):
self.aurora_data.update()
class AuroraData(object):
class AuroraData:
"""Get aurora forecast."""
def __init__(self, latitude, longitude, threshold):

View File

@@ -8,7 +8,7 @@ import logging
import voluptuous as vol
import homeassistant.components.bbb_gpio as bbb_gpio
from homeassistant.components import bbb_gpio
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import (DEVICE_DEFAULT_NAME, CONF_NAME)

View File

@@ -124,11 +124,11 @@ class BMWConnectedDriveSensor(BinarySensorDevice):
result['check_control_messages'] = check_control_messages
elif self._attribute == 'charging_status':
result['charging_status'] = vehicle_state.charging_status.value
# pylint: disable=W0212
# pylint: disable=protected-access
result['last_charging_end_result'] = \
vehicle_state._attributes['lastChargingEndResult']
if self._attribute == 'connection_status':
# pylint: disable=W0212
# pylint: disable=protected-access
result['connection_status'] = \
vehicle_state._attributes['connectionStatus']
@@ -166,7 +166,7 @@ class BMWConnectedDriveSensor(BinarySensorDevice):
# device class plug: On means device is plugged in,
# Off means device is unplugged
if self._attribute == 'connection_status':
# pylint: disable=W0212
# pylint: disable=protected-access
self._state = (vehicle_state._attributes['connectionStatus'] ==
'CONNECTED')

View File

@@ -25,6 +25,9 @@ DEFAULT_PAYLOAD_OFF = 'OFF'
SCAN_INTERVAL = timedelta(seconds=60)
CONF_COMMAND_TIMEOUT = 'command_timeout'
DEFAULT_TIMEOUT = 15
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_COMMAND): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@@ -32,10 +35,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(
CONF_COMMAND_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Command line Binary Sensor."""
name = config.get(CONF_NAME)
@@ -44,9 +48,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
payload_on = config.get(CONF_PAYLOAD_ON)
device_class = config.get(CONF_DEVICE_CLASS)
value_template = config.get(CONF_VALUE_TEMPLATE)
command_timeout = config.get(CONF_COMMAND_TIMEOUT)
if value_template is not None:
value_template.hass = hass
data = CommandSensorData(hass, command)
data = CommandSensorData(hass, command, command_timeout)
add_devices([CommandBinarySensor(
hass, data, name, device_class, payload_on, payload_off,

View File

@@ -5,9 +5,9 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.deconz/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.deconz import (
CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID,
DATA_DECONZ_UNSUB)
from homeassistant.components.deconz.const import (
ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ,
DATA_DECONZ_ID, DATA_DECONZ_UNSUB)
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -62,7 +62,8 @@ class DeconzBinarySensor(BinarySensorDevice):
"""
if reason['state'] or \
'reachable' in reason['attr'] or \
'battery' in reason['attr']:
'battery' in reason['attr'] or \
'on' in reason['attr']:
self.async_schedule_update_ha_state()
@property
@@ -107,6 +108,8 @@ class DeconzBinarySensor(BinarySensorDevice):
attr = {}
if self._sensor.battery:
attr[ATTR_BATTERY_LEVEL] = self._sensor.battery
if self._sensor.on is not None:
attr[ATTR_ON] = self._sensor.on
if self._sensor.type in PRESENCE and self._sensor.dark is not None:
attr['dark'] = self._sensor.dark
attr[ATTR_DARK] = self._sensor.dark
return attr

View File

@@ -14,7 +14,8 @@ from homeassistant.components.binary_sensor import (
from homeassistant.components.digital_ocean import (
CONF_DROPLETS, ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME,
ATTR_FEATURES, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY,
ATTR_REGION, ATTR_VCPUS, DATA_DIGITAL_OCEAN)
ATTR_REGION, ATTR_VCPUS, CONF_ATTRIBUTION, DATA_DIGITAL_OCEAN)
from homeassistant.const import ATTR_ATTRIBUTION
_LOGGER = logging.getLogger(__name__)
@@ -75,6 +76,7 @@ class DigitalOceanBinarySensor(BinarySensorDevice):
def device_state_attributes(self):
"""Return the state attributes of the Digital Ocean droplet."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
ATTR_CREATED_AT: self.data.created_at,
ATTR_DROPLET_ID: self.data.id,
ATTR_DROPLET_NAME: self.data.name,

View File

@@ -5,7 +5,6 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.eight_sleep/
"""
import logging
import asyncio
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.eight_sleep import (
@@ -16,8 +15,8 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['eight_sleep']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the eight sleep binary sensor."""
if discovery_info is None:
return
@@ -63,7 +62,6 @@ class EightHeatSensor(EightSleepHeatEntity, BinarySensorDevice):
"""Return true if the binary sensor is on."""
return self._state
@asyncio.coroutine
def async_update(self):
async def async_update(self):
"""Retrieve latest state."""
self._state = self._usrobj.bed_presence

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
REQUIREMENTS = ['https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4']
REQUIREMENTS = ['pyflic-homeassistant==0.4.dev0']
_LOGGER = logging.getLogger(__name__)

View File

@@ -23,7 +23,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the GC100 devices."""
binary_sensors = []
@@ -40,7 +39,6 @@ class GC100BinarySensor(BinarySensorDevice):
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

View File

@@ -117,7 +117,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
add_entities(entities)
class HikvisionData(object):
class HikvisionData:
"""Hikvision device event stream object."""
def __init__(self, hass, url, port, name, username, password):

View File

@@ -9,8 +9,8 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.homematicip_cloud import (
HomematicipGenericDevice, DOMAIN as HOMEMATICIP_CLOUD_DOMAIN,
ATTR_HOME_ID)
HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN,
HMIPC_HAPID)
DEPENDENCIES = ['homematicip_cloud']
@@ -21,17 +21,18 @@ ATTR_EVENT_DELAY = 'event_delay'
ATTR_MOTION_DETECTED = 'motion_detected'
ATTR_ILLUMINATION = 'illumination'
HMIP_OPEN = 'open'
async def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the HomematicIP binary sensor devices."""
"""Set up the binary sensor devices."""
pass
async def async_setup_entry(hass, config_entry, async_add_devices):
"""Set up the HomematicIP binary sensor from a config entry."""
from homematicip.device import (ShutterContact, MotionDetectorIndoor)
if discovery_info is None:
return
home = hass.data[HOMEMATICIP_CLOUD_DOMAIN][discovery_info[ATTR_HOME_ID]]
home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home
devices = []
for device in home.devices:
if isinstance(device, ShutterContact):
@@ -58,11 +59,13 @@ class HomematicipShutterContact(HomematicipGenericDevice, BinarySensorDevice):
@property
def is_on(self):
"""Return true if the shutter contact is on/open."""
from homematicip.base.enums import WindowState
if self._device.sabotage:
return True
if self._device.windowState is None:
return None
return self._device.windowState.lower() == HMIP_OPEN
return self._device.windowState == WindowState.OPEN
class HomematicipMotionDetector(HomematicipGenericDevice, BinarySensorDevice):

View File

@@ -3,8 +3,6 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ihc/
"""
from xml.etree.ElementTree import Element
import voluptuous as vol
from homeassistant.components.binary_sensor import (
@@ -70,7 +68,7 @@ class IHCBinarySensor(IHCDevice, BinarySensorDevice):
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
sensor_type: str, inverting: bool,
product: Element = None) -> None:
product=None) -> None:
"""Initialize the IHC binary sensor."""
super().__init__(ihc_controller, name, ihc_id, info, product)
self._state = None

View File

@@ -17,7 +17,9 @@ _LOGGER = logging.getLogger(__name__)
SENSOR_TYPES = {'openClosedSensor': 'opening',
'motionSensor': 'motion',
'doorSensor': 'door',
'wetLeakSensor': 'moisture'}
'wetLeakSensor': 'moisture',
'lightSensor': 'light',
'batterySensor': 'battery'}
@asyncio.coroutine
@@ -54,4 +56,9 @@ class InsteonPLMBinarySensor(InsteonPLMEntity, BinarySensorDevice):
@property
def is_on(self):
"""Return the boolean response if the node is on."""
return bool(self._insteon_device_state.value)
on_val = bool(self._insteon_device_state.value)
if self._insteon_device_state.name == 'lightSensor':
return not on_val
return on_val

View File

@@ -101,7 +101,7 @@ class IssBinarySensor(BinarySensorDevice):
self.iss_data.update()
class IssData(object):
class IssData:
"""Get data from the ISS API."""
def __init__(self, latitude, longitude):

View File

@@ -8,7 +8,7 @@ https://home-assistant.io/components/binary_sensor.isy994/
import asyncio
import logging
from datetime import timedelta
from typing import Callable # noqa
from typing import Callable
from homeassistant.core import callback
from homeassistant.components.binary_sensor import BinarySensorDevice, DOMAIN
@@ -28,7 +28,6 @@ ISY_DEVICE_TYPES = {
}
# 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."""
@@ -56,7 +55,7 @@ def setup_platform(hass, config: ConfigType,
else:
device_type = _detect_device_type(node)
subnode_id = int(node.nid[-1])
if (device_type == 'opening' or device_type == 'moisture'):
if device_type in ('opening', 'moisture'):
# These sensors use an optional "negative" subnode 2 to snag
# all state changes
if subnode_id == 2:
@@ -299,7 +298,6 @@ class ISYBinarySensorHeartbeat(ISYDevice, BinarySensorDevice):
# No heartbeat timer is active
pass
# pylint: disable=unused-argument
@callback
def timer_elapsed(now) -> None:
"""Heartbeat missed; set state to indicate dead battery."""
@@ -314,7 +312,6 @@ class ISYBinarySensorHeartbeat(ISYDevice, BinarySensorDevice):
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.

View File

@@ -115,7 +115,6 @@ class KNXBinarySensor(BinarySensorDevice):
"""Register callbacks to update hass after device was changed."""
async def after_update_callback(device):
"""Call after device was updated."""
# pylint: disable=unused-argument
await self.async_update_ha_state()
self.device.register_device_updated_cb(after_update_callback)

View File

@@ -52,19 +52,18 @@ class LinodeBinarySensor(BinarySensorDevice):
self._node_id = node_id
self._state = None
self.data = None
self._attrs = {}
self._name = None
@property
def name(self):
"""Return the name of the sensor."""
if self.data is not None:
return self.data.label
return self._name
@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
return self._state
@property
def device_class(self):
@@ -74,8 +73,18 @@ class LinodeBinarySensor(BinarySensorDevice):
@property
def device_state_attributes(self):
"""Return the state attributes of the Linode Node."""
if self.data:
return {
return self._attrs
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
if self.data is not None:
self._state = self.data.status == 'running'
self._attrs = {
ATTR_CREATED: self.data.created,
ATTR_NODE_ID: self.data.id,
ATTR_NODE_NAME: self.data.label,
@@ -85,12 +94,4 @@ class LinodeBinarySensor(BinarySensorDevice):
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
self._name = self.data.label

View File

@@ -7,7 +7,7 @@ https://home-assistant.io/components/binary_sensor.modbus/
import logging
import voluptuous as vol
import homeassistant.components.modbus as modbus
from homeassistant.components import modbus
from homeassistant.const import CONF_NAME, CONF_SLAVE
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.helpers import config_validation as cv

View File

@@ -6,11 +6,12 @@ https://home-assistant.io/components/binary_sensor.mqtt/
"""
import asyncio
import logging
from typing import Optional
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.mqtt as mqtt
from homeassistant.components import mqtt
from homeassistant.components.binary_sensor import (
BinarySensorDevice, DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
@@ -24,7 +25,7 @@ import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'MQTT Binary sensor'
CONF_UNIQUE_ID = 'unique_id'
DEFAULT_PAYLOAD_OFF = 'OFF'
DEFAULT_PAYLOAD_ON = 'ON'
DEFAULT_FORCE_UPDATE = False
@@ -37,6 +38,9 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean,
# Integrations shouldn't never expose unique_id through configuration
# this here is an exception because MQTT is a msg transport, not a protocol
vol.Optional(CONF_UNIQUE_ID): cv.string,
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@@ -61,7 +65,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
config.get(CONF_PAYLOAD_OFF),
config.get(CONF_PAYLOAD_AVAILABLE),
config.get(CONF_PAYLOAD_NOT_AVAILABLE),
value_template
value_template,
config.get(CONF_UNIQUE_ID),
)])
@@ -70,7 +75,8 @@ class MqttBinarySensor(MqttAvailability, BinarySensorDevice):
def __init__(self, name, state_topic, availability_topic, device_class,
qos, force_update, payload_on, payload_off, payload_available,
payload_not_available, value_template):
payload_not_available, value_template,
unique_id: Optional[str]):
"""Initialize the MQTT binary sensor."""
super().__init__(availability_topic, qos, payload_available,
payload_not_available)
@@ -83,6 +89,7 @@ class MqttBinarySensor(MqttAvailability, BinarySensorDevice):
self._qos = qos
self._force_update = force_update
self._template = value_template
self._unique_id = unique_id
@asyncio.coroutine
def async_added_to_hass(self):
@@ -134,3 +141,8 @@ class MqttBinarySensor(MqttAvailability, BinarySensorDevice):
def force_update(self):
"""Force update."""
return self._force_update
@property
def unique_id(self):
"""Return a unique ID."""
return self._unique_id

View File

@@ -29,7 +29,8 @@ async def async_setup_platform(
async_add_devices=async_add_devices)
class MySensorsBinarySensor(mysensors.MySensorsEntity, BinarySensorDevice):
class MySensorsBinarySensor(
mysensors.device.MySensorsEntity, BinarySensorDevice):
"""Representation of a MySensors Binary Sensor child node."""
@property

View File

@@ -29,6 +29,7 @@ class MyStromView(HomeAssistantView):
url = '/api/mystrom'
name = 'api:mystrom'
supported_actions = ['single', 'double', 'long', 'touch']
def __init__(self, add_devices):
"""Initialize the myStrom URL endpoint."""
@@ -44,16 +45,18 @@ class MyStromView(HomeAssistantView):
@asyncio.coroutine
def _handle(self, hass, data):
"""Handle requests to the myStrom endpoint."""
button_action = list(data.keys())[0]
button_id = data[button_action]
entity_id = '{}.{}_{}'.format(DOMAIN, button_id, button_action)
button_action = next((
parameter for parameter in data
if parameter in self.supported_actions), None)
if button_action not in ['single', 'double', 'long', 'touch']:
if button_action is None:
_LOGGER.error(
"Received unidentified message from myStrom button: %s", data)
return ("Received unidentified message: {}".format(data),
HTTP_UNPROCESSABLE_ENTITY)
button_id = data[button_action]
entity_id = '{}.{}_{}'.format(DOMAIN, button_id, button_action)
if entity_id not in self.buttons:
_LOGGER.info("New myStrom button/action detected: %s/%s",
button_id, button_action)

View File

@@ -8,7 +8,8 @@ from itertools import chain
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.nest import DATA_NEST, NestSensorDevice
from homeassistant.components.nest import (
DATA_NEST, DATA_NEST_CONFIG, CONF_BINARY_SENSORS, NestSensorDevice)
from homeassistant.const import CONF_MONITORED_CONDITIONS
DEPENDENCIES = ['nest']
@@ -30,12 +31,10 @@ CAMERA_BINARY_TYPES = {
STRUCTURE_BINARY_TYPES = {
'away': None,
# 'security_state', # pending python-nest update
}
STRUCTURE_BINARY_STATE_MAP = {
'away': {'away': True, 'home': False},
'security_state': {'deter': True, 'ok': False},
}
_BINARY_TYPES_DEPRECATED = [
@@ -56,12 +55,19 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Nest binary sensors."""
if discovery_info is None:
return
"""Set up the Nest binary sensors.
No longer used.
"""
async def async_setup_entry(hass, entry, async_add_devices):
"""Set up a Nest binary sensor based on a config entry."""
nest = hass.data[DATA_NEST]
discovery_info = \
hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_BINARY_SENSORS, {})
# Add all available binary sensors if no Nest binary sensor config is set
if discovery_info == {}:
conditions = _VALID_BINARY_SENSOR_TYPES
@@ -76,32 +82,37 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"for valid options.")
_LOGGER.error(wstr)
sensors = []
for structure in nest.structures():
sensors += [NestBinarySensor(structure, None, variable)
for variable in conditions
if variable in STRUCTURE_BINARY_TYPES]
device_chain = chain(nest.thermostats(),
nest.smoke_co_alarms(),
nest.cameras())
for structure, device in device_chain:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in BINARY_TYPES]
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CLIMATE_BINARY_TYPES
and device.is_thermostat]
if device.is_camera:
def get_binary_sensors():
"""Get the Nest binary sensors."""
sensors = []
for structure in nest.structures():
sensors += [NestBinarySensor(structure, None, variable)
for variable in conditions
if variable in STRUCTURE_BINARY_TYPES]
device_chain = chain(nest.thermostats(),
nest.smoke_co_alarms(),
nest.cameras())
for structure, device in device_chain:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CAMERA_BINARY_TYPES]
for activity_zone in device.activity_zones:
sensors += [NestActivityZoneSensor(structure,
device,
activity_zone)]
add_devices(sensors, True)
if variable in BINARY_TYPES]
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CLIMATE_BINARY_TYPES
and device.is_thermostat]
if device.is_camera:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conditions
if variable in CAMERA_BINARY_TYPES]
for activity_zone in device.activity_zones:
sensors += [NestActivityZoneSensor(structure,
device,
activity_zone)]
return sensors
async_add_devices(await hass.async_add_job(get_binary_sensors), True)
class NestBinarySensor(NestSensorDevice, BinarySensorDevice):
@@ -122,7 +133,7 @@ class NestBinarySensor(NestSensorDevice, BinarySensorDevice):
value = getattr(self.device, self.variable)
if self.variable in STRUCTURE_BINARY_TYPES:
self._state = bool(STRUCTURE_BINARY_STATE_MAP
[self.variable][value])
[self.variable].get(value))
else:
self._state = bool(value)

View File

@@ -57,7 +57,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the access to Netatmo binary sensor."""
netatmo = hass.components.netatmo
@@ -68,12 +67,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
module_name = None
import lnetatmo
import pyatmo
try:
data = CameraData(netatmo.NETATMO_AUTH, home)
if not data.get_camera_names():
return None
except lnetatmo.NoDevice:
except pyatmo.NoDevice:
return None
welcome_sensors = config.get(
@@ -143,7 +142,7 @@ class NetatmoBinarySensor(BinarySensorDevice):
"""Return the class of this sensor, from DEVICE_CLASSES."""
if self._cameratype == 'NACamera':
return WELCOME_SENSOR_TYPES.get(self._sensor_name)
elif self._cameratype == 'NOC':
if self._cameratype == 'NOC':
return PRESENCE_SENSOR_TYPES.get(self._sensor_name)
return TAG_SENSOR_TYPES.get(self._sensor_name)

View File

@@ -33,7 +33,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the available OctoPrint binary sensors."""
octoprint_api = hass.data[DOMAIN]["api"]

View File

@@ -44,7 +44,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Pilight Binary Sensor."""
disarm = config.get(CONF_DISARM_AFTER_TRIGGER)

View File

@@ -96,7 +96,7 @@ class PingBinarySensor(BinarySensorDevice):
self.ping.update()
class PingData(object):
class PingData:
"""The Class for handling the data retrieval."""
def __init__(self, host, count):

View File

@@ -0,0 +1,126 @@
"""
Integration with the Rachio Iro sprinkler system controller.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.rachio/
"""
from abc import abstractmethod
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.rachio import (DOMAIN as DOMAIN_RACHIO,
KEY_DEVICE_ID,
KEY_STATUS,
KEY_SUBTYPE,
SIGNAL_RACHIO_CONTROLLER_UPDATE,
STATUS_OFFLINE,
STATUS_ONLINE,
SUBTYPE_OFFLINE,
SUBTYPE_ONLINE,)
from homeassistant.helpers.dispatcher import dispatcher_connect
DEPENDENCIES = ['rachio']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Rachio binary sensors."""
devices = []
for controller in hass.data[DOMAIN_RACHIO].controllers:
devices.append(RachioControllerOnlineBinarySensor(hass, controller))
add_devices(devices)
_LOGGER.info("%d Rachio binary sensor(s) added", len(devices))
class RachioControllerBinarySensor(BinarySensorDevice):
"""Represent a binary sensor that reflects a Rachio state."""
def __init__(self, hass, controller, poll=True):
"""Set up a new Rachio controller binary sensor."""
self._controller = controller
if poll:
self._state = self._poll_update()
else:
self._state = None
dispatcher_connect(hass, SIGNAL_RACHIO_CONTROLLER_UPDATE,
self._handle_any_update)
@property
def should_poll(self) -> bool:
"""Declare that this entity pushes its state to HA."""
return False
@property
def is_on(self) -> bool:
"""Return whether the sensor has a 'true' value."""
return self._state
def _handle_any_update(self, *args, **kwargs) -> None:
"""Determine whether an update event applies to this device."""
if args[0][KEY_DEVICE_ID] != self._controller.controller_id:
# For another device
return
# For this device
self._handle_update()
@abstractmethod
def _poll_update(self, data=None) -> bool:
"""Request the state from the API."""
pass
@abstractmethod
def _handle_update(self, *args, **kwargs) -> None:
"""Handle an update to the state of this sensor."""
pass
class RachioControllerOnlineBinarySensor(RachioControllerBinarySensor):
"""Represent a binary sensor that reflects if the controller is online."""
def __init__(self, hass, controller):
"""Set up a new Rachio controller online binary sensor."""
super().__init__(hass, controller, poll=False)
self._state = self._poll_update(controller.init_data)
@property
def name(self) -> str:
"""Return the name of this sensor including the controller name."""
return "{} online".format(self._controller.name)
@property
def device_class(self) -> str:
"""Return the class of this device, from component DEVICE_CLASSES."""
return 'connectivity'
@property
def icon(self) -> str:
"""Return the name of an icon for this sensor."""
return 'mdi:wifi-strength-4' if self.is_on\
else 'mdi:wifi-strength-off-outline'
def _poll_update(self, data=None) -> bool:
"""Request the state from the API."""
if data is None:
data = self._controller.rachio.device.get(
self._controller.controller_id)[1]
if data[KEY_STATUS] == STATUS_ONLINE:
return True
if data[KEY_STATUS] == STATUS_OFFLINE:
return False
_LOGGER.warning('"%s" reported in unknown state "%s"', self.name,
data[KEY_STATUS])
def _handle_update(self, *args, **kwargs) -> None:
"""Handle an update to the state of this sensor."""
if args[0][KEY_SUBTYPE] == SUBTYPE_ONLINE:
self._state = True
elif args[0][KEY_SUBTYPE] == SUBTYPE_OFFLINE:
self._state = False
self.schedule_update_ha_state()

View File

@@ -67,6 +67,6 @@ class RainCloudBinarySensor(RainCloudEntity, BinarySensorDevice):
"""Return the icon of this device."""
if self._sensor_type == 'is_watering':
return 'mdi:water' if self.is_on else 'mdi:water-off'
elif self._sensor_type == 'status':
if self._sensor_type == 'status':
return 'mdi:pipe' if self.is_on else 'mdi:pipe-disconnected'
return ICON_MAP.get(self._sensor_type)

View File

@@ -8,7 +8,7 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.rainmachine import (
BINARY_SENSORS, DATA_RAINMACHINE, DATA_UPDATE_TOPIC, TYPE_FREEZE,
BINARY_SENSORS, DATA_RAINMACHINE, SENSOR_UPDATE_TOPIC, TYPE_FREEZE,
TYPE_FREEZE_PROTECTION, TYPE_HOT_DAYS, TYPE_HOURLY, TYPE_MONTH,
TYPE_RAINDELAY, TYPE_RAINSENSOR, TYPE_WEEKDAY, RainMachineEntity)
from homeassistant.const import CONF_MONITORED_CONDITIONS
@@ -20,7 +20,8 @@ DEPENDENCIES = ['rainmachine']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
async def async_setup_platform(
hass, config, async_add_devices, discovery_info=None):
"""Set up the RainMachine Switch platform."""
if discovery_info is None:
return
@@ -33,7 +34,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
binary_sensors.append(
RainMachineBinarySensor(rainmachine, sensor_type, name, icon))
add_devices(binary_sensors, True)
async_add_devices(binary_sensors, True)
class RainMachineBinarySensor(RainMachineEntity, BinarySensorDevice):
@@ -70,16 +71,16 @@ class RainMachineBinarySensor(RainMachineEntity, BinarySensorDevice):
self.rainmachine.device_mac.replace(':', ''), self._sensor_type)
@callback
def update_data(self):
def _update_data(self):
"""Update the state."""
self.async_schedule_update_ha_state(True)
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(self.hass, DATA_UPDATE_TOPIC,
self.update_data)
async_dispatcher_connect(
self.hass, SENSOR_UPDATE_TOPIC, self._update_data)
def update(self):
async def async_update(self):
"""Update the state."""
if self._sensor_type == TYPE_FREEZE:
self._state = self.rainmachine.restrictions['current']['freeze']

View File

@@ -42,7 +42,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the raspihats binary_sensor devices."""
I2CHatBinarySensor.I2C_HATS_MANAGER = hass.data[I2C_HATS_MANAGER]

View File

@@ -23,7 +23,7 @@ DEPENDENCIES = ['ring']
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=5)
SCAN_INTERVAL = timedelta(seconds=10)
# Sensor types: Name, category, device_class
SENSOR_TYPES = {

View File

@@ -8,7 +8,7 @@ import logging
import voluptuous as vol
import homeassistant.components.rpi_gpio as rpi_gpio
from homeassistant.components import rpi_gpio
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import DEVICE_DEFAULT_NAME
@@ -39,7 +39,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Raspberry PI GPIO devices."""
pull_mode = config.get(CONF_PULL_MODE)
@@ -59,7 +58,6 @@ class RPiGPIOBinarySensor(BinarySensorDevice):
def __init__(self, name, port, pull_mode, bouncetime, invert_logic):
"""Initialize the RPi binary sensor."""
# pylint: disable=no-member
self._name = name or DEVICE_DEFAULT_NAME
self._port = port
self._pull_mode = pull_mode

View File

@@ -10,7 +10,7 @@ import voluptuous as vol
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice)
import homeassistant.components.rpi_pfio as rpi_pfio
from homeassistant.components import rpi_pfio
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME
import homeassistant.helpers.config_validation as cv

View File

@@ -94,4 +94,4 @@ class SkybellBinarySensor(SkybellDevice, BinarySensorDevice):
self._state = bool(event and event.get('id') != self._event.get('id'))
self._event = event
self._event = event or {}

View File

@@ -0,0 +1,98 @@
"""
Support for Tahoma binary sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.tahoma/
"""
import logging
from datetime import timedelta
from homeassistant.components.binary_sensor import (
BinarySensorDevice)
from homeassistant.components.tahoma import (
DOMAIN as TAHOMA_DOMAIN, TahomaDevice)
from homeassistant.const import (STATE_OFF, STATE_ON, ATTR_BATTERY_LEVEL)
DEPENDENCIES = ['tahoma']
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=120)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Tahoma controller devices."""
_LOGGER.debug("Setup Tahoma Binary sensor platform")
controller = hass.data[TAHOMA_DOMAIN]['controller']
devices = []
for device in hass.data[TAHOMA_DOMAIN]['devices']['smoke']:
devices.append(TahomaBinarySensor(device, controller))
add_devices(devices, True)
class TahomaBinarySensor(TahomaDevice, BinarySensorDevice):
"""Representation of a Tahoma Binary Sensor."""
def __init__(self, tahoma_device, controller):
"""Initialize the sensor."""
super().__init__(tahoma_device, controller)
self._state = None
self._icon = None
self._battery = None
@property
def is_on(self):
"""Return the state of the sensor."""
return bool(self._state == STATE_ON)
@property
def device_class(self):
"""Return the class of the device."""
if self.tahoma_device.type == 'rtds:RTDSSmokeSensor':
return 'smoke'
return None
@property
def icon(self):
"""Icon for device by its type."""
return self._icon
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attr = {}
super_attr = super().device_state_attributes
if super_attr is not None:
attr.update(super_attr)
if self._battery is not None:
attr[ATTR_BATTERY_LEVEL] = self._battery
return attr
def update(self):
"""Update the state."""
self.controller.get_states([self.tahoma_device])
if self.tahoma_device.type == 'rtds:RTDSSmokeSensor':
if self.tahoma_device.active_states['core:SmokeState']\
== 'notDetected':
self._state = STATE_OFF
else:
self._state = STATE_ON
if 'core:SensorDefectState' in self.tahoma_device.active_states:
# Set to 'lowBattery' for low battery warning.
self._battery = self.tahoma_device.active_states[
'core:SensorDefectState']
else:
self._battery = None
if self._state == STATE_ON:
self._icon = "mdi:fire"
elif self._battery == 'lowBattery':
self._icon = "mdi:battery-alert"
else:
self._icon = None
_LOGGER.debug("Update %s, state: %s", self._name, self._state)

View File

@@ -63,7 +63,7 @@ class TapsAffSensor(BinarySensorDevice):
self.data.update()
class TapsAffData(object):
class TapsAffData:
"""Class for handling the data retrieval for pins."""
def __init__(self, location):

View File

@@ -129,9 +129,9 @@ class ThresholdSensor(BinarySensorDevice):
if self._threshold_lower is not None and \
self._threshold_upper is not None:
return TYPE_RANGE
elif self._threshold_lower is not None:
if self._threshold_lower is not None:
return TYPE_LOWER
elif self._threshold_upper is not None:
if self._threshold_upper is not None:
return TYPE_UPPER
@property

View File

@@ -23,7 +23,7 @@ from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.util import utcnow
REQUIREMENTS = ['numpy==1.14.3']
REQUIREMENTS = ['numpy==1.15.0']
_LOGGER = logging.getLogger(__name__)
@@ -57,7 +57,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the trend sensors."""
sensors = []

View File

@@ -0,0 +1,92 @@
"""
A platform that to monitor Uptime Robot monitors.
For more details about this platform, please refer to the documentation at
https://www.home-assistant.io/components/binary_sensor.uptimerobot/
"""
import logging
import voluptuous as vol
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyuptimerobot==0.0.5']
_LOGGER = logging.getLogger(__name__)
ATTR_TARGET = 'target'
CONF_ATTRIBUTION = "Data provided by Uptime Robot"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_API_KEY): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Uptime Robot binary_sensors."""
from pyuptimerobot import UptimeRobot
up_robot = UptimeRobot()
api_key = config.get(CONF_API_KEY)
monitors = up_robot.getMonitors(api_key)
devices = []
if not monitors or monitors.get('stat') != 'ok':
_LOGGER.error("Error connecting to Uptime Robot")
return
for monitor in monitors['monitors']:
devices.append(UptimeRobotBinarySensor(
api_key, up_robot, monitor['id'], monitor['friendly_name'],
monitor['url']))
add_devices(devices, True)
class UptimeRobotBinarySensor(BinarySensorDevice):
"""Representation of a Uptime Robot binary sensor."""
def __init__(self, api_key, up_robot, monitor_id, name, target):
"""Initialize Uptime Robot the binary sensor."""
self._api_key = api_key
self._monitor_id = str(monitor_id)
self._name = name
self._target = target
self._up_robot = up_robot
self._state = None
@property
def name(self):
"""Return the name of the binary sensor."""
return self._name
@property
def is_on(self):
"""Return the state of the binary sensor."""
return self._state
@property
def device_class(self):
"""Return the class of this device, from component DEVICE_CLASSES."""
return 'connectivity'
@property
def device_state_attributes(self):
"""Return the state attributes of the binary sensor."""
return {
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
ATTR_TARGET: self._target,
}
def update(self):
"""Get the latest state of the binary sensor."""
monitor = self._up_robot.getMonitors(self._api_key, self._monitor_id)
if not monitor or monitor.get('stat') != 'ok':
_LOGGER.warning("Failed to get new state")
return
status = monitor['monitors'][0]['status']
self._state = 1 if status == 2 else 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, hass.data[VERA_CONTROLLER])
for device in hass.data[VERA_DEVICES]['binary_sensor'])
[VeraBinarySensor(device, hass.data[VERA_CONTROLLER])
for device in hass.data[VERA_DEVICES]['binary_sensor']], True)
class VeraBinarySensor(VeraDevice, BinarySensorDevice):

View File

@@ -54,6 +54,7 @@ class VerisureDoorWindowSensor(BinarySensorDevice):
"$.doorWindow.doorWindowDevice[?(@.deviceLabel=='%s')]",
self._device_label) is not None
# pylint: disable=no-self-use
def update(self):
"""Update the state of the sensor."""
hub.update_overview()

View File

@@ -28,7 +28,7 @@ class VolvoSensor(VolvoEntity, BinarySensorDevice):
val = getattr(self.vehicle, self._attribute)
if self._attribute == 'bulb_failures':
return bool(val)
elif self._attribute in ['doors', 'windows']:
if self._attribute in ['doors', 'windows']:
return any([val[key] for key in val if 'Open' in key])
return val != 'Normal'

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