Compare commits

...

1225 Commits

Author SHA1 Message Date
Paulus Schoutsen
4750656f1a Bumped version to 0.81.0b1 2018-10-23 14:09:47 +02:00
Jaxom Nutt
011cc624b6 Bug fix for clicksend (#17713)
* Bug fix

Current version causes 500 error since it is sending an array of from numbers to ClickSend. Changing the from number to 'hass' identifies all messages as coming from Home Assistant making them more recognisable and removes the bug.

* Amendment

Changed it to use 'hass' as the default instead of defaulting to the recipient which is the array. Would have worked if users set their own name but users who were using the default were experiencing the issue.

* Added DEFAULT_SENDER variable
2018-10-23 14:09:41 +02:00
Anders Melchiorsen
2d9a964953 Update limitlessled to 1.1.3 (#17703) 2018-10-23 14:09:40 +02:00
Matt Snyder
87133a0e77 Update flux library version (#17677) 2018-10-23 14:09:40 +02:00
Nicko van Someren
fe8dec27a3 Fixed issue #16903 re exception with multiple simultanious writes (#17636)
Reworked tests/components/emulated_hue/test_init.py to not be
dependent on the specific internal implementation of util/jsonn.py
2018-10-23 14:09:39 +02:00
Bram Kragten
b5323cd894 Add lovelace websocket get and set card (#17600)
* Add ws get, set card

* lint+fix test

* Add test for set

* Added more tests, catch unsupported yaml constructors

Like !include will now give an error in the frontend.

* lint
2018-10-23 14:09:39 +02:00
Malte Franken
23316a8344 Geo location trigger added (#16967)
* zone trigger supports entity id pattern

* fixed lint error

* fixed test code

* initial version of new geo_location trigger

* revert to original

* simplified code and added tests

* refactored geo_location trigger to be based on a source defined by the entity

* amended test cases

* small refactorings
2018-10-23 14:09:38 +02:00
Paulus Schoutsen
86ecd7555a Update translations 2018-10-23 14:05:03 +02:00
Paulus Schoutsen
02f55b039c Update frontend to 20181023.0 2018-10-23 14:04:37 +02:00
Paulus Schoutsen
ebaf7f8c00 Bumped version to 0.81.0b0 2018-10-21 20:34:50 +02:00
Paulus Schoutsen
355005114b Update translations 2018-10-21 20:34:28 +02:00
Paulus Schoutsen
8f529b20d7 Bump frontend to 20181021.0 2018-10-21 20:34:12 +02:00
Richard Patel
b2faa67ab7 Add new rtorrent sensor (#17421)
* New rtorrent sensor

* Fix lint issue

* Fix another lint issue

* Fix pylint issue

* how many python linters do you guys use

* Cleanup code

* python linting

* newline
2018-10-21 20:12:51 +02:00
Luke Fritz
95371fe4a6 Bump pyarlo==0.2.2 (#17673)
* Bump pyarlo to 0.2.2, fixes #17427

* Increase log level for refresh message to clear up logs
2018-10-21 19:54:51 +02:00
Mathieu Velten
2d980f2a92 Update pynetgear to 0.5.0 (#17652) 2018-10-21 19:54:01 +02:00
Oscar Tin Lai
b6d3a199ce Add support for Dyson Hot+Cool Fan as a climate device (#14598)
* Added support for dyson hot+cool fan as climate device

* Removed decimal place in kelvin units conversion

Minor edits to be consistent with Dyson's internal conversion of temperature from kelvin to celsius. It does not include decimal place to convert between kelvin and celsius.

* made changes according to comments

* Refactored target temp logics, fixed enum issues

* changed name of component to entity

* removed temperature conversion for min/max property

* changed back to 644 permission

* added extra tests for almost-all coverage

* changed assert method to avoid lack of certain method in py35

* added test_setup_component

* shorten line length

* fixed mock spec and added checking of message listener is called

* added doc string and debug msg

* shorten line length

* removed pending target temp
2018-10-21 17:35:07 +02:00
Fabian Affolter
731753b604 Upgrade holidays to 0.9.8 (#17656) 2018-10-21 15:07:44 +02:00
Fabian Affolter
cf24687024 Upgrade async_timeout to 3.0.1 (#17655) 2018-10-21 14:13:30 +02:00
Daniel Høyer Iversen
ef93d48d50 available to switchmate (#17640) 2018-10-21 13:41:27 +02:00
emontnemery
9982867d66 Very minor cleanup of RFLink components (#17649) 2018-10-21 13:05:02 +02:00
Paulus Schoutsen
bdfd473aaa Reconnect if sub info comes in that is valid again (#17651) 2018-10-21 12:16:24 +02:00
Daniel Høyer Iversen
7e3d0f0700 Remove ryobi from .coveragerc (#17647) 2018-10-21 11:21:09 +02:00
guillaume1410
c03b137130 Removing ryobi gdo (#17637) 2018-10-21 08:08:35 +02:00
Raymon de Looff
85dbf1eed3 Upgrade dsmr_parser to 0.12 (#17634) 2018-10-20 22:07:47 +02:00
mvn23
237ac08076 Add opentherm_gw binary sensor support (#17625)
* Add OpenTherm Gateway binary sensor support.

* opentherm_gw binary_sensor platform does not need polling.
2018-10-20 18:51:01 +02:00
Bob Clough
2e973c7572 Fix mqtt light brightness slider (#17075)
* Enable brightness slider for RGB

If we are using RGB with no brightness topic, the brighness slider
should still be visible, as we can scale the RGB amount to give us the
brightness.

* Output RGB scaled by brightness

If we are outputting to an RGB device, but do not have a dedicated
brightness topic set, when the brightness slider is changed, we should
output the current colour's HS, with the V coming from the brightness
slider.

* Brightness from RGB when we're not using a brightness topic

When we aren't using a brightness topic, set the brightness slider based
on the received value from an RGB -> HSV conversion.

* Test for new brightness state scaled by RGB

This adds a test to make sure the brightness stored in the state is
being computed correctly from the RGB value when a dedicated brightness
topic is not set.

* Changes from review

Fixes formatting of supported features flags, and checks HS colour
hasn't been set when operating in RGB-only mode

* Set optimistic brightness correctly in rgb mode

When we're using rgb mode to set the brightness, we want to set
optimistic brightness if:

we are running in optimistic mode
OR
the brightness state topic isn't set and we have a brightness command topic
OR
the rgb state topic isn't set and we don't have a brightness command topic

* Add test for turn_on in RGB brightness mode
2018-10-20 12:37:25 -04:00
Julien Debaru
e980d1b9fe Fix linky sensor login error (#17110)
* Fix linky sensor login error

* Make platform fail-safe

Adding following enhancements:
* Make sure the platform loads correctly by making the first API request in setup_platform.
* Close the session after each API call.
* Use timeout parameter everywhere.

* Fix Hound CI error: line too long.

* Update pylinky library

* Remove LinkyClient from update()
2018-10-20 15:30:10 +02:00
Robert Svensson
26b7c2de7e deCONZ - Add support for Xiaomi window covers (#17337)
Add support for Xiaomi window covers
2018-10-20 15:13:23 +02:00
Daniel Shokouhi
ab826b8fe2 Use cached robot serial for Neato update (#17633)
* Use cached robot serial for neato update

* Hound
2018-10-20 14:28:30 +02:00
cdce8p
a9a8cbbd10 Homekit component cleanup (#17627)
* hass.async_add_executor_job
* Fix accessories.run -> async_track_state_change
* Fixed media_player test
* Flags are now local vars
* consistent use of " and '
2018-10-20 00:14:05 +02:00
Glenn Waters
3655fefec2 Add Elk-M1 sensor platform (#17342)
* Initial sensor version.

* Add Elk-M1 sensor services. Initial version of services.yaml.

* Fix lint errors.

* Fix PR comments.

* fix PR comment

* Fix PR comment

* Fix PR comments

* fix
2018-10-19 23:41:04 +02:00
cdce8p
ff33cbd22f Add water_heater support to HomeKit (#17614)
* Homekit add support for water_heater
* Added tests
2018-10-19 21:04:05 +02:00
Nick Touran
e343f5521c Upgrade gstreamer-player to 1.1.2 (#17568)
* Upgrade gstreamer-player to 1.1.2

* Updated requirements for gstreamer-player properly.
2018-10-19 18:11:47 +02:00
Steven Looman
f7bc44955c Upgrade async_upnp_client to 0.12.7 (#17601) 2018-10-19 18:10:04 +02:00
Nick Horvath
df2c3cdded Bump thermoworks version to fix conflict from upstream pyrebase sseclient (#17620) 2018-10-19 17:48:46 +02:00
ehendrix23
f504e5ef61 Add doorsense sensor for August 3rd Gen Smart Lock Pro (#17299)
* Add doorsense sensor for August 3rd Gen Smart Lock Pro

Add a binary sensor to August for the August 3rd Gen Smart Lock Pro doorsense sensor.

This is a re-do from PR 17116 https://github.com/home-assistant/home-assistant/pull/17116 that I closed due to rebase issue on my end.

* Changed to use snjoetw provided code

Going through the py-august I found that snjoetw had provided updated versions for the august component (august.py and binary_sensor/august.py) to include DoorSense sensor.
Changed what I did to to what snjoetw provided instead as he split it into 2 classes; much cleaner I think.

I modified his coding with:
   Fixes that were done to the August component and not part of the coding snjoetw provided.
   Added the debug logging improvement I had done in the code.

Note, fix I committed earlier for lock atribute (lock/august.py) is thus still the same.

* Reverted change from add_device to add_entities

Missed an item when merging snjoetw's code with current. Fixed.

* Updated call from add_devices to add_entities as well

Updated the call from add_devices to add_entities.

* Fixed permissions on files

Fixed permissions on components/august.py and binary_snesor/august.py

* Changed if/else to if/continue

Changed logic so that if the door sensor state is unknown during initalization the debug log is written and then continue the loop instead of using if/else logic.

* Added available property for Door Sensor

Added the available property for the Door Sensor and setting it to False if a status unknown is received.

* Updated setting self._available

Changed line for setting self._available to what Martin provided. Much  more efficient to read. :-)
2018-10-19 09:37:02 +02:00
Thomas Lovén
8bf58e1df5 Revert "De-syncing binary_sensor.ping (#17056)" (#17606)
This reverts commit 11d5671ee0.
2018-10-19 09:29:48 +02:00
Thomas Lovén
90183bd682 Tuya light icon fix (#17605)
* Fix for tuya light icons going too bright

* Make sure other values aren't strings either
2018-10-19 09:17:19 +02:00
jjlawren
88ec73ed8f Add missing await for coroutine (#17609) 2018-10-19 09:10:41 +02:00
mvn23
7baffed7b7 Add sensor support to opentherm_gw (#17314)
* Add OpenTherm Gateway sensor platform.

* Add OTGW_ variables to list of supported sensors.

* Order imports.

* Add OpenTherm Gateway binary sensor support.

* Revert "Add OpenTherm Gateway binary sensor support."

This reverts commit 115acaa912.

* Import COMP_SENSOR from sensor component rather than defining it.

* Update opentherm_gw sensor platform docs url.

* Update dependency to v0.2b1
Old version had incorrect variable names for some of the sensors

* Update requirements_all.txt

* Address review findings.

* Update .coveragerc
2018-10-19 07:31:19 +02:00
Anders Melchiorsen
cc4d29d42a Fix flux switch update interval (#17458) 2018-10-18 23:04:22 +02:00
emontnemery
10c1378195 Add binary_sensor support to RFlink (#17146)
* Add binary_sensor support to RFlink

* Add support for aliases

* Fix review comments

* Refactor, add tests

* Review comments

* Review comments

* Review comments

* Review comments
2018-10-18 22:28:40 +02:00
Ville Skyttä
cf3a97ff3c Upgrade pytest to 3.9.1 (#17598) 2018-10-18 21:31:52 +02:00
Kevin Fronczak
222ba96b3e Bump blinkpy version to 0.10.1 (#17595) 2018-10-18 21:30:46 +02:00
Nikolay Vasilchuk
6239a523cc Fix: Xiaomi Plug state is set twice (#17482)
* Xiaomi Plug UI fix #17422

* Review
2018-10-18 20:28:43 +02:00
Malte Franken
7eb6e49df7 Fixing race condition in geo location platforms (#17581)
* fixed race condition where obsolete entities kept listening for dispatcher signals

* making tests python 3.5 compatible
2018-10-18 15:25:48 +02:00
Paulus Schoutsen
7e91c0dc83 Merge branch 'master' into dev 2018-10-18 14:59:14 +02:00
Paulus Schoutsen
b1a05e7605 Merge pull request #17578 from home-assistant/rc
0.80.3
2018-10-18 14:55:46 +02:00
Paulus Schoutsen
cd90bb4161 Bumped version to 0.80.3 2018-10-18 13:58:58 +02:00
Paulus Schoutsen
17d0fe02c7 Update snapcast to 2.0.9 (#17573) 2018-10-18 13:58:48 +02:00
Steven Looman
43b140be5e Upgrade async_upnp_client to 0.12.6 (#17560) 2018-10-18 13:58:48 +02:00
Steven Looman
9ded16ccc3 Update to async-upnp-client==0.12.5 (#17401) 2018-10-18 13:58:47 +02:00
Marcel Hoppe
91dc0c3731 update hangups to 0.4.6 and fix Issue #16593 hangouts reconnects. (#17518) 2018-10-18 13:57:04 +02:00
Paulus Schoutsen
6e4a99cec0 Bump frontend to 20181018.0 2018-10-18 13:52:52 +02:00
Paulus Schoutsen
20bd14defb Bump frontend to 20181018.0 2018-10-18 13:52:38 +02:00
Paulus Schoutsen
20fa6c5383 Update snapcast to 2.0.9 (#17573) 2018-10-18 11:06:32 +02:00
Matthew Garrett
e2a1e21c8d Add support for LG soundbars (#17570)
* Add LG soundbar support

We can autodiscover these, so for now let's skip any local configuration.
Currently we expose volume, source and equaliser preset - we can expose the
other volume controls and options as well if necessary, but I don't know
whether it's worth it.

* Add discovery of LG devices

This is a generic discovery type that doesn't obviously contain enough
information to identify whether we're talking to a speaker system or any
other kind of device - on the other hand I haven't been able to find any
other LG devices that respond like this, so we can cross that bridge when
we get to it.

* Lint
2018-10-18 10:39:33 +02:00
Brian Gianforcaro
45878c6df0 Upgrade twilio package to version 6.19.1 (#17395) (#17424)
- Bump twilio requirement to latest 6.19.1 version

- The generic response type is gone in the latest
  versions of the twilio package. It appears we were
  generating an empty response just to get the empty
  xml body. TwilML is the new base class all responses
  inherit from. So I've switched the code over to using
  and empty TwilML object instead.

- The exception type was moved to a different location.
2018-10-18 09:17:55 +02:00
Daniel Høyer Iversen
3e5233d115 danielhiversen as mill codeowner (#17571) 2018-10-18 08:12:09 +02:00
Jorim Tielemans
b50c93ccb7 Validate ports as a port (#17549)
* Validate ports as port

Better than just a positive integer since it limits the range from 1 to 65535.

* Validate port for Axis

* Validate port for Xiaomi Home Camera

* Validate port for Modbus

* Validate port for Yamaha MusicCast Receivers

* Update zhong_hong.py

Validate port as a port, the gateway address as positive_int
Also moved the default values to their variable

* Validate port for the Asterisk Voicemail interface

* Fix lint

* Validate port for Xiaomi Cameras
2018-10-17 23:09:05 +02:00
Eduard van Valkenburg
50a66abd80 Updated package to fix #16960 (#17555) 2018-10-17 22:56:54 +02:00
Steven Looman
7106d9e9d4 Upgrade async_upnp_client to 0.12.6 (#17560) 2018-10-17 22:56:21 +02:00
ehendrix23
daf9d28565 Fix mold_indicator errors at startup (#17346)
* Initial changes to resolve issue 16733

Added logic to ensure that if the state is unknown during startup that the error about being unable to parse the value is not logged.
Further,  also ensured that if an attribute is set to None it does not try to convert the None value to Fahrenheit as that will cause an error.

* Cleaned up and added few comments

Cleaned up some lines based on flake8 and pylint.
Added some comment lines on the items added.

* Changed to async and using async_added_to_hass

Changed sensor to use async.
Registering state tracking for sensors and initial setup is now done upon the home assistant start event.

* Updated test and small fix

Updated test to handle unavailable state of sensor and return of None for attributes when data is unavailable.
Ensured that atributes are set to None when state is unavailable due to incorrect data.

* Fixed some flake8 issues in test

Fixed some flake8 issues in test_moldindicator.py.

* Updates based on review

Updates based on review from MartinHjelmare

* Added sensor entity_id to logger errors

Added sensor entity_id to logger error messages
Update test to use constant STATE_UNKNOWN instead of fixed string.
2018-10-17 18:42:39 +02:00
Bram Kragten
33860bf23c Adding id to lovelace cards in ui-lovelace.yaml (#17498)
* ID is added to cards without ID in ui-lovelace.yaml when loaded

* Hound

* Remove ui-lovelace.yaml

* Nicer get

* Update tests

* If YAML dump fails, config not gone

* Add tests

* Woof!

* Remove nosetests

* Address comments

* Woof...

* Delete test.yaml

* update rights to saved file

* fix

* line break
2018-10-17 16:31:06 +02:00
Paulus Schoutsen
fa196e5889 Merge pull request #17546 from home-assistant/rc
0.80.2
2018-10-17 14:56:58 +02:00
Paulus Schoutsen
9500bb1ac8 Bumped version to 0.80.2 2018-10-17 14:17:56 +02:00
Paulus Schoutsen
b3548f1ead Add another 3 days leeway to give time for payment processing times (#17542) 2018-10-17 14:17:47 +02:00
Kevin Fronczak
f74e976be1 Blink update - fixes #17316 (#17538)
* Updgrae blinkpy to 0.10.0

- Remove status sensor (API endpoint unreliable for this)
- Wifi strength reports in wifi bars rather than dBm (result of new API
endpoint)
- Added unique ids based on serial number

* Update requirements
2018-10-17 14:17:47 +02:00
Nikolay Vasilchuk
dc55718bc3 Fix: Connection pool of Request object is smaller than optimal value (8) (#17483) 2018-10-17 14:17:46 +02:00
Paulus Schoutsen
6551c53e32 Bump frontend to 20181017.0 2018-10-17 14:17:35 +02:00
Paulus Schoutsen
a27d49d022 Update translations 2018-10-17 14:17:33 +02:00
Paulus Schoutsen
95c43d634b Bump frontend to 20181017.0 2018-10-17 14:16:49 +02:00
Paulus Schoutsen
a5b9f5040f Update translations 2018-10-17 14:12:41 +02:00
cgtobi
e83a9aace4 Remove unnecessary call (#17514) 2018-10-17 10:53:05 +02:00
Anders Melchiorsen
1e4463957d Scan all network interfaces for LIFX bulbs (#17530) 2018-10-17 10:50:13 +02:00
Paulus Schoutsen
326787ef1a Add another 3 days leeway to give time for payment processing times (#17542) 2018-10-17 10:45:01 +02:00
Ville Skyttä
15f4ed74ac Tweak sensors comments in default config (#17526)
Makes it more clear that there should be only one sensors section, and
that the weather prediction comment applies only to the yr platform.
2018-10-17 09:58:41 +02:00
Ville Skyttä
b7b4224429 Huawei LTE sensor improvements (#17533)
* Sensor value formatting improvements

* Make default names more consistent with other sensors

* Improve unique id formatting
2018-10-17 09:00:15 +02:00
Kevin Fronczak
5088e7ee49 Blink update - fixes #17316 (#17538)
* Updgrae blinkpy to 0.10.0

- Remove status sensor (API endpoint unreliable for this)
- Wifi strength reports in wifi bars rather than dBm (result of new API
endpoint)
- Added unique ids based on serial number

* Update requirements
2018-10-17 08:38:03 +02:00
Nate Clark
11004bcf34 Manual IP & port configuration for Konnected devices (#17120)
* add capability for manually specifying IP and port of Konnected device(s)

* add config options for blink and discovery settings

* import konnected only in functions where needed

* updates from code review feedback

* convert manual_discovery to async

* code review updates; use correct sync vs async
2018-10-17 00:39:32 +02:00
Brant Knudson
83db673bd0 Add unique_id to Vera entities (#17450)
I believe this adds registry support. The UI allows me to change
the entity ID now.

For example, a light bulb called "BasementHallLight" in the Vera
has an initial Entity ID like light.basementhalllight_108, where
108 is the unique ID that the Vera assigned the device when I
added it to the z-wave network.

Now I can use the UI to change the Entity ID to
light.basementhalllight and I can still turn it on and off.
2018-10-16 23:27:57 +02:00
Marcel Hoppe
6b3e4ca7bd update hangups to 0.4.6 and fix Issue #16593 hangouts reconnects. (#17518) 2018-10-16 20:09:34 +02:00
Daniel Høyer Iversen
aa176312a5 Update switchmate library (#17519) 2018-10-16 20:08:34 +02:00
Daniel Høyer Iversen
6235aae196 Update mill library (#17520) 2018-10-16 20:08:04 +02:00
Ben Lebherz
764ea06795 Fix unhandled exception which creates many useless logs (#17508)
* Fix unhandled exception which creates many useless logs

* recover old component logic, sorry

* remove inline conditional
2018-10-16 16:41:38 +02:00
Charles Garwood
9c52a3ce22 Z-Wave Device Registry Support (#17291)
* Add device_registry support for sensor and switch domains

* Add device_registry support for light

* Add device registry to binary_sensor, climate, cover

* Add device registry to zwave fan

* Fix test for config entry loading

* lint

* revert erroneous modification

* Revert device_registry.py change
2018-10-16 14:58:25 +02:00
Tsvi Mostovicz
c6d9ceca63 Bump hdate version to 0.6.5 (#17510)
* Bump hdate version to 0.6.4

* Bump to 0.6.5

* Change test so we check when passing tzinfo object
2018-10-16 13:45:39 +02:00
ehendrix23
fee87cd6ed Add LogBook support to HomeKit (#17180) 2018-10-16 13:32:53 +02:00
William Scanlon
71ab8a9b1a Moved Wink water heater from climate to water heater. (#17504)
* Moved Wink water heater from climate to water heater.

* Remove deprecated states from Tuya

* Update toon.py

* Update toon.py

* Lint
2018-10-16 11:27:01 +02:00
Robert Svensson
a795093705 UniFi POE control (#17011)
* First commit

* Feature complete?

* Add dependency

* Move setting poe mode logic to library

* Use guard clauses

* Bump requirement to 2

* Simplify saving switches with poe off

* Store and use poe mode

* Fix indentation

* Fix flake8

* Configuration future proofing

* Bump dependency to v3

* Add first test

* Proper use of defaults with config flow (thanks helto)

* Appease hound

* Make sure there can't be duplicate entries of combination host+site

* More tests

* More tests

* 98% coverage of controller

* Fix hound comments

* Config flow step init not necessary

* Use async_current_entries to check if host and site for controller is used

* Remove storing/restoring poe off devices to slim PR

* First batch of switch tests

* More switch tests.

* Small improvements and clean up

* Make tests pass
Don't name device in device registry

* Dont process clients that belong to non-UniFi POE switches

* Allow selection of site from a list in config flow

* Fix double blank lines in method

* Update codeowners
2018-10-16 10:35:35 +02:00
Nikolay Vasilchuk
0c0c471447 Fix: Connection pool of Request object is smaller than optimal value (8) (#17483) 2018-10-16 10:22:57 +02:00
Daniel Shokouhi
dc7e5e3af4 Add unique_id for Ring (#17497) 2018-10-16 10:06:00 +02:00
Aaron Bach
a9389d2d43 Bumps simplisafe-python to 3.1.12 (#17509)
* Bumps simplisafe-python to 3.1.12

* Updated requirements
2018-10-15 22:54:32 -06:00
Alexander Lyon
993a02b8c4 Fix the sabnzbd component api error (#17014)
* Bump pysabnzbd version number

* Pass hass aiohttp session to pysabnzbd
2018-10-16 00:18:59 +02:00
Aaron Bach
29c2b2fe79 Clean up OpenUV config flow (#17349)
* Cleaned up OpenUV config flow

* Added proper listener removal

* Added proper exception

* Unnecessary exception message

* Moved API key error to correct place

* Member-requested changes (part 1)

* Hound

* Member-requested changes (part 2)

* Cleanup

* Fixed tests
2018-10-15 13:21:21 -06:00
Daniel Perna
73197c9a6c Update pyhomematic to 0.1.51 (#17491) 2018-10-15 21:15:26 +02:00
Anders Melchiorsen
1a5048baaf Add device info for LIFX (#17330)
* Add device info for LIFX

* Address review comments
2018-10-15 20:46:33 +02:00
Daniel Perna
9718a17351 Fix HomeMatic availability detection (#17341)
* Fix availability detection
2018-10-15 20:34:03 +02:00
Dougal Matthews
6feacbbfe1 Include the name of the Volumio media player in errors (#17481)
When you have multiple Volumio media players it can be hard to determine
which one has a problem without this information.
2018-10-15 19:11:12 +02:00
Paulus Schoutsen
4ea71b0602 Merge pull request #17480 from home-assistant/rc
0.80.1
2018-10-15 16:10:50 +02:00
emontnemery
2ceb4d2d1e Refactor RFLink component (#17402)
* Start refactor of RFLink component

* alias _id not added correctly

Aliases for sensor not added correctly
And some debug traces.

* Update rflink.py

* Cleanup, fix review comments

* Call event handlers directly when processing initial event

* Use hass.async_create_task when adding discovered device

* Review comments

* Review comments
2018-10-15 15:35:33 +02:00
Paulus Schoutsen
708334c0c2 Bumped version to 0.80.1 2018-10-15 15:31:12 +02:00
Paulus Schoutsen
b5272f2bc7 Fix websocket API (#17471) 2018-10-15 15:30:21 +02:00
Pascal Vizeli
80867cc9b7 Bugfix eventstream with EOF on end (#17465) 2018-10-15 15:30:20 +02:00
Tommy Jonsson
f92b392a24 Fix hangout.send_message requiring data key (#17393) 2018-10-15 15:30:20 +02:00
Martin Berg
220054a6c3 Fix arm/disarm calls. (#17381) 2018-10-15 15:30:19 +02:00
Pascal Vizeli
0904ff45fe Cleanup HM Notify platform (#17355)
* Cleanup HM Notify platform

* Fix python 3.5.4

* Update homematic.py

* Update homematic.py
2018-10-15 13:26:09 +02:00
Niels Mündler
d6752d2270 Fix rangefilter (#17473)
* Fix rangefilter

RangeFilter would break for lower or upper bounds of 0, evaluating to False and thus not being handled correctly as bounds

* Add test for zero bounds
2018-10-15 12:24:21 +02:00
Julius Mittenzwei
373e3b12d8 Switched to async_fire (#17472) 2018-10-15 12:16:40 +02:00
Paulus Schoutsen
e985f30247 Fix websocket API (#17471) 2018-10-15 11:48:36 +02:00
Adam
22bf4d0783 Re-assign conditions (#17364) 2018-10-15 11:43:27 +02:00
kennedyshead
1cbb5b8e51 State is set to UNKNOWN rather than ON in order to make UI have an play/pause button (#17357) 2018-10-15 11:42:27 +02:00
Paulus Schoutsen
ac79ff9e24 Add context to scripts run by template entities (#17329) 2018-10-15 11:38:49 +02:00
Anders Melchiorsen
0bf10b0b09 Fire an event when timer gets out of sync (#17398) 2018-10-15 11:34:36 +02:00
Ville Skyttä
31981fde7e Make dicttoxml logging less verbose (#17446) 2018-10-15 11:25:38 +02:00
Ville Skyttä
4fce051838 Add RSRQ, RSRP, and SINR to huawei_lte default sensors (#17425)
These are important LTE signal monitoring values.
2018-10-15 11:22:49 +02:00
Rohan Kapoor
bd450ee9ff Migrate CONF_WEBHOOK_ID to homeassistant.const (#17460)
* Migrate CONF_WEBHOOK_ID to homeassistant.const

* Switch over all instances of webhook_id to the const

* Switch last instance of webhook_id to the const

* automation: conf constants for conf

* webhook: conf constants for conf
2018-10-15 11:18:21 +02:00
Malte Franken
879924fea4 refactored to make its code structure similar to nsw_rural_fire_service_feed platform (#17461) 2018-10-15 11:17:46 +02:00
Pascal Vizeli
3b0db291dd Bugfix eventstream with EOF on end (#17465) 2018-10-15 10:31:49 +02:00
Julius Mittenzwei
a71cc67efb Fix NoEntitySpecifiedError during knx startup (#17366)
* Potential fix for #13699

* removed uneccessary initialization of hass

* removed hass from signature
2018-10-15 03:29:36 +02:00
Tsvi Mostovicz
c5905ee5ca Show torah reading during weekdays (#17447)
* Add support for showing torah reading on weekdays as well

* Update docstrings for test functions
2018-10-14 23:55:11 +02:00
Craig J. Midwinter
3edcc9420a Update pysher version (#17455) 2018-10-14 23:51:15 +02:00
Aaron Bach
b022dde622 Bumped simplisafe-python to 3.1.11 (#17454)
* Bumped simplisafe-python to 3.1.10

* Updated requirements

* Updated requirements
2018-10-14 14:26:44 -06:00
kennedyshead
1187e0aea5 Remove day as a conf option (#17452) 2018-10-14 21:33:18 +02:00
Daniel Høyer Iversen
a401be9b1b New climate device (#17313)
* initial version of millheater

* Remove unused imports

* Add some comments

* separate lib

* fix review comments
2018-10-14 20:22:20 +02:00
Paulus Schoutsen
49a9b61f6f Update frontend to 20181014.0 2018-10-14 19:17:41 +02:00
Paulus Schoutsen
ed683d8c2c Update frontend to 20181014.0 2018-10-14 19:17:30 +02:00
Fabian Affolter
d4061b73b0 Upgrade youtube_dl to 2018.10.05 (#17429) 2018-10-14 17:14:06 +02:00
Fabian Affolter
9305ea9a6b Upgrade numpy to 1.15.2 (#17431) 2018-10-14 17:13:25 +02:00
Fabian Affolter
7b0c88c54b Update docstrings (#17435) 2018-10-14 17:12:34 +02:00
Fabian Affolter
253e154a79 Minor updates (#17436) 2018-10-14 17:11:39 +02:00
Fabian Affolter
daf48a3b1f Minor updates (#17437) 2018-10-14 17:10:46 +02:00
Fabian Affolter
5ac0469ef9 Upgrade python-telegram-bot to 11.1.0 (#17441) 2018-10-14 17:08:17 +02:00
Malte Franken
fccaf7f919 NSW Rural Fire Service platform (#16802)
* initial integration with nsw rural fire service feed

* improved test coverage

* updated requirements

* grouped imports

* removed debug print statement

* moved manager's startup code into separate call

* simplified feed update code

* simplified feed update code

* simplified device state attribute code

* added source to conform with pr #17339

* fixed lint

* refactored how entities are managed

* fixed pylint

* simplified signalling
2018-10-14 14:11:25 +02:00
Brant Knudson
f198859441 Upgrade pyvera to 0.2.45 (#17419)
This release adds support for garage door openers. A garage door
opener will show up as a switch.
2018-10-14 09:26:34 +02:00
333ryan18
a302112879 Bump Totalconnect (#17418)
* Update totalconnect.py

* Update requirements_all.txt
2018-10-14 09:25:24 +02:00
William Scanlon
6a8eb8d0a1 Moved econet from climate to water heater (#17322)
* Moved econet from climate to water heater

* Updated .coveragerc

* Fixed requirements_all.txt
2018-10-13 23:16:44 +02:00
Fredrik Baberg
f23708ce6f Update Vagrant Windows support (#17205)
* Update Vagrantfile with Hyper-V support

* Correction to name.

* Exclude files in Vagrant provision tests
2018-10-13 23:02:00 +02:00
noxhirsch
0dd3640c78 Adding support for HmIP-SLO (outdoor brightness sensor) (#17413)
* Add IPBrightnessSensor

* Add ILLUMINATION unit & icon

* Update homematic.py

* Added missing entry
2018-10-13 22:58:41 +02:00
J4nsen
d0da265166 Fix netio component (#17411)
* bump pynetio to 0.1.9.1 to actually use provided credentials.

* cast to int to fix TypeError

* update requirements_all.txt
2018-10-13 21:09:10 +02:00
Dan Klaffenbach
3ca3fe7015 homematic: Support additional property for sabotage detection (#17407)
At least HM-Sec-Sir-WM uses ERROR_SABOTAGE, see:

    pyhomematic.devicetypes.actors.RFSiren
2018-10-13 21:08:06 +02:00
Miloš Bunčić
ef8253c549 Added ssl and verify_ssl parameters in ddwrt device tracker component (#17406)
* Added ssl and verify_ssl parameters in ddwrt device tracker component

* Set defaults in validation
2018-10-13 21:04:51 +02:00
Keiran S
78e29cd3fa Add AWS Route53 Dynamic DNS support (#17072)
* Add AWS Route53 dynamic DNS support to Home Assistant

* Remove line breaks
2018-10-13 21:03:30 +02:00
Ville Skyttä
9c178cf488 Add unique id to syncthru sensors (#17399) 2018-10-13 14:31:53 +02:00
Steven Looman
e6d002c377 Update to async-upnp-client==0.12.5 (#17401) 2018-10-13 14:29:12 +02:00
Tommy Jonsson
70281a148b Fix hangout.send_message requiring data key (#17393) 2018-10-13 10:54:35 +02:00
Florian Klien
d4b092706a XMPP async (#17283)
* new lib dependencies, working old xmpp

* non working aioxmpp

* reverting to sync xmpp

will try slixmpp instead of aioxmpp

reasons:
echo bot example of aioxmpp had blocking behavior (slixmpp echo bot works fine)
closer API to sleekxmpp
less dependencies than aioxmpp

* first working slixmpp version

* DEBUG messages, changed MUC call

the joinMUC method changed from sleekxmpp to slixmpp
added debug messages
better name for cleanup callback

* flake8

* little cleanup, tested MUC

* requirements_all

* dependencies managed by slixmpp, removed debug messages

* resource configurable by user, requirements updated

* changed __init__ parameter code format

* removed trailing dots from LOG messages

* changed super call to python3 format
2018-10-13 10:37:42 +02:00
Tom Harris
db536797be Bump insteonplm version to 0.15.0 (#17384) 2018-10-13 10:33:34 +02:00
Martin Berg
d9d27733d1 Fix arm/disarm calls. (#17381) 2018-10-13 10:30:49 +02:00
Daniel Shokouhi
5f16f3c3a6 Add unique_id for Bloomsky (#17383)
* Add unique_id for Bloomsky

* Add bloomsky camera unique ID
2018-10-13 10:23:00 +02:00
Daniel Shokouhi
d3672f36fb Add unique_id for Neato (#17369)
* Add unique_id for Neato

* Only send the serial per review comments
2018-10-13 00:33:13 +02:00
Daniel Shokouhi
3cf6c76f8b Add unique_id for Lightify (#17377) 2018-10-13 00:32:35 +02:00
Tom French
434d1d7d63 Added option to use a location other than home (#17340) 2018-10-12 20:04:52 +02:00
Aaron Bach
401e22fc0c Add config entry for SimpliSafe (#17148)
* Initial move into component

* Base functionality in place

* Starting tests in place

* All config entry tests in place

* Made default scan interval more obvious and removed extra logging

* Inherit default scan interval from alarm_control_panel

* Updated coveragerc and CODEOWNERS

* Member-requested changes

* Hound

* Updated requirements

* Updated tests

* Member-requested changes

* Owner-requested changes

* Constant cleanup

* Fixed config flow test

* Owner-requested updates

* Updated requirements

* Using async_will_remove_from_hass

* Corrected scan interval logic

* Fixed tests

* Owner-requested changes

* Additional logging

* Owner-requested changes
2018-10-12 19:07:47 +02:00
Paulus Schoutsen
8eb4e77365 Merge pull request #17361 from home-assistant/rc
0.80.0
2018-10-12 16:57:52 +02:00
Malte Franken
1f863830e1 Adding source attribute to geo location platforms (#17339)
* added source attribute to all geo_location platforms

* amended test cases

* constant moved and source method now forces subclasses to override
2018-10-12 16:48:02 +02:00
Paulus Schoutsen
cd29b47924 Bumped version to 0.80.0 2018-10-12 14:59:36 +02:00
Pascal Vizeli
edb3722abd Hass.io auth/sso part2 (#17324)
* Update discovery.py

* Create const.py

* Update auth.py

* Update const.py

* Update const.py

* Update http.py

* Update handler.py

* Update auth.py

* Update auth.py

* Update test_auth.py
2018-10-12 14:59:17 +02:00
Pascal Vizeli
bc036cc2fe Fix auth for hass.io (#17318)
* Update test_auth.py

* Update auth.py

* Update test_auth.py
2018-10-12 14:59:17 +02:00
Martin Hjelmare
8778b707f1 Allow tradfri groups for new imported entries (#17310)
* Clean up leftover config schema option

* Allow import groups via new config yaml setup

* Fix and add test

* Add a test without groups for legacy import

* Change default import groups to False

* Fix I/O in test
2018-10-12 14:59:16 +02:00
Alok Saboo
05ae8f9dd4 Fix samsung bug (#17285) 2018-10-12 14:59:16 +02:00
Nikolay Vasilchuk
d199f57aa8 Logbook: filter by entity and period (#17095)
* Filter logbook by entity_id

* Filter logbook by period

* Simple test

* houndci-bot review

* Tests

* Test fix

* Test Fix
2018-10-12 14:59:15 +02:00
Paulus Schoutsen
f47e080f37 Update translations 2018-10-12 14:58:48 +02:00
Paulus Schoutsen
cfc5ebbfb0 Update frontend 2018-10-12 14:58:23 +02:00
Paulus Schoutsen
6971e84ddf Update translations 2018-10-12 14:58:09 +02:00
Paulus Schoutsen
397c4336bc Update frontend 2018-10-12 14:57:35 +02:00
kennedyshead
e00ed84d84 The ping command will not detect device in standby as off (#17358) 2018-10-12 14:51:03 +02:00
Jedmeng
7b28963a88 Fix setting opple light color temperature (#17359) 2018-10-12 14:50:20 +02:00
Sebastian Muszynski
5a00cc5afc Provide an individual color temperature range per Yeelight model (#17305)
* Provide an individual color temperature range per Yeelight model

* Fix lint

* Bump yeelight version

* Remove unused const

* Enable SUPPORT_COLOR_TEMP for BulbType.WhiteTemp
2018-10-12 11:35:33 +02:00
Martin Mois
cb3d62eeef notify.homematic (#16973)
* Add notify.homematic_signalgen

* Update homematic_signalgen.py, test_homematic_signalgen.py

* Added new files to .coveragerc

* Fixed review comments from houndci-bot

* Fixed pylint errors

* Regenerate requirements_test_all.txt by script/gen_requirements_all.py

* Fix flake8 warnings

* Renamed notify.homematic_signalgen to notify.homematic and made it generic

* Update .coveragerc and requirements_test_all.txt

* Removed the terms signal generator from the sources.
2018-10-12 09:36:52 +02:00
Daniel Perna
241d87e9d3 Add exception handling to dnsip sensor (#17332)
* Add exception handling to dnsip sensor

* Refactor import

* Fix exception
2018-10-12 09:30:35 +02:00
emontnemery
b2789d9883 Support abbreviations in discovery topic (#16635)
* Support abbreviations in discovery topic

* Add abbreviations for all words. Add testcase.

Add missing docstring.

* Add missing abbreviations

* Support topic prefix

* Update test case

* Restrict topic prefix

* Fix merge

* Simplify abbreviations expanding, assume TOPIC_PREFIX is one character long

* Support abbreviated keys instead of words

* Remove redundant abbreviations

* Remove extra spaces in abbreviation list

* Make topic prefix less restrictive

* Make topix prefix a bit more restrictive again
2018-10-12 08:51:16 +02:00
shred86
7bb60068d7 Color control for Abode RGB lights (#17347)
* Color control support for Abode lights

* Updated add_devices to add_entities

* Update line length

* Changed elif to if for pylint warning
2018-10-12 08:47:14 +02:00
Daniel Shokouhi
1c23a36f46 Set botvac availability (#17350)
* Set botvac availability

* Lint

* Reduce availability calls per review comments
2018-10-12 08:40:45 +02:00
Zhong Jianxin
0ea5a73e8d Fix motion sensor in Aqara LAN protocol V2 (#17240) 2018-10-12 08:34:32 +02:00
Mister Wil
6df3c480b3 Bump version of abodepy to 0.14.0 (#17336) 2018-10-11 22:00:51 +02:00
Paulus Schoutsen
61f7a39748 Add permissions foundation (#16890)
* Add permission foundation

* Address comments

* typing

* False > True

* Convert more lambdas

* Use constants

* Remove support for False

* Fix only allow True
2018-10-11 19:24:25 +02:00
emontnemery
5961f2f577 Add support for off_delay to MQTT binary_sensor (#16993)
* Add support for off_delay to MQTT binary_sensor

* Fix debounce, add testcase

* Make off_delay number of seconds instead of timedelta

* Update mqtt.py

* Fix testcase, remove CONF_OFF_DELAY from const.py
2018-10-11 19:14:23 +02:00
Paulus Schoutsen
61bf4d8a29 Add user events (#17328) 2018-10-11 17:06:51 +02:00
Nikolay Vasilchuk
44477f3d32 Logbook: filter by entity and period (#17095)
* Filter logbook by entity_id

* Filter logbook by period

* Simple test

* houndci-bot review

* Tests

* Test fix

* Test Fix
2018-10-11 14:15:04 +02:00
Karim Geiger
ed45dff6e8 Implement turn_off and turn_on actions for eq3btsmart (#17168)
* Implement turn_off and turn_on actions for eq3btsmart

This commit implements the turn_off and turn_on methods for eq3btsmart. Turning the device off will set the thermostat to "OFF". Turning it on will set it to "AUTO".

* Add missing support flags for on/off feature

* Fix line length
2018-10-11 13:25:48 +02:00
Nikolay Vasilchuk
2a35a3901e Template Lock (#17288)
* Template Lock component

* Tests

* CI Fix

* Don't track templates if they have result in MATCH_ALL

* async/await

* houndci-bot review fix
2018-10-11 12:53:54 +02:00
Paulus Schoutsen
ebff253cc9 still update sensor on startup (#17319) 2018-10-11 11:38:35 +02:00
Pascal Vizeli
f5d3aa1826 Hass.io auth/sso part2 (#17324)
* Update discovery.py

* Create const.py

* Update auth.py

* Update const.py

* Update const.py

* Update http.py

* Update handler.py

* Update auth.py

* Update auth.py

* Update test_auth.py
2018-10-11 10:55:38 +02:00
uchagani
cffb704311 Enable BMW component to be unit system aware (#17197)
* Enable BMW component to be unit system aware

* lint fixes

* use constants for config entries

* remove configuration from component and rely only on HA config of unit_system

* remove unused import

* update code to reflect feedback

* lint fixes

* remove unnecessary comments

* rework return statement to satisfy pylint

* more lint fixes

* add tests for volume utils

* lint fixes

* more lint fixes

* remove unnecessary comments
2018-10-11 10:55:22 +02:00
Martin Hjelmare
58af332d21 Allow tradfri groups for new imported entries (#17310)
* Clean up leftover config schema option

* Allow import groups via new config yaml setup

* Fix and add test

* Add a test without groups for legacy import

* Change default import groups to False

* Fix I/O in test
2018-10-11 10:37:34 +02:00
George Marshall
ef2c8b2e5b Update python_openzwave==0.4.10 (#17323) 2018-10-11 10:59:16 +03:00
Fabien Piuzzi
9fa7906aef Made it possible to define multiple Octoprint printers (#16519)
* Made it possible to define multiple octoprint printers

* style fix

* Added configuration option for octoprint port

* SSL support in octoprint platform configuration

* Octoprint component now auto loads sensor and binary_sensor platforms

* preliminary support for auto discovery of octoprint servers

* Moved sensors and binary sensors configuration into main octoprint configuration

* Using base_url as the key for storing api in the octoprint component

* made sure to not supersede the platforms' domains

* bugfix: continue setting up other printers if one fails

* flake8 style correction

* Added icons to sensors

* Fail platform setup if no printers were successfully added

* Simplified custom validator
2018-10-11 09:52:13 +02:00
Dav0815
c6c5d40056 Transport NSW (#17242)
* Resubmission of development

* Clean up

* Finishing touch and clean up

* Remove not needed error check
2018-10-11 09:44:17 +02:00
Rohan Kapoor
d7cd1a2b4b Implement ZoneMinder run states (#17198) 2018-10-11 09:38:31 +02:00
Fabian Affolter
7e8973a315 Update file header (#17317) 2018-10-11 07:43:15 +02:00
Pascal Vizeli
3f87d41381 Fix auth for hass.io (#17318)
* Update test_auth.py

* Update auth.py

* Update test_auth.py
2018-10-11 01:02:00 +02:00
Fabian Affolter
8d9da4e7b9 Upgrade construct to 2.9.45 (#16362) 2018-10-10 23:52:45 +02:00
Glenn Waters
93e3596e5a Add Elk-M1 switch and scene platforms (#17256)
* Add Elk-M1 switch platform.

* Fix travis error.

* Fix very annoying lint error.

* Fix PR comments.

* Fix comment.

* Fix lint errors.

* Fix PR comments.

* Fix PR

Apologize. Going too fast. You should not have to find those.
2018-10-10 19:05:19 +02:00
zhumuht
c434ad6af5 fix_broadlink_sp2_show_energy (#17271)
* fix_broadlink_sp2_show_energy

Signed-off-by: 朱海涛 <zhumu.zht@alibaba-inc.com>

* fix_broadlink_sp2_show_energy

Signed-off-by: 朱海涛 <zhumu.zht@alibaba-inc.com>

* fix_broadlink_sp2_show_energy

Signed-off-by: 朱海涛 <zhumu.zht@alibaba-inc.com>

* fix_broadlink_sp2_show_energy

Signed-off-by: 朱海涛 <zhumu.zht@alibaba-inc.com>

* fix_broadlink_sp2_show_energy

Signed-off-by: 朱海涛 <zhumu.zht@alibaba-inc.com>
2018-10-10 18:56:00 +02:00
cgtobi
99c6622ee2 Add direction configuration (#17308) 2018-10-10 17:59:55 +02:00
Paulus Schoutsen
9d4dbd7d97 ABC config entries (#17309) 2018-10-10 16:02:03 +02:00
Paulus Schoutsen
fa3f6ca2c7 Bumped version to 0.80.0b5 2018-10-10 14:29:04 +02:00
Paul Annekov
1d78393680 fixed 'on_startup() takes 0 positional arguments but 1 was given' (#17295) 2018-10-10 14:26:34 +02:00
Pascal Vizeli
951d7154b8 Fix hassio discovery (#17275)
* Update discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Fix tests

* fix lint
2018-10-10 14:26:33 +02:00
Pascal Vizeli
3f28b30860 Hassio auth (#17274)
* Create auth.py

* Update auth.py

* Update auth.py

* Update __init__.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Add tests

* Update test_auth.py

* Update auth.py

* Update test_auth.py

* Update auth.py
2018-10-10 14:26:32 +02:00
Paulus Schoutsen
83dec7173c Update translations 2018-10-10 14:25:49 +02:00
Paulus Schoutsen
d16e6c8524 Update translations 2018-10-10 14:25:21 +02:00
Paul Annekov
052c094425 fixed 'on_startup() takes 0 positional arguments but 1 was given' (#17295) 2018-10-10 14:24:30 +02:00
Pascal Vizeli
40e0966d7f Hassio auth (#17274)
* Create auth.py

* Update auth.py

* Update auth.py

* Update __init__.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Update auth.py

* Add tests

* Update test_auth.py

* Update auth.py

* Update test_auth.py

* Update auth.py
2018-10-10 14:07:51 +02:00
Joshi
ad4d5666fe Yamaha AVR update and change Sound Mode only on main_zone (#17241)
* Add support for sound_mode for Yamaha rxv media_player

* Catch ParseError Exeption on surround_program for unsupported models

* Catch all Exeptions from rxv

* only get sound mode list / current sound mode on main_zone
2018-10-10 14:07:33 +02:00
definitio
7f896bfb40 WIP: Don't set initial values for MQTT HVAC in non-optimistic mode (#17268)
* Don't set initial temperature in non-optimistic mode

* Fix tests

* Don't set initial values in non-optimistic mode

For fan mode, current operation and swing mode

* Fix tests again
2018-10-10 14:06:53 +02:00
Pascal Vizeli
83dd961fde Fix hassio discovery (#17275)
* Update discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Fix tests

* fix lint
2018-10-10 13:50:11 +02:00
Paulus Schoutsen
a1dac28e4b Template sensors to not track all state changes (#17276)
* Disable template sensor match all

* Only manual update template sensors that match all
2018-10-10 13:49:15 +02:00
Martin Hjelmare
e5c3a4be80 Fix and clean haveibeenpwned (#17306)
* Move first forced data fetching and update to async_added_to_hass.
* Clean up code.
2018-10-10 13:46:03 +02:00
Ville Skyttä
707b7c202d Narrow scope of various pylint inline disables (#15364)
* Narrow scope of various pylint inline disables

* Whitespace tweaks
2018-10-10 12:17:11 +02:00
Teemu R
78c38749ab Xiaomi Vacuum: keep error state active after erroring (#16562)
* Check for got_error to keep consistent error reporting

* reword a comment
2018-10-10 12:16:32 +02:00
Markus Nigbur
670c75e844 Added resolve_state to template distance function (#17290)
_resolve_state was already used in the "closest" function, to allow for states and entity ids
2018-10-10 11:49:24 +02:00
Colby Rome
419725e1a9 Add Verizon Fios Quantum Gateway device_tracker platform (#17023)
* wrote quantum_gateway.py

* ran gen_requirements script

* fixed linting errors, added docstrings

* update .coveragerc

* fixed typo

* add myself to contributors

* single quotes for single words

* added error handling to prevent stacktrace

* updated my pypi library

* houndci fixes - added RequestException

* added password to config schema
2018-10-10 08:23:31 +02:00
cgtobi
cfc175d71d Make rmvtransport async (#17225)
* Make rmvtransport async

* Make rmv transport async

* Make async tests

* Update rmvtransport module version

* Remove unnecessary import

* Make rmvtransport async

* Make rmv transport async

* Make async tests

* Update rmvtransport module version

* Remove unnecessary import

* Update requirements

* Remove async loop

* Fix wrong import

* Fix stupidness

* Remove unnecessary import

* Bump upstream version

* Don't store the session

* Refactor tests

* Add test for no data

* Fix linter issues

* Fix stale docstring

* Fix stale docstring

* Remove unnecessary test code

* Remove unnecessary import

* Add configurable timeout

* Remove global variable
2018-10-10 08:10:42 +02:00
hanzoh
8310f4a1cf Add valve level to HmIP thermostat attributes (#17297) 2018-10-09 18:53:49 -06:00
Fabian Affolter
5022cf8a6c Upgrade locationsharinglib to 3.0.6 (#17294) 2018-10-10 00:02:07 +02:00
Fabian Affolter
2cd99e5a97 Upgrade shodan to 1.10.4 (#17292) 2018-10-09 22:37:50 +02:00
Alok Saboo
26f2e3dd8b Fix samsung bug (#17285) 2018-10-09 21:43:59 +02:00
mvn23
fc67f5eef3 Rewrite opentherm_gw to a component (#17133)
* Rewrite opentherm_gw to a component which loads the opentherm_gw climate platform.

* Add OpenTherm Gateway sensor platform.

* Remove library imports from platforms (use hass.data instead)
* Update .coveragerc
* Update docstrings to use new component documentation url

* Add OpenTherm Gateway binary sensor support.
Fix houndci findings.

* Revert "Add OpenTherm Gateway binary sensor support."

This reverts commit 5711dc4c25.

* Revert "Add OpenTherm Gateway sensor platform."

This reverts commit b3505ed561.

* Remove import from platform, use hass.data instead.
Update .coveragerc
Update docstrings
Update requirements_all.txt
General code cleanup

* Fix review findings.
Avoid using hass.data within connect_and_subscribe.
2018-10-09 21:06:24 +02:00
dickesW
2aeb0efc7c Fixed Temperature for HMIP-WeatherStation Plus/Basic (#17216) 2018-10-09 11:19:21 -06:00
Markus Nigbur
a99ba0a1d4 Bumped fints component to version 1.0.1 (#17280) 2018-10-09 11:18:46 -06:00
Paulus Schoutsen
e903f7ffda Manual updates (#17278) 2018-10-09 16:54:38 +02:00
Charles Garwood
cf249e3e5e Z-Wave Config Entry Support (#17119)
* Initial Z-Wave Config Entry Support

* Use conf.get() for config import

* Uncomment test

* Re-add line breaks

* tabs -> space

* Unused import cleanup & lint fixes

* Remove unused config flow link step

* Address comments

* Remove unused import

* Fix tests

* Check for valid usb_path

* Test for Z-Stick in config flow

* Pass config dir to ZWaveOption

* Auto-generate Network Key if none provided

* Test fixes

* Address comments & more start network service registration

* add_executor_job for options.lock()
2018-10-09 16:30:55 +02:00
Paulus Schoutsen
a8a21ee28d Bumped version to 0.80.0b4 2018-10-09 16:20:39 +02:00
Anders Melchiorsen
1a76603f53 Remove warning on script delay (#17264)
* Remove warning on script delay

* Use suppress
2018-10-09 16:20:31 +02:00
Steven Looman
089e15e046 Add defaults, fixing #17229 (#17261) 2018-10-09 16:20:31 +02:00
Sebastian Muszynski
b59d69f313 Fix ambient light state of the Philips Eyecare Lamp (Closes: #16269) (#17259) 2018-10-09 16:20:30 +02:00
Paulus Schoutsen
eb7db1f763 block external IP (#17248)
* block external IP

* Update __init__.py
2018-10-09 16:20:29 +02:00
Paulus Schoutsen
429f09deb3 Add a webhook automation trigger (#17246) 2018-10-09 16:20:29 +02:00
damarco
5167658a1d Add support for zha custom cluster mappings (#16714)
* Add support for custom cluster mappings

* Refactor sub_component mapping
2018-10-09 12:53:02 +02:00
definitio
6bf3f9e748 Fix mpd timeout error (#17254)
* Increase mpd client timeout

* Update mpd.py
2018-10-09 12:24:39 +02:00
Daniel Perna
9d56730b8d Add optional "all" parameter for groups (#17179)
* Added optional mode parameter

* Cleanup

* Using boolean configuration

* Fix invalid syntax

* Added tests for all-parameter

* Grammar

* Lint

* Docstrings

* Better description
2018-10-09 10:14:55 +02:00
Otto Winter
26cf5acd5b Make async_track_time_change smarter (#17199)
* Make async_track_time_change smarter

* Move to util/dt

* Remove unnecessary check

* Lint

* Remove tzinfo check

* Remove check

* Add comment about async_track_point_in_utc_time

* Fix typing check

* Lint
2018-10-09 10:14:18 +02:00
Otto Winter
9190fe1c21 Add device registry to MQTT fan (#17250) 2018-10-09 10:13:42 +02:00
marcolertora
0c34c50d2f Added lumitek/ankuoo recswitch component (#15764)
* Added lumitek/ankuoo recswitch component

* cosmetics

* remove callback

* cosmetics

* update requirements pyrecswitch==1.0.2

* add in .coveragerc
2018-10-09 10:13:03 +02:00
Sebastian Muszynski
757ba3b60e Add basic support of the Philips Zhirui desk lamp (philips.light.mono1) (#17258) 2018-10-09 10:11:34 +02:00
Sebastian Muszynski
882c4b73ae Fix ambient light state of the Philips Eyecare Lamp (Closes: #16269) (#17259) 2018-10-09 10:11:14 +02:00
Steven Looman
4455a287fc Add defaults, fixing #17229 (#17261) 2018-10-09 10:07:30 +02:00
Anders Melchiorsen
5db7d702c8 Remove warning on script delay (#17264)
* Remove warning on script delay

* Use suppress
2018-10-09 10:06:42 +02:00
Tomas Hellström
540d22d603 Swedish weather institute weather component (#16717)
* SMHI Component

* Clean up typos

* Fixed default values first config to home location (tests will follow)

* Fixed tests and removed unused function

* Minor fixup after comments from @kane610

* add support for precipitation in forecast

* Removed old async_step_init not needed.
2018-10-08 23:54:55 +02:00
Teemu R
56a43436d7 Bump python-miio requirement (#17260) 2018-10-08 22:33:06 +02:00
Nick Horvath
1393766659 Thermoworks Smoke Sensor (#16139)
* Add support for monitoring the temperature of a thermoworks smoke thermometer.

* Use string formatting.

* Add line break.

* Add error handling for authentication.

* Fix linting errors.

* Fix quotes.

* Bump thermoworks_smoke library version.

* Review changes for @MartinHjelmare

* Add unique id attribute and change battery attribute to the standard "battery_level".

* requested changes to snake case and monitored conditions

* fix lint error

* exclude firmware from state attrs. rename original_unit to unit_of_min_max so it's more clear what it is for.

* add device_info

* add regex validator for exclude

* undo device info stuff

* remove serial number from attributes even though other components are allowed to have it...

* exclude firmware
2018-10-08 22:15:38 +02:00
Paulus Schoutsen
68d72931c4 block external IP (#17248)
* block external IP

* Update __init__.py
2018-10-08 20:50:24 +02:00
Jari Ylimäinen
0c0184973b Add configurable temperature step for MQTT climate component (#16201)
* Add configurable temperature step

* Remove temp step from climate component
2018-10-08 20:24:25 +02:00
damarco
59ec469722 Use only_cache parameter in binary_sensor.zha.Remote (#16711) 2018-10-08 20:23:26 +02:00
Paulus Schoutsen
68472b8241 Add a webhook automation trigger (#17246) 2018-10-08 20:16:37 +02:00
Glenn Waters
9380fca97e Add Elk light platform (#17222)
* Add Elk light platform.

* Add ElkM1 light code. Doh.

* Fix PR comments.

* Fix hound errors.

* Fix PR comment.

* Move state from base to class(s) where used
2018-10-08 17:30:27 +02:00
Paulus Schoutsen
c3b1121d77 Add group foundation (#16935)
Add group foundation
2018-10-08 16:35:38 +02:00
Paulus Schoutsen
4d3d51635d Bumped version to 0.80.0b3 2018-10-08 16:16:01 +02:00
Malte Franken
a372053eac updated georss-client library to 0.3 (#17239) 2018-10-08 16:15:40 +02:00
Paulus Schoutsen
2e120061b4 Guard for bad device info (#17238) 2018-10-08 16:15:40 +02:00
Paulus Schoutsen
3a6eac216c Fix SPC (#17236) 2018-10-08 16:15:39 +02:00
Otto Winter
6671cbb96d Fix potential MQTT discovery race condition (#17208)
* Fix potential MQTT discovery race condition

* Rename data key
2018-10-08 16:15:39 +02:00
Matt Schmitt
9e386938bb MyQ cover return unknown state if not available (#17207)
* Add additional supported states

* Use get method for lookup

* Return None if unable to get status
2018-10-08 16:15:38 +02:00
Paulus Schoutsen
6be52208fc Prevent accidental device reg override (#17136) 2018-10-08 16:15:38 +02:00
Otto Winter
dd55ff87c8 Add device registry to MQTT cover (#17245)
* Add device registry to MQTT cover

* Fix tests
2018-10-08 16:13:44 +02:00
Malte Franken
b637b48bd8 emptying device state attributes if the update from the feed fails (#17249) 2018-10-08 16:13:08 +02:00
emontnemery
42fb886d71 Add support for HS color to mqtt light (#16958)
* Add support for HS color to mqtt light

* Warn if hs state update is invalid
2018-10-08 15:36:57 +02:00
Otto Winter
9290f245bf Convert MQTT fan to config entry (#17247) 2018-10-08 14:59:04 +02:00
Otto Winter
c41ef65da6 Add device registry to MQTT switches (#17244) 2018-10-08 14:57:07 +02:00
Otto Winter
744dd42ad3 Add device registry to MQTT binary sensor (#17243) 2018-10-08 14:44:00 +02:00
Matt Schmitt
27f0331f0f MyQ cover return unknown state if not available (#17207)
* Add additional supported states

* Use get method for lookup

* Return None if unable to get status
2018-10-08 14:19:23 +02:00
Malte Franken
394ffc40b6 updated georss-client library to 0.3 (#17239) 2018-10-08 13:32:16 +02:00
Paulus Schoutsen
d5f5273c31 Guard for bad device info (#17238) 2018-10-08 12:53:51 +02:00
Otto Winter
af2402ea59 Implement base for MQTT device registry integration (#16943)
* Implement base for MQTT device registry integration

* Lint

* Lint

* Address comments

* Lint

* Lint

* Address comments

* Only add keys if specified

See https://github.com/home-assistant/home-assistant/pull/17136#discussion_r223267185
2018-10-08 12:53:30 +02:00
Daniel Lashua
71a0274b12 Add Support for Xiaomi Vibration Sensor (#16422) 2018-10-08 12:08:46 +02:00
Otto Winter
3f498bd042 Fix potential MQTT discovery race condition (#17208)
* Fix potential MQTT discovery race condition

* Rename data key
2018-10-08 10:59:43 +02:00
MatteGary
ee5e1fa355 Daikin Climate - Better integration with Climate base component (#16913)
* Daikin Climate - Better integration with Climate base component

Made some modification in order to better integrate the Daikin AC Component with the base Climate Component.
Benefits are:
Support localization for Operation Mode
Support for Homekit Integration (if the AC is turned On, now the status is updated in Homekit)

* Build bug fixing

* Bug fixing in Set Operation_Mode functionality

Fixed to .title() functionality in matching the Operation_Mode

* Fix useless code

* Fix hound bug

* Bug fixing

Change in list of Operation Mode

* Trailing white space fix

* Compile fixing

* Whitespace fix
2018-10-08 10:49:54 +02:00
William Scanlon
f2d8f3bcb8 Water heater support (#17058)
* Moved econet to water_heater

* Wink and Econet are now in water heater!

* Removed away mode from econet and added demo water heater

* Added demo tests

* Updated coveragerc

* Fix lint issues.

* updated requirements all

* Requirements all actually updated.

* Reset wink and econet and fixed service.

* Reset wink and econet to the correct dev state

* Reset requirements_all and .coveragerc and removed the new econet and wink water_heater files

* Removed @bind_hass service methods

* Actually reset the .coverage file

* Fixed the tests

* Addressed @MartinHjelmare's comments

* Removed unused import

* Switched to async_add_executor_job

* Fixed lint

* Removed is_on

* Added celsius demo water heater and tests.

* Removed metric import
2018-10-08 10:38:07 +02:00
Sebastian Muszynski
ff4204244b Fix data_key and power_consumed attribute of the Aqara Wall Switch (Closes: #16457) (#17235) 2018-10-08 10:37:27 +02:00
Paulus Schoutsen
a54e242245 Fix SPC (#17236) 2018-10-08 10:20:18 +02:00
Oliver
849665b9ca Pushed to version 0.7.6 of denonavr library to add more sound modes (#17227) 2018-10-08 09:32:14 +02:00
Sebastian Muszynski
315f83e1ea Add some new model names of Xiaomi Aqara devices (#17234)
* Add additional model name of the Xiaomi Aqara Button (WXKG11LM)

* Add additional model name of the Xiaomi Aqara Wireless Switch (WXKG02LM, WXKG03LM)

* Bump PyXiaomiGateway version
2018-10-08 09:32:01 +02:00
Paulus Schoutsen
4b7f85518f Prevent accidental device reg override (#17136) 2018-10-08 09:30:40 +02:00
Daniel Høyer Iversen
59d78b060f danielhiversen as codeowner for met.no (#17232) 2018-10-08 08:36:32 +02:00
Paulus Schoutsen
bd4ff6fc21 Bumped version to 0.80.0b2 2018-10-07 23:33:46 +02:00
Martin Berg
5fcea074c4 Init sub-components using global var. (#17220) 2018-10-07 23:33:40 +02:00
Per Sandström
7369af0639 Verisure standard config for scan interval (#17192)
* verisure configurable polling

* fix indentation
2018-10-07 23:33:40 +02:00
Anders Melchiorsen
b872cb95c7 Upgrade aiolifx_effects to 0.2.1 (#17188) 2018-10-07 23:33:39 +02:00
cdce8p
3338ebd4c1 Bugfix switch flux - light service call (#17187)
* Bugfix switch flux - light service call

* Change x_val and y_val test
2018-10-07 23:33:38 +02:00
cdce8p
7b176aa7c9 Fix device_tracker service call & cleanup (#17173)
* Bugfix group service - device_tracker

* Cleanup
2018-10-07 23:33:38 +02:00
Paulus Schoutsen
c722091967 Fix data used for logbook (#17172)
* Fix data used for logbook

* Lint
2018-10-07 23:33:38 +02:00
Martin Berg
e922dd10ba Init sub-components using global var. (#17220) 2018-10-07 23:30:09 +02:00
Paulus Schoutsen
4cffef7f2b Update translations 2018-10-07 23:27:57 +02:00
Paulus Schoutsen
31b1b0c044 Update frontend to 20181007.0 2018-10-07 23:27:57 +02:00
Paulus Schoutsen
2de1193fd9 Update translations 2018-10-07 23:26:46 +02:00
Paulus Schoutsen
c12bbddc0b Update frontend to 20181007.0 2018-10-07 23:26:23 +02:00
Anders Melchiorsen
086c71525e Add config entry for LIFX (#17201)
* Add config entry for LIFX

* Use list for dependencies

* Obsolete the platform config

* Use DOMAIN

* Use async_create_task
2018-10-07 23:14:53 +02:00
Glenn Waters
06a64c0167 Add support for ElkM1 alarm/automation panel (#16952)
* Add support for ElkM1 alarm/automation panel.

* fix lines too long

* Address PR comments

* Fix hound ci errors

* Changes for PR comments

* Use vol.Range for checking range value

* Address PR comments

* Fix lint errors

* Added elkm1-lib requirement

* Update coverage to exclude elk

* Fix flake8 errors

* Fix flake8 error

* Cleanup config parsing

* Add housecode converter

* fix PR comments

* fix syntax error

* Fix PR comment
2018-10-07 21:45:36 +02:00
Daniel Høyer Iversen
c1ed9edd26 Add forecast for Met.no (#17109)
test met

met no 0.3.0

fix line length

fix line length
2018-10-07 21:00:12 +02:00
emontnemery
1d7d82fde5 Fix aliases support for RFLink sensors (#17190) 2018-10-07 13:14:37 +02:00
Brian Towles
592e1dc96a Enable new registry rename for Insteon (#17171)
* Enable new registry rename for Insteon

* Segment unique_id from name
2018-10-07 13:12:33 +02:00
Anders Melchiorsen
6e81ae096e Disallow list/dict for string configuration (#17202) 2018-10-07 12:35:44 +02:00
Per Sandström
c8266c6692 vsure version 1.5.0 (#17209) 2018-10-07 12:33:16 +02:00
Philip Rosenberg-Watt
8fda705377 Fix Todoist custom project update (#17115)
* Fix Todoist custom project update

Custom projects were not refreshing the API state and were using
local/stored state. This resulted in invalid tasks being retained upon
update. This change resets the local Todoist API state, syncs it, and
then continues normal update operation(s) on the Todoist project data
object.

* Remove blank line after docstring

* Update logging call

* Simplify logging
2018-10-07 01:38:25 +02:00
cdce8p
5d6562a73f Bugfix switch flux - light service call (#17187)
* Bugfix switch flux - light service call

* Change x_val and y_val test
2018-10-06 23:30:07 +02:00
Guy Khmelnitsky
9285831fa1 Upgrade boto3 to 1.9.16 (#17140) 2018-10-06 20:46:20 +02:00
Per Sandström
760047f964 Verisure standard config for scan interval (#17192)
* verisure configurable polling

* fix indentation
2018-10-06 20:03:22 +02:00
Anders Melchiorsen
8683eeb908 Upgrade aiolifx_effects to 0.2.1 (#17188) 2018-10-06 14:32:54 +02:00
Fabian Affolter
75e236de73 Add myself to more sensors (#17185) 2018-10-06 09:54:03 +02:00
Fabian Affolter
169abe637c Update core, add myself and introduce grouping (#17175) 2018-10-06 07:16:06 +02:00
cdce8p
07d90c6c55 Fix device_tracker service call & cleanup (#17173)
* Bugfix group service - device_tracker

* Cleanup
2018-10-05 23:09:55 +02:00
Paulus Schoutsen
a66db59359 Fix data used for logbook (#17172)
* Fix data used for logbook

* Lint
2018-10-05 23:07:27 +02:00
Fabian Affolter
bed1b96f5a Revert "Update core, add myself and introduce grouping"
This reverts commit 13106a9b55.
2018-10-05 21:46:26 +02:00
Fabian Affolter
13106a9b55 Update core, add myself and introduce grouping 2018-10-05 21:45:14 +02:00
kennedyshead
7598067b55 Adding myself as melissa owner (#17157) 2018-10-05 20:48:20 +02:00
Paulus Schoutsen
7a22fbe961 Bumped version to 0.80.0b1 2018-10-05 20:21:27 +02:00
Paulus Schoutsen
738089c57c Fix incorrect yaml in hangouts (#17169) 2018-10-05 20:21:23 +02:00
Paulus Schoutsen
5e7d4a57a3 Fix incorrect yaml in hangouts (#17169) 2018-10-05 20:21:09 +02:00
Paulus Schoutsen
1061c369f1 Bumped version to 0.81.0.dev0 2018-10-05 19:54:15 +02:00
Paulus Schoutsen
88c0a92857 Bumped version to 0.80.0b0 2018-10-05 17:56:30 +02:00
Paulus Schoutsen
0c770520ed Update translations 2018-10-05 17:50:00 +02:00
Paulus Schoutsen
92e28067d8 Update frontend to 20181005.0 2018-10-05 17:49:27 +02:00
prophit987
d1636dab31 @danielhiversen as codeowner for Tibber (#17154) 2018-10-05 14:55:16 +02:00
prophit987
13aa935147 Discover Danfoss/devolo RS Room Sensor thermostat (#17153)
Allows the Danfoss/Devolo RS Room Sensor thermostat to be discovered as climate device in homeassistant.
This device is defined as GENERIC_TYPE_MULTILEVEL_SENSOR, and not GENERIC_TYPE_THERMOSTAT.
Ref: https://community.home-assistant.io/t/devolo-zwave-thermostat/58739
2018-10-05 14:39:10 +02:00
Paul Annekov
29c3f1618f Fix miflora connection errors during platform setup (#16798)
* possible fix for startup delay

* fixed reported issues

* moved update code into setup

* reverted to previous solution
2018-10-05 13:33:28 +02:00
cdce8p
37a47b5a59 Add faucet, shower, sprinkler, valve to HomeKit (#17145) 2018-10-05 12:43:50 +02:00
Julius Mittenzwei
2e62afabdc Added warning to HomeKit component (#16807)
* added warning if more then 100 devices are added to HomeKit
2018-10-05 12:32:26 +02:00
Rohan Kapoor
a8f5e8699a Fix zoneminder zms_url construction (#17150) 2018-10-05 10:30:08 +02:00
Lewis Juggins
4218efddcd Cleanly stop tradfri on shutdown (#17114)
* Tradfri shutdown fix

* Bump version

* Bump version

* Fix

* Derp

* Remove unnecessary shutdown event
2018-10-05 09:59:34 +02:00
Gerard
aec320dc19 Fix a typo (#17147) 2018-10-05 07:48:40 +02:00
Aaron Bach
acf684de05 Added OpenUV CODEOWNERS info (#17149) 2018-10-04 19:57:52 -06:00
Florian Klien
ce1e8f8ace YesssSMS handling more errors, upgrade to version 0.2.3 (#17052)
* YesssSMS handling more errors, upgrade to version 0.2.1

 - handling missing internet connection nicely
 - disabling login with non-working credentials (website locked account for 1 hour)
 - upgrade to new upstream version of YesssSMS

* notify.yessssms tests

* test requirements

* flake8 fix

* fixing tests, new upstream version 0.2.3

fixing tests based on requested changes, coverage

* removing unmotivated print

* passing exception to ConnectionError and SMSSendingError logger
2018-10-04 17:34:04 -04:00
Per Sandström
b55c7a5ba2 verisure configurable polling (#17144) 2018-10-04 21:41:02 +02:00
cdce8p
52ff232797 Bugfix invalid entity_config parameter HomeKit (#17143) 2018-10-04 20:37:04 +02:00
Paulus Schoutsen
769dda735d Remove discovery (#17070) 2018-10-04 16:04:44 +02:00
Ana Paula Gomes
02bf07d9df Add timeout and fix oscillations on Samsung TV component (#17102)
* Add timeout and fix oscillations

* Adjust code to py3.5.3

* Clean code
2018-10-04 16:02:14 +02:00
Georgi Kirichkov
b92b24e8a4 Webhook component - pass headers to webhook handler (#17091)
* Pass headers to webhook handler

* Refactors webhook component to post Request object instead of data

* Update webhook tests

* Cleanup webhook test and fix a bug in ifttt

* Address code review comments
2018-10-04 15:54:51 +02:00
Paulus Schoutsen
0cfbb9ce91 Allow config entry setup to raise not ready (#17135) 2018-10-04 15:53:50 +02:00
Tsvi Mostovicz
c9976718d4 Take timezone into consideration when calulating Zmanim. Partial fix for #16946 (#17131) 2018-10-04 15:51:56 +02:00
Jason Hu
b61a250321 Fix upnp component l10n error (#17132) 2018-10-04 15:50:42 +02:00
Alok Saboo
ae0dd33e2e Add update service to Google Travel Sensor (#17092)
* Add update service

* Update service name

* Register service only once

* Make hound happy
2018-10-04 14:02:30 +02:00
Daniel Høyer Iversen
0d93bf9a45 Update xiaomi lib (#17129) 2018-10-04 13:24:54 +02:00
Heiko Thiery
178bf736f6 Add new component fritzbox binary_sensor (#17057)
* Add new component fritzbox binary_sensor

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

* fix failed flake8 test

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

* add new file to .coveragerc

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

* use wildcard to cover all platforms

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

* remove polling because polling is true by default

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

* add blank line to keep imports ordered

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

* Minor changes
2018-10-04 12:16:27 +02:00
Paulus Schoutsen
a559c06d6b Make it easier for auth to consume newer formats (#17127) 2018-10-04 10:41:13 +02:00
Jerad Meisner
cc1891ef2b Add time created to persistent notifications. (#17121)
* Add time created to persistent notifications.

* UTC
2018-10-04 10:24:14 +02:00
Daniel Høyer Iversen
05d8c57212 Tibber component and notify (#17062)
* Refactor tibber, and Tibber notify

* update Tibber lib.

* tibber

* Tibber coveragerc

* Tibber upgrade lib

* style

* comments

* use async_get_service

* event
2018-10-04 09:29:49 +02:00
Adam Mills
3abdf217bb Homekit controller reconnect (#17060)
* Add threaded call_later helper

* Reconnect to device when connection fails

* Consolidate connection logs and warn on first
2018-10-04 09:25:05 +02:00
Martin Hjelmare
6a0c9a718e Fix sonos async use (#17099)
* Entry setup wasn't using the async api. Fix this by using correct
  async api.s
* Also use new async executor scheduler in async_added_to_hass.
2018-10-04 09:20:20 +02:00
Ville Skyttä
abd329d707 Upgrade pytest to 3.8.2 (#17125) 2018-10-04 09:03:31 +02:00
Aaron Bach
04fdde0e86 Bumps simplisafe-python to 3.1.2 (#16931)
* Bumps simplisafe-python to 3.0.4

* Updated requirements

* Refresh token logic added

* Member-requested changes

* Removed unused import

* Updated CODEOWNERS

* Bump library to 3.1.2
2018-10-03 21:03:29 -06:00
Michael Wei
2b3019bdf4 Support multiple accounts in Tile, use device identifiers (#17108)
*  Update tile to support multiple accounts

* 🎨 fix indent

* 🐛 fix format string

* 🎨 use .format

* 🎨 fix line indent
2018-10-03 17:22:21 -06:00
Diogo Gomes
cf0147098a Merge pull request #16300 from StevenLooman/igd
Adds discovery and config flow
Breaking change: no longer possible to map ports other than the port used by Home Assistant
2018-10-03 22:55:01 +01:00
mvn23
aeb21596a0 Fix counter restore. (#17101)
Add config option to disable restore (always use initial value on restart).
Add unit tests for restore config option.
2018-10-03 23:12:21 +02:00
Tsvi Mostovicz
cf5f02b347 Fix jewish calendar sensor with language set to english (#17104)
* Add failing testcase for issue #16830

* Fix for #16830
2018-10-03 18:43:25 +02:00
Michael Wei
467a59a6ed Change Tile icon to view-grid (#17098) 2018-10-03 09:59:11 -06:00
Kevin Fronczak
da622abb79 Adding myself as blink codeowner (#17096) 2018-10-03 17:04:31 +02:00
David Bilay
16cbc2d07f Add weather condition code to OpenWeatherMap sensor (#17093) 2018-10-03 16:15:45 +02:00
Anders Melchiorsen
952a1b3513 Smaller steps for Sonos volume up/down (#17080) 2018-10-03 15:09:05 +02:00
Anders Melchiorsen
a5402739b7 Keep the repeat mode when setting Sonos shuffle mode (#17083)
* Keep the repeat mode when setting Sonos shuffle mode

* Fix test
2018-10-03 14:50:13 +02:00
Pascal Vizeli
704c9d8582 Add support for Hass.io discovery feature for Add-ons (#17035)
* Update handler.py

* Update __init__.py

* Update handler.py

* Update __init__.py

* Create discovery.py

* Update handler.py

* Update discovery.py

* Update __init__.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update struct

* Update handler.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update __init__.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update discovery.py

* Update __init__.py

* Update discovery.py

* fix lint

* Update discovery.py

* cleanup old discovery

* Update discovery.py

* Update discovery.py

* Fix lint

* Fix tests

* Write more tests with new functions

* Update test_handler.py

* Create test_discovery.py

* Update conftest.py

* Update test_discovery.py

* Update conftest.py

* Update test_discovery.py

* Update conftest.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Update test_discovery.py

* Fix test

* Add test

* fix lint

* Update handler.py

* Update discovery.py

* Update test_discovery.py

* fix lint

* Lint
2018-10-03 13:10:38 +02:00
David Peterson
2e5eb4d9dc Add optional headers configuration for scrape (#17085) 2018-10-03 12:47:38 +02:00
Steven Looman
5d693277f0 Fix stale docstrings 2018-10-03 11:27:38 +02:00
Steven Looman
3cb20c7b4d Changes after review by @MartinHjelmare 2018-10-03 11:08:32 +02:00
Paulus Schoutsen
4210835dcd Async response all the things (#17073)
* Use async_response

* Update device_registry.py
2018-10-03 07:53:54 +02:00
Dan Cinnamon
15a160a630 Bump pyenvisalink (#17086) 2018-10-03 06:28:08 +02:00
Kevin Fronczak
c78850a983 Overhaul of Blink platform (#16942)
* Using new methods for blink camera

- Refactored blink platform (breaking change)
- Camera needs to be uniquely enabled in config from now on
- Added motion detection enable/disable to camera platform

* Fix motion detection

- bumped blinkpy to 0.8.1
- Added wifi strength sensor

* Added platform schema to sensor

- Added global variables for brand and attribution to main platform

* Removed blink binary sensor

* Add alarm control panel

* Fixed dependency, added alarm_home

* Update requirements

* Fix lint errors

* Updated throttle times

* Add trigger_camera service (replaced snap_picture)

* Add refresh after camera trigger

* Update blinkpy version

* Wait for valid camera response before returning image

- Motion detection now working!

* Updated for new blinkpy 0.9.0

* Add refresh control and other fixes for new blinkpy release

* Add save video service

* Pushing to force bot to update

* Changed based on first review

- Pass blink as BLINK_DATA instead of DOMAIN
- Remove alarm_arm_home from alarm_control_panel
- Re-add discovery with schema for sensors/binar_sensors
- Change motion_detected to a binary_sensor
- Added camera_armed binary sensor
- Update camera device_state_attributes rather than state_attributes

* Moved blink.py to own folder. Added service hints.

* Updated coveragerc to reflect previous change

* Register services with DOMAIN

- Change device add for loop order in binary_sensor

* Fix lint error

* services.async_register -> services.register
2018-10-03 04:17:14 +02:00
Fabian Affolter
8e3a70e568 Upgrade youtube_dl to 2018.09.26 (#17079) 2018-10-02 22:31:59 +02:00
Thomas Lovén
06340c9875 Allow no movement in vamera.onvif_ptz service (#17065) 2018-10-02 14:36:28 +02:00
Paulus Schoutsen
31dd327c59 Catch possible errors from tradfri (#17068)
* Catch possible errors from tradfri

* Update config_flow.py
2018-10-02 14:35:43 +02:00
Paulus Schoutsen
38e2926a48 Update translations 2018-10-02 14:33:47 +02:00
Paulus Schoutsen
19722a0ef8 bump frontend to 20181002.0 2018-10-02 14:33:30 +02:00
Paulus Schoutsen
a6f8c3f662 Add logging to light updates (#17069) 2018-10-02 13:33:16 +02:00
Thomas Lovén
11d5671ee0 De-syncing binary_sensor.ping (#17056)
* Quick and dirty asyncing of binary_sensor.ping

* Minor changes
2018-10-02 11:43:34 +02:00
Paulus Schoutsen
20601cfc2d Merge branch 'master' into dev 2018-10-02 11:19:25 +02:00
Paulus Schoutsen
07972c84a1 Merge pull request #17067 from home-assistant/rc
0.79.3
2018-10-02 11:17:14 +02:00
cdce8p
7f0a50ce31 async_create_task (#17059)
* async_create_task

* Update google.py
2018-10-02 11:03:09 +02:00
Paulus Schoutsen
03845ef490 Lint 2018-10-02 10:35:15 +02:00
Paulus Schoutsen
37706c2731 Lint 2018-10-02 10:35:00 +02:00
Aaron Bach
71e3047f5c OpenUV: Fixed issue with missing protection window data (#17051) 2018-10-02 10:34:12 +02:00
Charles Garwood
0954eefa9f MJPEG Camera Log Filter Fixes (#17050)
* Move filter to setup platform

* pylint
2018-10-02 10:24:44 +02:00
cdce8p
90f71261c5 Remove service helper (8) (#17055)
* Updated keyboard

* Updated microsoft_face

* Updated ffmpeg

* Updated logger

* Updated components/__init__.py
2018-10-02 10:23:37 +02:00
Malte Franken
13af61e103 GeoRSS events sensor refactored (#16939)
* refactored geo_rss_events sensor to make use of new georss-client library that handles the communication with the rss feed

* fixed lint error
2018-10-02 10:20:51 +02:00
Nicko van Someren
b0b3620b2b Added working support for private storage (#16903)
* Fixed file corruption bugs in private storage code.

* Restoring fixed test case.

* Implemented test suite for utils/json.py

* Added new unit test cases for util/json.py

* Dixed formatting nags

* Fixed more nags from the Hound

* Added doc strings to some very short functions

* Fixing lint's complains about my choice of parts of speach. Sigh.

* Moved atomic save operations down into util/json.py so that all benefit.

Added extra clean-up code to ensure that temporary files are removed in
case of errors.
Updated emulated_hue unit tests to avoid errors.

* Apparently 'e' is not allows as a variable name for an exception...
2018-10-02 10:16:43 +02:00
Paulus Schoutsen
e07b6dfe0b Bumped version to 0.79.3 2018-10-02 09:57:01 +02:00
Paulus Schoutsen
3f5018459f Proactor policy fix (#17066)
* Proactor policy fix

* Backport Proactor policy for <py37
2018-10-02 09:56:54 +02:00
Jason Hu
2feab82396 Use Protractor loop in Windows (#17061)
* Use Protractor loop in Windows

* Add import sys
2018-10-02 09:56:53 +02:00
Paulus Schoutsen
1decba0052 Proactor policy fix (#17066)
* Proactor policy fix

* Backport Proactor policy for <py37
2018-10-02 09:55:37 +02:00
Jason Hu
1667481342 Use Protractor loop in Windows (#17061)
* Use Protractor loop in Windows

* Add import sys
2018-10-02 09:21:02 +02:00
Mathieu Velten
8e276295eb Update pynetgear to 0.4.2 (fix #14752) (#17064) 2018-10-02 09:14:07 +02:00
Sean Wilson
284d4d49c7 Add AquaLogic component (#16763)
* Add missing ups.status states.

* Add missing DISCHRG state.

* AquaLogic work-in-progress

* - Fix dependencies
- Switch updates

* Add support for aqualogic 0.8 features.

* Remove debugging.

* Switch to async updates rather than using polling.

* Rebase

* Fix lint errors

* Fix lint errors

* Fix lint errors

* Fix lint errors

* Fix lint errors.

* Bump aqualogic version to 0.11

* Update .coveragerc

* Remove integration-specific I/O

* Resolve code review issues.

* Fixed init() call.
2018-10-02 07:32:03 +02:00
cdce8p
c3eff5773b Remove alert service helper (#17038) 2018-10-02 00:33:45 +02:00
Daniel Perna
4d471622f6 Update pyhomematic to 0.1.50 (#17048) 2018-10-02 00:02:59 +02:00
SNoof85
90f2990b9e Support code 7 (#17047) 2018-10-01 22:18:56 +02:00
Charles Garwood
b197b8bab3 Suppress urllib3 header parsing error (#17042) 2018-10-01 20:39:40 +02:00
Fabian Affolter
a148f3e2a9 Mind the unit system (fixes #16819) (#16823) 2018-10-01 20:38:41 +02:00
Steven Looman
d732f8eca2 Changes after review by @MartinHjelmare 2018-10-01 19:26:43 +02:00
Nikolay Vasilchuk
b0c1c37cd5 Fix long update 'load_power' and 'in_use' for Xiaomi Zegbee Plug (#16915) 2018-10-01 18:00:48 +02:00
Anders Melchiorsen
4c36ffd0ef Remove error logging when Sonos shuffle_set is not available (#16921)
Error on set_shuffle with UPnP Error 712 received: Play mode not supported from 10.23.2.16
2018-10-01 17:58:04 +02:00
Paulus Schoutsen
fbc1c41673 Logbook context (#16937)
* Convert logbook to use attr

* Add context to events

* Enhance logbook

* Lint

* Fix logbook entry

* Don't use intermediary classes for logbook entries
2018-10-01 16:12:25 +02:00
Paulus Schoutsen
2e6346ca43 Break up websocket 2 (#17028)
* Break up websocket 2

* Lint+Test

* Lintttt

* Rename
2018-10-01 16:09:31 +02:00
Paulus Schoutsen
122e21a3c4 Merge pull request #17041 from home-assistant/rc
0.79.2
2018-10-01 15:50:25 +02:00
Paulus Schoutsen
33a54043e9 Bumped version to 0.79.2 2018-10-01 15:08:56 +02:00
Rohan Kapoor
d09ee8ac82 Fix switch.zoneminder name (#17026) 2018-10-01 15:08:50 +02:00
Paulus Schoutsen
1ca2e5226b Fix MQTT certificates (#16999) 2018-10-01 15:08:50 +02:00
cdce8p
b5e3d8c337 Async syntax (#17033)
* async-syntax-mqtt_room

* async-syntax-alert

* Additional fixes
2018-10-01 14:44:11 +02:00
Josh Anderson
f0fbdd6a26 Send headers with REST switch GET request (#17036) 2018-10-01 14:43:54 +02:00
cdce8p
e5c0e4336d Update coverage to exclude not tested file (#17039) 2018-10-01 14:41:35 +02:00
Paulus Schoutsen
c69a790ede Add Hass.io discovery to MQTT (#16962)
* Add Hass.io discovery to MQTT

* Update en.json

* Update __init__.py

* Update config_flow.py

* Update strings.json

* Update test_config_flow.py

* Lint

* Ensure we don't send bad data in config entry
2018-10-01 14:11:21 +02:00
David De Sloovere
5bc2e78ab4 Bump Enphase_Envoy dependency for older models (#17032) 2018-10-01 14:10:32 +02:00
Heiko Thiery
68bda1c732 Add new device attributes to fritzbox climate (#17027)
* Add new device attributes to fritzbox climate

With Fitz!OS 7 new parameters are introduced.

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

* update requirements

Signed-off-by: Heiko Thiery <heiko.thiery@gmail.com>
2018-10-01 12:53:25 +02:00
Timmo
3137099348 🔨 update errors (#17029) 2018-10-01 12:51:39 +02:00
Paulus Schoutsen
22a80cf733 Break up websocket component (#17003)
* Break up websocket component

* Lint
2018-10-01 11:21:00 +02:00
Fabian Affolter
9edf1e5151 Upgrade locationsharinglib to 3.0.3 (#17010)
* Upgrade locationsharinglib to 3.0.3

* Revert change from #16969
2018-10-01 10:55:16 +02:00
Rohan Kapoor
6159f8b0ce Fix switch.zoneminder name (#17026) 2018-10-01 10:53:20 +02:00
Ville Skyttä
d4cde2bfbf Upgrade huawei-lte-api to 1.0.16 (#16972) 2018-10-01 10:52:49 +02:00
Otto Winter
760f822dce Fix MQTT discovery (#17004) 2018-10-01 09:01:40 +02:00
cdce8p
b24f9f5dfa Async syntax 3/8 (#17017)
* Async syntax 3, device_tracker & fan & hassio & image_processing & input

* Pylint fixes
2018-10-01 08:59:45 +02:00
cdce8p
134eeecd65 Async syntax 4/8 (#17018)
* Async syntax 4, media_player & notify

* Pylint fixes
2018-10-01 08:58:21 +02:00
cdce8p
3b5e5cbcd6 Async syntax 5, light & lock & remote & scene & telegram & helpers (#17019) 2018-10-01 08:56:50 +02:00
cdce8p
9e4c8f45d6 Async syntax 6, sensor (#17020) 2018-10-01 08:55:43 +02:00
cdce8p
121dba659c Async syntax 7, switch & tts & vacuum (#17021) 2018-10-01 08:55:00 +02:00
cdce8p
9aaf11de8c Async syntax 8/8 (#17022)
* Async syntax 8

* Pylint fixes
2018-10-01 08:52:42 +02:00
Charles Garwood
ea7b1e4573 Update Z-Wave service descriptions to point to proper log file (#17024) 2018-10-01 08:50:32 +02:00
cdce8p
8444b9ba03 Async syntax 2, camera & climate & config (#17016) 2018-10-01 08:50:05 +02:00
cdce8p
38e371c5d9 Async syntax 1, alarm_control_panel & automation & binary_sensor (#17015) 2018-10-01 08:49:19 +02:00
sander76
750c96709e Homematic cloud device update fix (#17001) 2018-09-30 22:39:25 +02:00
Patrik
940d5fb2ee Add basic support for Tradfri switches (#17007)
* Initial commit

* Sockets have been moved to separate component

* Sockets have been moved to separate component

* Fix const PLATFORM_SCHEMA

* Fix unique id

* Fix async_create_task

* Fix PLATFORM_SCHEMA

* Fix typo

* Remove pylint disable
2018-09-30 22:22:07 +02:00
Paulus Schoutsen
0a2b266742 Fix MQTT certificates (#16999) 2018-09-30 21:36:27 +02:00
Steven Looman
f511920a04 Merge remote-tracking branch 'upstream/dev' into igd 2018-09-30 20:22:10 +02:00
Totoo
1b7bfec247 Google Maps supports battery level and charging. (#16969)
* Google Maps supports battery level and charging.

With 3.0.2 locationsharinglib now the battery level and the charging attributes are available.

* Update google_maps.py

fix too long line error

* Update google_maps.py

Fix multi line import, and line length limit

* Update gen_requirements_all.py

Add locationsharinglib to gen_requirements_all

* update requirements_all

* Last try to fix requirements_all...
2018-09-30 15:17:39 +02:00
Paulus Schoutsen
f5632a5da5 Add webhook + IFTTT example (#16817)
* Add webhook + IFTTT example

* Abort if not externally accessible

* Abort on local url

* Add description to create entry

* Make body optional

* Allow ifttt setup without config

* Add tests

* Lint

* Fix Lint + Tests

* Fix typing
2018-09-30 14:45:48 +02:00
Paulus Schoutsen
1e28c752e1 Merge pull request #16988 from home-assistant/rc
0.79.1
2018-09-30 13:31:29 +02:00
Paulus Schoutsen
677714ecab Bumped version to 0.79.1 2018-09-30 10:22:49 +02:00
Rohan Kapoor
e2aadc3227 Bump zm-py to 0.0.4 (#16979) 2018-09-30 10:22:42 +02:00
Greg Laabs
c45b240026 Fix ISY blocking bug (#16978)
This fix results in `is_on` returning False if the state is unknown (which was a bug in 0.79).
2018-09-30 10:22:42 +02:00
Jason Hu
32d652884b Override unique_id of NestActivityZoneSensor (#16961) 2018-09-30 10:22:41 +02:00
Anders Melchiorsen
3528f8e647 Fix exception during history_stats startup (#16932)
* Fix exception during history_stats startup

* Do not track changes during startup

* Ignore args
2018-09-30 10:22:41 +02:00
Jason Hu
3e6cf8f59f Optimize Ring Sensors platform setup (#16886) 2018-09-30 10:22:41 +02:00
Ville Skyttä
06d959ed43 Upgrade pytest to 3.8.1 (#16980) 2018-09-30 10:20:10 +02:00
Greg Laabs
7f47d601f1 Fix ISY blocking bug (#16978)
This fix results in `is_on` returning False if the state is unknown (which was a bug in 0.79).
2018-09-30 09:21:27 +02:00
Rohan Kapoor
8b1bdda0fa Bump zm-py to 0.0.4 (#16979) 2018-09-30 09:21:07 +02:00
Otto Winter
70ce9bb7bc Add pressure sensor device class (#16965)
* Add pressure sensor device class

* Undo github desktop line exclude
2018-09-29 22:01:04 +02:00
Otto Winter
45fdda3f5d Add unique_id to MQTT fan (#16949) 2018-09-29 21:22:57 +02:00
Anders Melchiorsen
caaf4f5694 Fix exception during history_stats startup (#16932)
* Fix exception during history_stats startup

* Do not track changes during startup

* Ignore args
2018-09-29 21:22:24 +02:00
Otto Winter
3ddad83a84 Add unique_id to MQTT cover (#16950)
* Add unique_id to MQTT cover

* Fix tests
2018-09-29 20:54:57 +02:00
Otto Winter
35b6064581 Convert fan component to config entry (#16951)
* Conver fan component to config entry

* Lint
2018-09-29 20:53:48 +02:00
Jason Hu
c600d28b6a Override unique_id of NestActivityZoneSensor (#16961) 2018-09-29 20:53:02 +02:00
Diogo Gomes
909c3bdd63 Merge pull request #16954 from abmantis/patch-2
Adds @abmantis to CODEOWNERS
2018-09-28 23:05:59 +01:00
Abílio Costa
4b8c38819e Add myself to CODEOWNERS 2018-09-28 22:47:29 +01:00
Jan van Helvoort
7d1960baba Add zwave.network_complete_some_dead event (#16894)
* Add zwave.network_complete_some_dead event

* add missing comma

* typo

* Add SIGNAL_AWAKE_NODES_QUERIED_SOME_DEAD Test

* Add blank lines

* fix linter warnings

Line too long

* remove trailing whitespace

* Change test signal

* Listen to other event
2018-09-28 19:14:57 +02:00
Otto Winter
af89e7c50f Move more MQTT platforms to config entries (#16918)
* Move more MQTT platforms to config entries

* Address comments
2018-09-28 16:57:17 +02:00
Paulus Schoutsen
c93879074b Merge pull request #16940 from home-assistant/rc
0.79.0
2018-09-28 16:51:25 +02:00
Paulus Schoutsen
f476d781ec Version bump to 0.79.0 2018-09-28 15:28:24 +02:00
Anders Melchiorsen
720b05c301 Fix race between script delay and turn_off (#16923) 2018-09-27 22:08:09 -06:00
Otto Winter
c9b6567265 Remove discovered mqtt_json light entity when discovery is cleared (#16906)
* Remove discovered mqtt_json entity device when discovery topic is cleared

* Keep imports ordered
2018-09-28 00:25:51 +02:00
Greg Laabs
9abdbf3db6 Rachio component modernization (#16911)
Add `unique_id` to all rachio entities
Add platform discovery to rachio component
Move config options from switch.rachio platform to the rachio component
2018-09-27 23:17:15 +02:00
cdce8p
f879ac0993 Remove service helper (6) (#16920)
* Update automation

* Update group

* Async_create_task
2018-09-27 23:14:09 +02:00
cdce8p
70b901017f Remove service helper (5) (#16917)
* Update switch

* Update script

* Update light

* Fix tests

* Fix config/script hook

* Async_create_task

* Fix flux switch
2018-09-27 23:13:11 +02:00
Paulus Schoutsen
7bfe0e1c00 Bumped version to 0.79.0b3 2018-09-27 23:10:42 +02:00
Paulus Schoutsen
fb39641eef Handle exception handling websocket command (#16927)
* Handle exception handling websocket command

* lint

* Lint2
2018-09-27 23:10:36 +02:00
Paulus Schoutsen
b7e03f6973 Prevent discovered Tradfri while already configured (#16891)
* Prevent discovered Tradfri while already configured

* Lint
2018-09-27 23:10:35 +02:00
Charles Garwood
7597e30efb Add unique_id to Nest Sensors (#16869)
* Add unique_id

* Add device_info

* Fix typo

* Update __init__.py
2018-09-27 23:10:35 +02:00
Paulus Schoutsen
a7248d4574 Handle exception handling websocket command (#16927)
* Handle exception handling websocket command

* lint

* Lint2
2018-09-27 23:10:07 +02:00
Paulus Schoutsen
51dbc988f9 Update translations 2018-09-27 23:08:04 +02:00
Paulus Schoutsen
71333a15f7 Bump frontend to 20180927.0 2018-09-27 23:08:04 +02:00
Paulus Schoutsen
bac71d3d22 Update translations 2018-09-27 23:04:28 +02:00
Paulus Schoutsen
d77e88645e Bump frontend to 20180927.0 2018-09-27 23:04:03 +02:00
Otto Winter
2d104f95d7 Fix MQTT Config Entry Discovery (#16919) 2018-09-27 22:56:04 +02:00
Paulus Schoutsen
81e21b90c9 Fix auth redirect (#16914)
* Fix auth redirect

* Remove old test
2018-09-27 09:02:50 -07:00
Otto Winter
d1ad2cc225 Make MQTT platforms config entries (#16904)
* Make MQTT platforms config entries

* Fix tests

* Address Comment

* Rework tests

* Undo style auto-reformat style changes
2018-09-27 16:07:56 +02:00
zxdavb
8d65230a36 Add (EU-based) Honeywell evohome CH/DHW controller (#16427)
* Add support for Honeywell evohome CH/DHW systems

More flake8 corrections

Passes Flake8 tests

Almost passed flake8.pylint!

Passed all tox tests

Now it needs serious testing!

Almost ready to submit

BUGFIX: DHW state now functional

More improvements to available()

Solved the DHW temp units problem!

Last minute bug squash

to improve dicts merge

Trying to rebase

fixing more rbase errors

revert to creating HTTP_error_code internally for now

ready to submit PR

Added support for Honeywell evohome CH/DHW systems

* Updated requirements_test_all.txt

* Fix: D401 First line should be in imperative mood

* Remove _LOGGER.info (replace with _LOGGER.debug)

* raise PlatformNotReady when RequestException during setup()

* Revert some LOGGER.debug to LOGGER.warning

* Improved logging levels, and removed some unwanted comments

* Improvments to logging - additional self._status info

* BUGFIX: DHW wrongly showing available = False (and some other tweaks)

* Fix trailing whitespace

* Remove state_attributes override and API_VER code

* Removed heating zones, DHW and heuristics to reduce # lines of code

* Removed some more lines of code

* Removed unused configuration parameters

* Remove some more un-needed lines

* Removed more (uneeded) lines of code & fixed two minor typos

* Improvements to debug logging of available() = False

* Improvements to code, and code clean-up

* Corrected a minor typo

* A small tidy up

* reduces precision of emulated temps floats to 0.1

* Some code improvements as suggested by JeardM

* Rewrite of exception handler

* Removed another unwanted logging in properties

* Remove async_ version of various methods

* moved available heuristics to update()

* Cleanup of code, and re-work linter hints

* fixed a minor documentation typo

* scan_interval is now no longer a configurable option

* Change from Master/Slave to Parent/Child

* Removed the last of the slaves

* Removed the last of the masters

* Move -PARALLEL_UPDATES to .\climate\evohome.py'

* main code moved to climate/evohome.py

* merge EvoEntity into EvoController class

* remove should_poll (for now)

* woops! left a hint in

* removed icon

* only log a WARNING the first time available = False

* cleanup dodgy exception handling

* Tidy up exception handling

* Many changes as suggested by @MartinHjelmare, thanks

* remove hass from init, part 1

* use async_added_to_hass instead of dispatcher_connect

* remove hass from init, part 2 (done)

* add 1-2 arrays, and tidied up some comments

* from dispatcher to async_added_to_hass

* cleaned up some logging, and removed others

* Many changes as request by @MartinHjelmare

* Homage to the lint

* Changed to the HA of doing operating_mode

* Now using update_before_add=True

* reduced logging further still...

* fixed minor lint

* fix a small logic error

* Add device_state_attributes to track actual operating mode

* Clean up doc quotes caused by  previous changes

* Woops! removed some debug lines that shoudln't have stayed in

* Add a complete set of device_state_attributes

* Cleanup some constants

* Remove more legacy code

* domain_data to evo_data & this else should be a finally

* minor change for readability

* Minor change for readability #2

* removed unused code

* small tidy up - no code changes

* fix minor lint

* Correct URLs & descriptions in docstring

* whoops - fixed a typo in docstrings

* remove an unused line of cide & a small tidy-up
2018-09-27 13:29:44 +02:00
Blake Blackshear
2cc6263092 Add new services for set/refresh Z-Wave device values (#16638)
* Add services for getting and setting indicator values for Z-Wave

* Add service to manually refresh Z-Wave node value by value_id

* Remove refresh_indicator service

* Coerce to int

* Add generic set_node_value service

* Remove set_indicator service
2018-09-27 12:34:42 +02:00
emontnemery
ad79dc673d MQTT Light - Do not throw if property is missing from templated MQTT message (#16720)
* Do not throw if property is missing

* Render template once, add debug prints
2018-09-27 11:48:52 +02:00
Paulus Schoutsen
da3342f1aa Update new values coming in for dev registry (#16852)
* Update new values coming in for dev registry

* fix Lint+Test;2C
2018-09-27 11:26:58 +02:00
Anders Melchiorsen
29db43edb2 Ignore Xiaomi hub callbacks during setup (#16910) 2018-09-27 09:44:19 +02:00
Anders Melchiorsen
24e9c62fe7 Upgrade pysonos to 0.0.3 (#16901)
This version has downgraded/removed some logging that we then no
longer have to hide.
2018-09-27 01:09:30 +02:00
Anders Melchiorsen
81d4338b93 Upgrade aiolifx_effects to 0.2.0 (#16900) 2018-09-27 01:08:20 +02:00
Jason Hu
96e5acda1a Optimize Ring Sensors platform setup (#16886) 2018-09-26 15:38:13 -07:00
Anders Melchiorsen
726cf9b1c4 Remove unused import (#16909)
* Remove unused import

* Revert test usage
2018-09-27 00:21:29 +02:00
Paulus Schoutsen
631ecf578e Lint 2018-09-26 23:48:55 +02:00
Paulus Schoutsen
273a7af330 Prevent discovered Tradfri while already configured (#16891)
* Prevent discovered Tradfri while already configured

* Lint
2018-09-26 18:03:25 +02:00
cdce8p
4b674b1d16 Remove unused legacy test helper methods (#16893) 2018-09-26 18:03:13 +02:00
cdce8p
dd45e99302 Remove service helper (4) (#16892)
* Update media_player

* Update lock

* Update notify

* Update remote

* Update scene

* Update vacuum

* Remove timer helpers

* Removed unused legacy helpers
2018-09-26 18:02:05 +02:00
Paulus Schoutsen
d0ddc28f96 Revert file mode write_json (#16897) 2018-09-26 17:58:44 +02:00
Paulus Schoutsen
9ab8f78b19 Don't pass use_env=True (#16896) 2018-09-26 17:20:34 +02:00
Paulus Schoutsen
732009c668 Update translations 2018-09-26 15:39:20 +02:00
Paulus Schoutsen
d20e0f5c95 Update frontend to 20180926.0 2018-09-26 15:39:20 +02:00
cgtobi
98a4b1e9ac Update language strings (#16884) 2018-09-26 13:07:43 +02:00
Nikolay Vasilchuk
917df1af00 Telegram_bot polling support proxy_url and proxy_params (Fix #15746) (#16740)
* Telegram bot polling proxy support

* CI fix

* houndci-bot review fix

* houndci-bot review fix

* CI fix

* Review

* Update polling.py
2018-09-26 11:59:37 +02:00
Blake Blackshear
f13f723a04 Add bitwise operations as template helpers (#16833) 2018-09-26 11:57:16 +02:00
Paulus Schoutsen
7b68f344e3 Bumped version to 0.79.0b2 2018-09-26 11:21:22 +02:00
Paulus Schoutsen
824e59499f Make ring sync again (#16866) 2018-09-26 11:21:10 +02:00
Paulus Schoutsen
ff9377d1d9 Fix MQTT discovery (#16864)
* Fix MQTT discovery

* Update __init__.py
2018-09-26 11:21:09 +02:00
Charles Garwood
cf0d0fb33e Device Registry Support for iOS Sensors (#16862)
* Add device_info property to iOS sensors for device registry

* Remove unused logger import

* Fix spacing

* lint

* Lint
2018-09-26 11:21:09 +02:00
Jason Hu
75c372021d Fix example for long-lived access token WS API (#16882) 2018-09-26 11:20:48 +02:00
Paulus Schoutsen
3aaf619fc3 Update translations 2018-09-26 11:20:07 +02:00
Paulus Schoutsen
e375b63902 Update frontend to 20180926.0 2018-09-26 11:20:00 +02:00
Nicko van Someren
e5861241c7 Added support for private storage. (#16878)
* Addded support for private storage.

Include 'private' flag parameters to the Store class and save_json function.
Updated various authentication and onboarding classes to use private stores.
Fixed unit test for emulated_hue which used a mock patch on save_json().

* Fixed Hound formatting issues not detected by local linting.
2018-09-26 10:24:32 +02:00
Paulus Schoutsen
e205092693 Revert incorrect check (#16883) 2018-09-26 10:15:49 +02:00
cdce8p
fa98a27df7 Remove service helper (2) (#16863)
* alarm_control_panel

* automation

* camera

* climate

* counter

* fan

* Add legacy notes

* Fix tests
2018-09-26 09:49:55 +02:00
Paulus Schoutsen
c899875abb Fix MQTT discovery (#16864)
* Fix MQTT discovery

* Update __init__.py
2018-09-26 09:38:50 +02:00
Charles Garwood
bab079f649 Add unique_id to Nest Sensors (#16869)
* Add unique_id

* Add device_info

* Fix typo

* Update __init__.py
2018-09-26 09:19:47 +02:00
Jason Hu
92a5068977 Use HA native OAuth2 flow for google assistant components (#16848)
* Use HA native OAuth2 flow for google assistant components

* Lint

* Force breaking changes

* Fix CONFIG_SCHEMA
2018-09-26 08:57:55 +02:00
Charles Garwood
3cba2e695c Device Registry Support for iOS Sensors (#16862)
* Add device_info property to iOS sensors for device registry

* Remove unused logger import

* Fix spacing

* lint

* Lint
2018-09-26 08:56:23 +02:00
cdce8p
bfa1c55803 Fix fan_init test (#16865)
* Fix fan_init test

* Readd __init__.py
2018-09-26 08:53:24 +02:00
Paulus Schoutsen
ba2b8512c5 Make ring sync again (#16866) 2018-09-26 08:52:22 +02:00
cdce8p
e2a56721d3 Remove service helper (3) (#16879)
* Update duckdns

* Update google_assistant

* Update group

* Update homematic

* Update image_processing

* Update input_boolean

* Update input_number

* Update input_select

* Update input_text
2018-09-26 08:50:05 +02:00
Martin Mois
672fc61bb2 aiohttp.ClientSession gets proxy information from HTTP_PROXY/HTTPS_PROXY (#16874) 2018-09-26 08:43:09 +02:00
Gerard
6490ec87f3 Upgrade to bimmer_connected 0.5.3 (#16877) 2018-09-26 08:24:59 +02:00
Daniel Høyer Iversen
dc102a96a9 Tibber realtime consumption, Tibber pulse (#16870)
* tibber real time data

* Tibber Pulse, realtime consumption

* update lib
2018-09-26 07:49:09 +02:00
cdce8p
b91a061cef Add missing __init__ test files (#16871) 2018-09-25 23:08:37 +02:00
cdce8p
7eaf8640d0 Update cover tests (#16832)
* Update test_group
* Update test_command_line
* Update test_demo
* Update test_mqtt
* Update test_template
* Remove cover service call helpers
2018-09-25 22:32:05 +02:00
Jedmeng
8e311686d0 Add support for Opple light (#16765)
* Add support for Opple light

* Update docstring

* review fix

* review fix

* review fix
2018-09-25 20:49:37 +02:00
Paulus Schoutsen
5a22e7d211 Fail if dirty (#16839)
* Fail if dirty

* Update check_dirty

* Update text

* Fix comment

* Add -e

* Update dirty script
2018-09-25 20:47:51 +02:00
Paulus Schoutsen
7de0e1e39a Add executor job (#16853) 2018-09-25 20:47:22 +02:00
emontnemery
4501bdb4a0 Remove discovered MQTT cover device when discovery topic is cleared (#16857) 2018-09-25 19:32:42 +02:00
emontnemery
4a265f37e0 Remove discovered MQTT fan device when discovery topic is cleared (#16858) 2018-09-25 19:32:31 +02:00
emontnemery
c3f58b8c74 Remove discovered MQTT lock device when discovery topic is cleared (#16859) 2018-09-25 19:32:25 +02:00
emontnemery
422ccc1a28 Remove discovered MQTT sensor device when discovery topic is cleared (#16860) 2018-09-25 19:32:16 +02:00
emontnemery
eb59f2dd3c Move MQTT discovery removal tests to platform test files (#16861) 2018-09-25 19:32:04 +02:00
Paulus Schoutsen
399040de46 Fix files left behind (#16855)
* Light demo test to not write entity registry

* Fix Manual MQTT alarm control panel
2018-09-25 17:19:46 +02:00
emontnemery
0dbfd77402 Remove discovered MQTT climate device when discovery topic is cleared (#16856) 2018-09-25 17:15:39 +02:00
Paulus Schoutsen
61a2d09342 Bumped version to 0.79.0b1 2018-09-25 15:39:42 +02:00
Paulus Schoutsen
345c886dec Add unique ID and device info to Nest camera (#16846)
* Add unique ID and device info to Nest camera

* Remove sw version
2018-09-25 15:39:36 +02:00
Paulus Schoutsen
b9043ef7a7 Allow MQTT discovery (#16842) 2018-09-25 15:39:36 +02:00
Paulus Schoutsen
356040d506 Support old tradfri config format (#16841) 2018-09-25 15:39:35 +02:00
Rohan Kapoor
0431e38aa2 Bump zm-py to 0.0.3 (#16835) 2018-09-25 15:39:34 +02:00
Paulus Schoutsen
4c32ad3b48 Don't warn but info when on dev mode (#16831) 2018-09-25 15:39:33 +02:00
Tommy Jonsson
bc8d323bdd Add image support to hangouts notifications (#16560)
* add image to services.yaml

* add image support

Add image support to hangouts notification.

* fix indents

* fix line length

* Add data schema

forgot schema

* fix linelength

* use is_allowed_path and better file error handling

* elif not else if

* fix logger error

* fixes

* fix travis errors/warnings

* remove trailing whitespace

* fix travis pylint naming

* new async style

* removed unused async_timeout

* change to image_file/url (#3)

* change to image_file/url

* removed whitespace

* forgot to remove unused urlparse import

* image_file/url in service help
2018-09-25 15:04:43 +02:00
Paulus Schoutsen
a1c914dfeb On removal, only unload config entry if loaded (#16844)
* On removal, only unload config entry if loaded

* Fix test
2018-09-25 14:29:13 +02:00
emontnemery
093285f92f Remove discovered MQTT binary_sensor device when discovery topic is cleared (#16826) 2018-09-25 14:25:03 +02:00
Paulus Schoutsen
9ea5afd109 Add unique ID and device info to Nest camera (#16846)
* Add unique ID and device info to Nest camera

* Remove sw version
2018-09-25 13:47:12 +02:00
Daniel Høyer Iversen
01925fdfff change unknown to None in Netatmo public (#16845) 2018-09-25 13:41:29 +02:00
Paulus Schoutsen
7840b1e387 Fix MQTT leaving files behind (#16840) 2018-09-25 12:22:27 +02:00
Paulus Schoutsen
e4898bb05c Allow MQTT discovery (#16842) 2018-09-25 12:22:14 +02:00
Paulus Schoutsen
f4974f58fe Config entry update data (#16843)
* WIP

* Allow updating data of a config entry
2018-09-25 12:21:11 +02:00
Paulus Schoutsen
2b2502c91c Support old tradfri config format (#16841) 2018-09-25 11:57:32 +02:00
Daniel Winks
4a4c07ac1b GitLab-CI sensor integration addition. (#16561)
* Updates to GitLab Sensor

* Updates to GitLab_CI sensor.

* Added GitLab_CI sensor.

* Updated interval to a more appropriate 300 seconds.

* Added GitLab_CI.py to ommitted files.

* Initial refactor to use python-gitlab PyPI module.

* Fixes to dict parsing.

* Updated required packages for GitLab_CI requirements.

* Updates and refactoring to more closely align with Home-Assistant standards.

* Moved import to init, removed unreachable requests exception.

* Removed references to STATE_UNKNOWN and replaced with None

* Removed extra whitespace

* Changed PLATFORM_SCHEMA and renamed add_devices.

Changed PLATFORM_SCHEMA to use SCAN_INTERVAL and add_devices to add_entities

* Added configurable name, changed logger and removed cruft.

* Removed _status to use _state instead, as both held same value.

* Fixed ATTR_BUILD_BRANCH, removed more extraneous cruft.

* Changed required config keys to dict[key] format.

* Removed extraneous CONF_SCAN_INTERVAL as it's already defined.
2018-09-25 11:18:23 +02:00
GP8x
03bce84c32 Add additional Netatmo public data sensors (#16671)
Update deps
2018-09-25 11:14:42 +02:00
Paulus Schoutsen
4f9fc9b39f Don't create entity registry in tests (#16838) 2018-09-25 10:16:30 +02:00
sander76
069b819679 Add unique_id to homematic_cloud (#16828)
* add unique_id

* add docstring and trigger travis
2018-09-25 10:15:03 +02:00
emontnemery
90197b6ec9 Remove discovered MQTT light device when discovery topic is cleared (#16824) 2018-09-25 09:19:04 +02:00
emontnemery
42790d3e97 Remove discovered MQTT alarm_control_panel device when discovery topic is cleared (#16825) 2018-09-25 08:44:14 +02:00
Paulus Schoutsen
e78f4d1b65 Extract lovelace to it's own component (#16816)
* Extract lovelace to it's own component

* Lint

* Update comment

* Lint

* Lint
2018-09-25 08:39:35 +02:00
Rohan Kapoor
354c8f3409 Bump zm-py to 0.0.3 (#16835) 2018-09-25 07:53:35 +02:00
Paulus Schoutsen
579b77ba4c Don't warn but info when on dev mode (#16831) 2018-09-25 07:52:10 +02:00
randellhodges
b52e8525ac Add mode (daily/hourly) to darksky (#16719)
* added daily mode to darksky and wind_bearing, ozone, and visibility

* Removed dew point and pressure until the standard is updated
2018-09-24 18:09:15 +02:00
Paulus Schoutsen
dc75db6376 Bumped version to 0.80.0.dev0 2018-09-24 12:15:47 +02:00
Paulus Schoutsen
7d68ec1110 Merge branch 'dev' into rc 2018-09-24 12:14:43 +02:00
Paulus Schoutsen
c352b6fa59 Version bump to 0.79.0b0 2018-09-24 12:13:52 +02:00
Paulus Schoutsen
0bd94d8b56 Remove unused translation key 2018-09-24 12:02:26 +02:00
Paulus Schoutsen
3e2a9afff0 Another update translations 2018-09-24 12:01:34 +02:00
Paulus Schoutsen
d4b239d1d4 Update translations 2018-09-24 12:01:01 +02:00
Paulus Schoutsen
b2a9e203f2 Update frontend to 20180924.0 2018-09-24 11:58:03 +02:00
Greg Laabs
dc1534c6d1 Fix some unhandled exceptions due to missing null checks (#16812)
Fixed a couple cases that would produce errors when the ISY node status was None or `-inf`.
2018-09-24 11:43:00 +02:00
Robin Clarke
589554ad16 Allow soundtouch to play https content too (#16713) 2018-09-24 11:16:28 +02:00
Ville Skyttä
33d6c99f19 Add Python 3.7 classifier (#16645) 2018-09-24 11:11:24 +02:00
Daniel Høyer Iversen
1f74adae2a Broadlink service name (#16345)
* Broadlink. slugify service name

* style
2018-09-24 11:10:33 +02:00
Jason Hu
7a77951bb4 Add Notify MFA module (#16314)
* Add Notify MFA

* Fix unit test

* Address review comment, change storage implementation

* Add retry limit to mfa module

* Fix loading

* Fix invalaid login log processing

* Typing

* Change default message template

* Change one-time password to 8 digit

* Refactoring to not save secret

* Bug fixing

* Change async_initialize method name to aysnc_initialize_login_mfa_step

* Address some simple fix code review comment
2018-09-24 11:06:50 +02:00
Thomas Lovén
ad47ece5c6 Allow split component definitions in packages (#16177)
* Allow split component definitions in packages

Two different configuration styles are described in
https://www.home-assistant.io/docs/configuration/devices/#style-2-list-each-device-separately

But only one is allowed in packages according to
https://www.home-assistant.io/docs/configuration/packages/

This change allows "Style 2" configuration in packages.

* Added test for split component definition in packages
2018-09-24 10:17:24 +02:00
emontnemery
5ee4718e24 Remove discovered MQTT Switch device when discovery topic is cleared (#16605)
* Remove discovered device when discovery topic is cleared

* Move entity removal away from mqtt discovery

* Move discovery update to mixin class

* Add testcase

* Review comments
2018-09-24 10:11:49 +02:00
Martin Berg
a5cb4e6c2b Use pyspcwebgw for SPC component (#16214)
* Use pyspcwebgw library.

* Support alarm triggering.

* Update requirements.

* Add pyspcwebgw to test reqs.

* Also update script.

* Use dispatcher.

* Address review feedback.
2018-09-24 10:10:10 +02:00
Baptiste Lecocq
4fd2f773ad Add linky sensor (#16468)
* Add linky component

* Update requirements_all.txt

* Add timeout to pylinky requests

* Make linky platform fail fast
2018-09-24 08:09:34 +02:00
Jason Hu
564ad7e22a Rework chromecast fix (#16804)
* Revert changes of #16471, and fix the platform setup issue

* Fix unit test

* Fix

* Fix comment

* Fix test

* Address review comment

* Address review comment
2018-09-23 23:35:07 +02:00
mvn23
329d9dfc06 Improve opentherm_gw state detection (#16809)
Only show the platform in STATE_HEAT when the boiler is actually heating.
2018-09-23 22:16:24 +02:00
Anders Melchiorsen
2258c56d34 Handle netgear_lte connection errors (#16806) 2018-09-23 18:58:09 +02:00
Daniel Shokouhi
e90abf1007 Set botvac state when offline (#16805) 2018-09-23 12:35:18 +02:00
Paul Biester
eaee55175b Add configurable host for bbox routers (#16778)
* Add configurable host for bbox routers

Add configurable host for bbox router running on non-default IP addresses.

* Fix unused import

Fix unused import which also resolves "line too long"

* Fix wrong import order

* Update validation
2018-09-23 10:37:53 +02:00
huangyupeng
539b86e079 Add Tuya cover state (#16709)
* add cover open and close state

* add tuya cover state

* fix pylint

* fix pylint problem
2018-09-23 10:36:33 +02:00
tadly
127395ae8d Upgrade zeroconf to 0.21.3 (#16789) 2018-09-23 09:50:59 +02:00
Greg Laabs
eca1f050cd Bump sucks (Ecovacs) lib to 0.9.3 (#16803)
* Bump sucks (Ecovacs) lib to 0.9.3

Changed code that was in place as a workaround pre-0.9.2. This version bump fixes a few issues.

* Update requirements_all
2018-09-23 09:43:01 +02:00
Rohan Kapoor
0d681b0ba6 Bump zm-py up to 0.0.2 (#16800) 2018-09-23 09:42:11 +02:00
Daniel Shokouhi
94d38a1c6b Bump pybotvac to 0.0.10 (#16799) 2018-09-23 09:41:45 +02:00
Daniel Høyer Iversen
cfd1aec741 Update Tibber lib (#16795) 2018-09-22 18:25:17 +02:00
Daniel Shokouhi
eec6722cf4 Fix return to base logic for neato (#16776) 2018-09-22 11:34:46 +02:00
Paulus Schoutsen
fce275d29c Merge pull request #16787 from home-assistant/rc
0.78.3
2018-09-22 11:20:29 +02:00
Paulus Schoutsen
5eda5f2f7b Bumped version to 0.78.3 2018-09-22 11:18:54 +02:00
edif30
5ab580ab34 Bump gtts-token to 1.1.2 (#16775)
* bump gtts-token to 1.1.2

* bump gtts-token to 1.1.2

* bump gtts-token to 1.1.2
2018-09-22 11:18:47 +02:00
Paulus Schoutsen
5613816476 Fix Windows loop (#16737)
* Fix Windows loop

* Fix windows Ctrl+C

* Move windows restart handler out of setup_and_run_hass
2018-09-22 09:54:37 +02:00
Gerard
e9c7fe184d Upgrade bimmer_connected to 0.5.2 (#16780) 2018-09-22 08:52:57 +02:00
Fabian Affolter
41bb4760f7 Upgrade restrictedpython to 4.0b5 (#16779) 2018-09-21 23:21:00 +02:00
edif30
ee3f17d5c7 Bump gtts-token to 1.1.2 (#16775)
* bump gtts-token to 1.1.2

* bump gtts-token to 1.1.2

* bump gtts-token to 1.1.2
2018-09-21 21:22:27 +02:00
Malte Franken
18d37ff0fd GeoJSON platform (#16610)
* initial version of geojson platform

* unit tests for geo json platform added; smaller bugfixes and code cleanups

* fixing pylint issues

* moved all code accessing the external feed into separate library; refactored platform and tests to work with that new library

* fixing lint

* small refactorings
2018-09-21 21:15:57 +02:00
Robert Svensson
7fe0d8b2f4 deCONZ cover support (#16759)
deCONZ cover platform for Keen vents
2018-09-21 19:59:20 +02:00
Paulus Schoutsen
8b42d0c471 Add confirmation to Cast/Sonos/iOS config entries (#16769)
* Add confirmation to Cast/Sonos/iOS config entries

* Remove redundant code
2018-09-21 16:34:37 +02:00
Giuseppe
213171769d Refactored units and icons for the Dyson sensors (#14550)
* Refactored units and icons for the Dyson sensors

* Adapted unit tests to the new device names and unit of measurements

* Use None as empty unit of measurement

* Unrelated overall improvements following code review

* Adapted tests to new constructors as per previous commit

* Make sure the sensors have their `hass` attribute set in the test environment
2018-09-21 15:55:07 +02:00
Malte Franken
d5813cf167 Make rest sensor and binary sensor more efficient (#14484)
* create binary sensor even if initial update fails

* fixed broken test assertion

* fixed broken test assertion

* avoid fetching resource twice - manually in the setup_platform and then through add_devices

* raising PlatformNotReady instead of creating the sensor if the initial rest call fails; throttling the update to avoid fetching the same resource again immediately after setting up sensor

* rolled back throttling of the rest update call; can still avoid updating the binary sensor's rest resoure twice; fixed tests

* typo
2018-09-21 15:54:50 +02:00
Paulus Schoutsen
3e59ffb33a Add tradfri device info (#16768) 2018-09-21 14:47:52 +02:00
Maciej Bieniek
a0a54dfd5b Add unique_id to mqtt camera (#16569)
* Add unique_id to mqtt camera

* Remove whitespaces

* Add test for unique_id

* Add blank line
2018-09-21 13:09:54 +02:00
cdce8p
3bfe9e757e Add Carbon Monoxide HomeKit Sensor (#16664) 2018-09-21 12:51:02 +02:00
Alexei Chetroi
9fdf123a2f Zha switch schedule update state (#16621)
* switch.zha: Schedule state update.

if turning switch On or Off operation was successful, then schedule
state update

* switch.zha: Update debug logging.
2018-09-21 12:11:23 +02:00
Evan Bruhn
aeaf694552 Add Logi Circle component, camera and sensor platform (#16540)
* Added Logi Circle platform, camera and sensor

* Integrated with Logo Circle API’s feature detection to exclude sensors not supported by device. Added services for recording livestream and taking a snapshot from the livestream.

* Migrated livestream snapshot and recording functionality out of home assistant components and into the logi circle API wrapper. Added services.yaml entries for logi services.

* Added new Logi sensor types, updated to latest version of `logi_circle` and tidy up in preparation for pull request.

- Renamed `logi_set_mode` to `logi_set_config`.
- Live stream recording and snapshot methods now respect whitelisted path configuration.
- Added `streaming_mode` and `speaker_volume` sensors.
- Moved model-specific turn on/off logic to `logi_circle` library.

* Renamed `logi` component domain to `logi_circle`.

* Updates based on PR feedback

* Added timeout of 15s to logi_circle initial setup requests (login and grabbing cameras).
* Added unique ID (uses MAC address for camera platform, MAC address + sensor type for sensor platform).
* Added battery level and battery charging attributes to camera.
* Removed static attributes from device_state_attributes.
* Replaced STATE_UNKNOWN with None, replaced ‘on’ & ‘off’ with STATE_ON and STATE_OFF.
* Removed redundant SCAN_INTERVAL in sensor, removed redundant hass param from async_setup_platform in camera and sensor.
* Style tweaks.

* Replaced `asyncio.wait_for` with `async_timeout` to be consistent with other components.
2018-09-21 12:00:15 +02:00
Rohan Kapoor
3d1c8ee467 Implement support for complex templates in script delays (#16442)
* Implement support for complex templates in script delays

* Swap out dict instead of collections.Mapping
2018-09-21 11:57:01 +02:00
PhracturedBlue
98b92c78c0 Add Call Data Log platform. Mailboxes no longer require media (#16579)
* Add multiple mailbox support

* Fix extraneous debugging

* Add cdr support

* liniting errors

* Mailbox log messages should mostly be debug.  Fix race condition with initializing CDR

* async decorators to async

* Lint fixes

* Typo

* remove unneeded parameter

* Fix variable names

* Fix async calls from worker thread.  Other minor cleanups

* more variable renames
2018-09-21 11:55:12 +02:00
Paulus Schoutsen
2ac16b12c1 Merge pull request #16770 from home-assistant/rc
0.78.2
2018-09-21 11:40:04 +02:00
Paulus Schoutsen
df67093441 Fix faulty color temp crashing google (#16758)
* Fix faulty color temp crashing google

* Rename

* Print warning for incorrect color temp
2018-09-21 10:51:46 +02:00
Anders Melchiorsen
c475a876ce Upgrade pysonos to 0.0.2 (#16761) 2018-09-21 09:21:56 +02:00
Robert Svensson
90c18d1c15 deCONZ add via_hub attribute for device registry (#16760)
* deCONZ add via_hub attribute for device registry

* A shorter way to get bridgeid
2018-09-21 09:21:44 +02:00
Anders Melchiorsen
78b6439ee6 Use pysonos for Sonos media player (#16753) 2018-09-20 23:50:11 +02:00
Paulus Schoutsen
092c146eae Add option to disable specific integrations (#16757)
* Add option to disable specific integrations

* Lint
2018-09-20 23:46:51 +02:00
Paulus Schoutsen
44a98fb77a Bumped version to 0.78.2 2018-09-20 20:26:25 +02:00
Fabian Affolter
03d93bea34 Upgrade zeroconf to 0.21.2 (#16730) 2018-09-20 20:25:39 +02:00
Fabian Affolter
9dbac9b033 Upgrade zeroconf to 0.21.1 (#16687) 2018-09-20 20:25:38 +02:00
tadly
9e86f0498b Upgrade zeroconf to 0.21.0 (#16647) 2018-09-20 20:25:38 +02:00
Zoé Bőle
03de658d4d Changed save_on_change to default to False (#16744) 2018-09-20 20:24:01 +02:00
Alexei Chetroi
3ea8c25e1f light.zha: Catch exceptions for all commands. (#16752)
Catch exceptions for all operations which may fail because of device
reachibility
More verbose debug logging on operations
2018-09-20 20:23:09 +02:00
Steven Looman
6e01ea5929 Preserve compatibility with original upnp 2018-09-20 18:15:04 +02:00
Pascal Vizeli
c6ccbed828 Small cleanup for slack (#16743) 2018-09-20 15:14:43 +02:00
Paulus Schoutsen
e58836f99f Add subscription info endpoint (#16727)
* Add subscription info endpoint

* Lint

* Lint

* Make decorator

* Lint
2018-09-20 14:53:13 +02:00
Paulus Schoutsen
874225dd67 Merge branch 'master' into dev 2018-09-20 13:57:35 +02:00
Paulus Schoutsen
0d0d5c8c2c Merge pull request #16742 from home-assistant/rc
0.78.1
2018-09-20 13:55:09 +02:00
Paulus Schoutsen
fc6cc22b6d Bump frontend to 20180920.0 2018-09-20 13:09:43 +02:00
Fabian Affolter
39ea9a8c90 Upgrade shodan to 1.10.2 (#16736) 2018-09-20 11:34:37 +02:00
Paulus Schoutsen
c7d5f7698e Bumped version to 0.78.1 2018-09-20 11:32:26 +02:00
Jason Hu
9bbd61cbe4 Upgrade netdisco to 2.1.0 (#16735) 2018-09-20 11:32:16 +02:00
Jason Hu
7a7a164cb8 Handle chromecast CONNECTION_STATUS_DISCONNECTED event (#16732) 2018-09-20 11:32:15 +02:00
Andreas Oberritter
8379567636 SnmpSensor: Fix async_update (#16679) (#16716)
Bugfix provided by awarecan.
2018-09-20 11:32:14 +02:00
Daniel Høyer Iversen
93af3c57ff Avoid calling yr update every second for a minute ones every hour (#16731)
* Avoid calling yr update every second for a minute one every hour

* style
2018-09-20 11:31:05 +02:00
Jason Hu
d1acb0326c Upgrade netdisco to 2.1.0 (#16735) 2018-09-20 11:11:39 +02:00
Fabian Affolter
35005474f8 Upgrade keyring to 15.1.0 (#16734) 2018-09-20 11:11:08 +02:00
Jason Hu
3a45481b5b Handle chromecast CONNECTION_STATUS_DISCONNECTED event (#16732) 2018-09-20 10:48:45 +02:00
Daniel Høyer Iversen
aa7635398a Met.no weather platform (#16582)
* Add met.no weather component

* requirements .coveragerx

* use lib

* style

* style

* renam function to _fetch_data

* Only update once per hour

* style
2018-09-20 10:32:14 +02:00
Fabian Affolter
d3658c4af9 Upgrade zeroconf to 0.21.2 (#16730) 2018-09-20 09:38:35 +02:00
Fabian Affolter
dfe38b4d5a Upgrade youtube_dl to 2018.09.18 (#16729) 2018-09-20 09:37:44 +02:00
Vikram Gorla
fcb84d951e On-demand update of swiss public transport sensor (#16723)
* Sensor values to be updated only when required (only after the train has crossed).
Looking at the documentation on https://transport.opendata.ch/docs.html, delay information is available only on stationboard, so no need to query often if there is no "real"time info.

* Bumping up version of python_opendata_transport to 0.1.4 in order to catch client errors like throttling rejection (HTTP 429)

* pleasing the hound

* bumping python_opendata_transport to 0.1.4
2018-09-20 08:45:16 +02:00
Nikolay Kasyanov
27eede724c Add unique_id to mqtt_json light (#16721)
Applies changes from #16303 to mqtt_json component.
Fixes #16600.
2018-09-20 08:15:17 +02:00
kunago
258beb9cd3 Upgrading librouteros version (#16718) 2018-09-20 07:52:06 +02:00
Charles Garwood
da882672bd Save disabled_by in entity registry (#16699)
* Save disabled_by in entity registry

* Add trailing comma
2018-09-20 00:52:10 +02:00
geekofweek
60dfd68083 MyQ Open State Fix (#16681)
* MyQ Open State Fix

Fixes issues with MyQ reporting an open state

* Update myq.py

* MyQ STATE_OPEN Fix

remove un-needed code
2018-09-19 22:03:47 +02:00
Andreas Oberritter
6e4a6cc069 SnmpSensor: Fix async_update (#16679) (#16716)
Bugfix provided by awarecan.
2018-09-19 22:01:54 +02:00
Paulus Schoutsen
a1c524d372 Config flow tradfri (#16665)
* Fix comments

* Add config flow tests

* Fix Tradfri light tests

* Lint

* Remove import group from config flow

* fix stale comments
2018-09-19 21:21:43 +02:00
Ville Skyttä
3160fa5de8 Make pylint report non-LF linefeeds per the style guidelines (#16601) 2018-09-19 15:51:57 +02:00
Ville Skyttä
d4b7057a3d Use posargs in tox lint env (#16646)
Allows linting specified files or dirs only.
2018-09-19 15:48:04 +02:00
Ville Skyttä
227a1b919b More isort preparations (#16633)
* Adjust config closer to currently established style conventions

* Adjust some imports for better outcome with their comments
2018-09-19 15:40:36 +02:00
Matthias Urlichs
0121e3cb04 Remove usage of "run_until_complete" (#16617)
* De-run_forever()-ization

* Use asyncio.run (or our own implementation on Python <3.7)
* hass.start is only used by tests
* setup_and_run_hass() is now async
* Add "main" async hass.run method
* move SIGINT handling to helpers/signal.py
  * add flag to .run to disable hass's signal handlers
* Teach async_start and async_stop to not step on each other
  (more than necessary)

* shorten over-long lines

* restore missing "import asyncio"

* move run_asyncio to homeassistant.util.async_

* LOGGER: warn => warning

* Add "force" flag to async_stop

only useful for testing

* Add 'attrs==18.2.0' to requirements_all.txt

Required for keeping requirements_test_all.txt in sync, where it is in
turn required to prevent auto-downgrading "attrs" during "pip install"

* Fixes for mypy

* Fix "mock_signal" fixture

* Revert mistaken edit

* Flake8 fixes

* mypy fixes

* pylint fix

* Revert adding attrs== to requirements_test*.txt

solved by using "pip -c"

* Rename "run" to "async_run", as per calling conventions
2018-09-19 15:40:02 +02:00
Paulus Schoutsen
da108f1999 bump frontend to 20180919.0 2018-09-19 15:17:05 +02:00
Pascal Vizeli
d376049a3f Use one regex for Hass.io URL check (#16710) 2018-09-19 12:57:55 +02:00
Ville Skyttä
7f462ba0ec Upgrade mypy to 0.630 (#16674) 2018-09-19 08:58:58 +02:00
Fabien Piuzzi
b7ef4dddb4 Netdata configuration change: Allows multiple elements per group (#16656)
Allows multiple Netdata elements per group
2018-09-19 00:42:09 +02:00
Steven Looman
b72499a949 Fixes after rename 2018-09-18 20:22:33 +02:00
Steven Looman
b770acd9a7 Update requirements_all.txt 2018-09-18 20:21:52 +02:00
Adam Dullage
56b0d2e99f Added support for Starling Bank (#16522)
* Added support for Starling Bank

* Fixing Lint Issues

* Resolving Change Requests

* HTTP Error Handling & Sandbox URL Option

* Update starlingbank Requirement to v1.2

* Minor changes
2018-09-18 15:55:10 +02:00
Maikel Punie
8e7f783da8 Added velbus counter sensors, updated to py-velbus 2.0.20 (#16683) 2018-09-18 15:51:22 +02:00
Fabian Affolter
1913d07c39 Upgrade zeroconf to 0.21.1 (#16687) 2018-09-18 15:49:54 +02:00
Glenn Waters
2a85ed7236 Streamline log messages (#16243) 2018-09-18 15:06:52 +02:00
Fabian Affolter
cba3a5b055 Upgrade paho-mqtt to 1.4.0 (#16688) 2018-09-18 14:59:39 +02:00
Fabian Affolter
d2246d5a4f Fix test 2018-09-18 12:15:55 +02:00
Greg Laabs
72419a1afe Fix Ecovacs vacuums showing "None" for name (#16654)
Was previously checking for a missing key, when should have been checking for the value being None
2018-09-18 08:30:20 +02:00
Fabian Affolter
a7325ebe1f Suppress traceback and log error (#16669) 2018-09-18 07:55:13 +02:00
Aaron Bach
27d50d388f Fixes an AirVisual bug where response data is missing (#16673) 2018-09-17 14:44:18 -06:00
Tsvi Mostovicz
25712f16b3 Jewish calendar sensor (#16393)
* Initial commit for  jewish calendar sensor

* Make check for logging errors into it's own function

* Can't use f-strings as we need to support python3.5

* Implement basic functionality: printing of date

* Update requirements_all.txt

* Allow user to specify date for sensor

* Add hdate to test requirements

* Update to match pull request

* Support date output in hebrew

* Limit languages to english and hebrew

* Add name back to sensor

* Change icon to be calendar-today

* Add multiple sensors

* Fix tests

* Make Hound happy, remove unused imported class

* hdate expects datetime.date not datetime.datetime

* Return sensor name

* Times should be returned as time object, not datetime

* Add myself to codeowners for jewish calendar component

* Return actual reading, not index

* Add more tests. Currently failing.

Will need to update hdate API and version before continuing.

* Fix weekly portion test

* Make all tests pass

* Make travis happy and add a test so it doesnt happen again

* Remove defaults in __init__ method

* Change sensor state variable to local variable in update() method

* Minor changes
2018-09-17 22:43:31 +02:00
Steven Looman
5cb0c92e78 Fix discovery/config entry handlers 2018-09-17 22:25:32 +02:00
Steven Looman
a7a16e4317 Fix tracebacks 2018-09-17 22:15:44 +02:00
Steven Looman
c19665fed4 Rename igd to upnp 2018-09-17 22:10:18 +02:00
Steven Looman
6588dceadd Fix uppercase
Fix translation
2018-09-17 22:09:51 +02:00
Paulus Schoutsen
9e59fc5d05 Merge pull request #16666 from home-assistant/rc
0.78.0
2018-09-17 19:03:37 +02:00
Paulus Schoutsen
366e270e94 Bump frontend to 20180916.0 2018-09-17 18:33:41 +02:00
Paulus Schoutsen
4b30cbbf3b Update translations 2018-09-17 14:18:20 +02:00
Paulus Schoutsen
41ac2a3c73 Bump frontend to 20180917.0 2018-09-17 14:18:04 +02:00
Paulus Schoutsen
b8257866f5 Clean up device update, add via-hub (#16659)
* Clean up device update, add via-hub

* Test loading/saving data

* Lint

* Add to Hue"

* Lint + tests
2018-09-17 13:39:30 +02:00
Paulus Schoutsen
849a93e0a6 Update translations 2018-09-17 10:48:22 +02:00
Paulus Schoutsen
f918d62571 version bump to 0.78.0 2018-09-17 10:48:09 +02:00
damarco
1c251009fe Add zha device entity (#14579)
* Add endpoint entity

* Fix lint error

* Add nwk address as device state attribute

* Change to ZhaDeviceEntity

* Show last_seen only if offline

* Remove obsolete _discover_endpoint_info()
2018-09-17 10:42:21 +02:00
Paulus Schoutsen
201fd4afee Add config entries to connection class (#16618) 2018-09-17 10:12:46 +02:00
Anders Melchiorsen
3e0c6c176a Rework timer delays (#16650)
* Calibrate timer for each tick

* Return of timer out of sync detection
2018-09-17 10:10:50 +02:00
Alexei Chetroi
44fdfdf695 Log raw result of configure_reporting() command. (#16655) 2018-09-17 07:45:44 +02:00
Fabian Affolter
fea1c921fc Fix link to docs (#16652) 2018-09-17 07:45:01 +02:00
Fabian Affolter
5e3e441aa0 Upgrade holidays to 0.9.7 (#16651) 2018-09-17 07:44:23 +02:00
Daniel Perna
a1e6e04a5e Update pyhomematic to 0.1.49 (#16649) 2018-09-16 22:56:01 +02:00
tadly
2002497d09 Upgrade zeroconf to 0.21.0 (#16647) 2018-09-16 22:53:25 +02:00
Daniel Høyer Iversen
baeb791d84 Upgrade Switchmate lib (#16637)
* new version switchmate lib, fix bug, and enable more logging in lib

* new version switchmate lib, fix bug, and enable more logging in lib
2018-09-16 13:58:26 +02:00
Abílio Costa
9c9df793b4 New EDP re:dy component (#16426)
* add new EDP re:dy platform

* lint

* move api code to pypi module; fix lint

* fix lint; remove unused import

* pass aiohttp client session and hass loop to platform

* update requirements_all.txt

* fix docstring lint

* normalize quotes

* use async setup_platform

* improve entities update mechanism

* doc lint

* send update topic only after loading platforms

* lint whitespaces

* mute used-before-assignment pylint false error
2018-09-16 01:17:47 +02:00
Mattias Welponer
05922ac56a Add new devices to HomematicIP Cloud (#16636)
* Add support for outdoor temperature sensor and cleanup

* Add support for rotary handle and water sensor

* Fix comment
2018-09-15 21:28:49 +02:00
Jason Hu
34deaf8849 Add valid_window=1 to TOTP verify (#16625) 2018-09-15 13:28:25 +02:00
Ville Skyttä
cc38981a38 Update developer doc links to developers.home-assistant.io (#16622) 2018-09-15 13:27:37 +02:00
Paulus Schoutsen
18ce5092b4 Bumped version to 0.78.0b3 2018-09-15 12:46:02 +02:00
Paulus Schoutsen
7f7372198a Update translations 2018-09-15 12:45:54 +02:00
Pascal Vizeli
abe61c5529 Rewrite bluetooth le (#16592)
* Rewrite bluetooth le

* Update requirements_all.txt

* Update gen_requirements_all.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py
2018-09-15 12:45:23 +02:00
Jason Hu
b231fa2616 Fix broken bluetooth tracker (#16589) 2018-09-15 12:45:23 +02:00
Jason Hu
336289d7e7 Add retry limit for chromecast connection (#16471) 2018-09-15 12:45:22 +02:00
Rohan Kapoor
19514ea500 Clean up MjpegCamera by removing unnused hass object in __init__ (#16628) 2018-09-15 11:26:29 +02:00
Sören Oldag
00918af94d Upgrade pwmled to 1.3.0 (#16624) 2018-09-15 10:08:52 +02:00
Ville Skyttä
e054e4da1b Small huawei_lte improvements (#16626)
* Add bunch of RouterData tests

* Avoid raising AttributeError from RouterData.__getitem__

* Use new style string formatting

* Use {key: value} instead of dict(key=value)
2018-09-15 10:42:36 +03:00
Rohan Kapoor
0c945d81c8 Add @rohankapoorcom to CODEOWNERS for the zoneminder platform (#16627) 2018-09-15 09:25:21 +02:00
Rohan Kapoor
1ca09ea36f Extracting zoneminder to a new library (#16527)
* Migrating out the zoneminder platform (and camera.zoneminder) to a new library

* Clean up the global variable ZM usage

* Modify camera.zoneminder to use the new Monitor class implementation

* Refactor camera.zoneminder after latest refactor in zm-py

* Implementing changes to switch.zoneminder to use zm-py native methods

* Complete migrating over sensor.zoneminder to the zm-py library

* Tweaking ZoneMinder components from code review

* Linting fixes for the zoneminder components

* Directly assign value when turning on/off in switch.zoneminder
2018-09-15 08:44:48 +02:00
Nate Clark
8ce2d701c2 fix bug where momentary switch with activation low does not reset (#16603) 2018-09-14 13:31:41 -06:00
christopheBfr
9c1a539f90 Adding support for RTDSContactSensor and RTDSMotionSensor in Tahoma … (RTS Alarms sensors and contacts for Somfy Protexiom alarms) (#16609)
* Adding support for RTDSContactSensor and RTDSContactSensor in Tahoma component

* Removing extra blank lines
2018-09-14 13:31:08 -06:00
Steven Looman
ca7911ec47 Set ports (hass -> hass) when no ports have been specified 2018-09-14 21:30:08 +02:00
Alexei Chetroi
0d0bda9658 Switch components.sensor.zha to await syntax. (#16619) 2018-09-14 13:29:10 -06:00
Pascal Vizeli
7705666061 Rewrite bluetooth le (#16592)
* Rewrite bluetooth le

* Update requirements_all.txt

* Update gen_requirements_all.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py

* Update bluetooth_le_tracker.py
2018-09-14 13:49:20 +02:00
Anders Melchiorsen
e82e75baf3 Improve precision of timer ticks (#16598) 2018-09-14 12:28:09 +02:00
Fabian Affolter
481f6e09fa Upgrade python-twitch-client to 0.6.0 (#16602) 2018-09-14 12:08:33 +02:00
Paulus Schoutsen
72e746d240 MQTT config entry (#16594)
* MQTT config entry

* Solely rely on config entry

* Improve wawrning

* Lint

* Lint2
2018-09-14 11:57:31 +02:00
Paulus Schoutsen
05c0717167 Add websocket list APIs for the registries (#16597)
* Add websocket list APIs for the registries

* Remove identifiers

* Fix tests

* Use ordered dict
2018-09-14 11:57:18 +02:00
Ville Skyttä
67b5b5bc85 Add myself to CODEOWNERS for upcloud (#16599) 2018-09-14 08:42:23 +02:00
Harvtronix
d076251b18 Changing z-wave brightness calculation to respect 0x01 and 0x02 byte values (#16420)
* Changing z-wave brightness calculation to respect 0x01 and 0x02 byte
values

* adding additional line breaks to satisfy houndci

* - Update comment style for linter
- Add additional unit test to increase code coverage

* Update zwave.py
2018-09-13 10:38:07 -04:00
Ville Skyttä
e59ba28fe6 Add Huawei LTE router platform, device tracker, and sensor (#16498)
* Add Huawei LTE router platform, device tracker, and sensor

* Add myself to CODEOWNERS for huawei_lte
2018-09-13 10:01:28 +02:00
Sergiy Maysak
f63dba5521 Multiple tag managers for Wireless Sensor Tags. (#16353)
* Added support for multiple tag managers. Fixed typo for signal strength.

* Corrected broken merge.

* Fixed flake8/pylint error.

* Improved docstring.
2018-09-13 09:48:17 +02:00
William Scanlon
f43d9ba680 Support for the Quirky Nimbus (#16520)
* Support for quriky nimbu

* Fixed lint

* fixed some typos
2018-09-13 09:35:40 +02:00
Daniel Høyer Iversen
aec134c47a xiaomi lib 0.10.0 (#16591) 2018-09-13 09:26:46 +02:00
Daniel Høyer Iversen
901dd9acca Update tibber lib version (#16590) 2018-09-13 09:23:33 +02:00
Alexei Chetroi
7a52bbdf24 Allow only_cache parameter in zha.safe_read() (#16553)
* Allow only_cache parameter in zha.safe_read()

* Use cache_only for binary_sensor.zha initial update.

* Use cache_only for fan.zha initial update.

* Use cache_only for sensor.zha initial update.

* Use cache_only for switch.zha initial update.

* Use cache_only for light.zha initial update.

* Refactor cached only read in zha platform.
2018-09-13 09:22:50 +02:00
Alexei Chetroi
f2203e52ef Add configure_reporting() method to zha component (#16487)
* Add zha.configure_reporting() method.

Binds a cluster and configures reporting for the specified attribute.

* git add homeassistant/components/binary_sensor/zha.py

* Refactor sensor.zha to use new 'configure_reporting() method.

* Zha configure reporting - switch (#1)

* use configure_reporting for zha switch

* lint fixes

* Rename variables/classes to properly reflect the content
2018-09-13 09:11:47 +02:00
Jason Hu
1586d3000c Fix broken bluetooth tracker (#16589) 2018-09-13 07:52:31 +02:00
Daniel Perna
d0aeb90c22 Update pyhomematic to 0.1.48 (#16588) 2018-09-13 01:04:15 +02:00
Daniel Høyer Iversen
cb542a90eb Switchmate (#16395)
* Switchmate

* switchmate

* swithcmate

* switchmate

* switchmate

* switchmate

* Make switchmate a bit more robust

* style

* style

* style

* Use external lib for switchmate

* add_entities

* Update requirements_all.txt

* unnecessary string format
2018-09-12 21:10:04 +02:00
Paulus Schoutsen
3824582e68 Add config entry to iOS (#16580)
* Add config entry to iOS

* Add translation
2018-09-12 20:17:52 +02:00
Nate Clark
2682d26f79 Konnected component feature updates (#16479)
* add  config option to invert state of binary_sensor

* Add API endpoint to get state of a Konnected switch
2018-09-12 13:54:38 +02:00
Jason Hu
308b7fb385 Add retry limit for chromecast connection (#16471) 2018-09-12 13:42:54 +02:00
Paulus Schoutsen
a88cda44d9 Bumped version to 0.78.0b2 2018-09-12 13:30:14 +02:00
Zoé Bőle
08100a485a Increasing python-websockets' version number (#16578)
* increasing python-websockets' version number so now it works with python 3.7

* required version for websockets increased to work with Python 3.7

* script/gen_requirements_all.py is done
2018-09-12 13:30:04 +02:00
Jason Hu
4c1b816bb8 Track refresh token last usage information (#16408)
* Extend refresh_token to support last_used_at and last_used_by

* Address code review comment

* Remove unused code

* Add it to websocket response

* Fix typing
2018-09-12 13:30:04 +02:00
Jason Hu
1983361373 Return if refresh token is current used one in WS API (#16575) 2018-09-12 13:29:38 +02:00
Marcel Hoppe
4efe86327d Hangouts help "page" and little bugfix (#16464)
* add 'default_conversations' parameter

* remove the empty message segment at the end of every message

* add 'HangoutsHelp' intent

* add hangouts/intents.py
2018-09-12 13:27:21 +02:00
Jason Hu
ff78a5b04b Track refresh token last usage information (#16408)
* Extend refresh_token to support last_used_at and last_used_by

* Address code review comment

* Remove unused code

* Add it to websocket response

* Fix typing
2018-09-12 13:24:16 +02:00
Paulus Schoutsen
68c21530ca Update frontend to 20180912.0 2018-09-12 13:24:02 +02:00
Paulus Schoutsen
453cbb7c60 Update frontend to 20180912.0 2018-09-12 13:23:50 +02:00
Zoé Bőle
77026a2242 Increasing python-websockets' version number (#16578)
* increasing python-websockets' version number so now it works with python 3.7

* required version for websockets increased to work with Python 3.7

* script/gen_requirements_all.py is done
2018-09-12 12:44:14 +02:00
Alexei Chetroi
501f2b0a93 Update fan.zha platform. (#16551)
* Update fan.zha platform.

switch from asyncio to async def()
catch DeliveryError exceptions
keep previous state, if reading 'fan_mode' attribute fails

* fan.zha: Use None for unknown state.

if we fail to read 'fan_mode' attribute, use None for state
2018-09-12 12:43:06 +02:00
Alexei Chetroi
117ea9e553 Refactor zha/async_device_initialized(). (#16485)
Leverage endpoint.model and endpoint.manufacturer properties
2018-09-12 11:39:23 +02:00
Ville Skyttä
beed82ab12 Upgrade pytest to 3.8.0 and pytest-timeout to 1.3.2 (#16489) 2018-09-12 11:33:26 +02:00
Daniel Perna
601f2df5a7 Notifications for Android TV: Add fontsize and sending images (#16565)
* Add fontsize and image functionality

* woof
2018-09-12 11:22:21 +02:00
Jason Hu
34d369ba26 Return if refresh token is current used one in WS API (#16575) 2018-09-12 09:49:44 +02:00
Daniel Høyer Iversen
e2465da7c2 yr: use async syntax (#16563) 2018-09-12 04:52:01 +02:00
William Scanlon
6bd120ff1d Bump pyeconet (#16571)
* Updated pyeconet to the newest version to deal with econet API change
2018-09-11 21:45:51 -04:00
Aaron Bach
188f5de5ae Fixes an OpenUV bug with the scan interval (#16570) 2018-09-11 17:13:16 -06:00
Paulus Schoutsen
6d33fb2dc8 Bumped version to 0.78.0b1 2018-09-11 21:45:42 +02:00
Paulus Schoutsen
e50fc69c1e Add websocket commands for refresh tokens (#16559)
* Add websocket commands for refresh tokens

* Comment
2018-09-11 21:45:33 +02:00
Paulus Schoutsen
20f06f4eb9 Fix invalid state (#16558)
* Fix invalid state

* Make slightly more efficient in unsubscribing

* Use uuid4"
2018-09-11 21:45:32 +02:00
Paulus Schoutsen
37c8aac76f Fix typo (#16556) 2018-09-11 21:45:32 +02:00
Jason Hu
20e562b816 Long-lived access token (#16453)
* Allow create refresh_token with specific access_token_expiration

* Add token_type, client_name and client_icon

* Add unit test

* Add websocket API to create long-lived access token

* Allow URL use as client_id for long-lived access token

* Remove mutate_refresh_token method

* Use client name as id for long_lived_access_token type refresh token

* Minor change

* Do not allow duplicate client name

* Update docstring

* Remove unnecessary `list`
2018-09-11 21:45:31 +02:00
Zellux Wang
a4cec0b871 Fix arlo intilization when no base station available (#16529)
* Fix arlo intilization when no base station

* Fix pylint for empty camera check

* Fix typo

* Minor change to trigger CI again
2018-09-11 21:44:32 +02:00
vikramgorla
60b45d4ba5 bugfix - incorrect camera type and missing sensors when multiple netatmo cameras (#16490)
fixed get_camera_type as it was originally not consuming any input, was looping with all cameras and the first camera type was retutned, modified to call cameraType using provided camera name.
2018-09-11 21:44:31 +02:00
Tom Harris
1ea8c1ece3 Fix insteon Hub v1 support (#16472)
* Fix support for Hub version 1 (i.e. pre-2014 Hub model 2242)

* Bump insteonplm to 0.14.1

* Code review changes

* Clean up and better document set_default_port

* Simplify set_default_port based on code review

* Remove Callable type import

* Simplify port setup
2018-09-11 21:44:31 +02:00
Paulus Schoutsen
44d210698e Fail fetch auth providers if onboarding required (#16454) 2018-09-11 21:44:30 +02:00
Diogo Gomes
968809c991 Replace api_password in Camera.Push (#16339)
* Use access_token and user provided token instead of api_password

* address comments by @awarecan

* new tests

* add extra checks and test

* lint

* add comment
2018-09-11 21:44:30 +02:00
Paulus Schoutsen
06af76404f Fix invalid state (#16558)
* Fix invalid state

* Make slightly more efficient in unsubscribing

* Use uuid4"
2018-09-11 21:40:35 +02:00
Paulus Schoutsen
b9b8bc678c Update frontend to 20180911.0 2018-09-11 21:37:49 +02:00
Paulus Schoutsen
629c4a0bf5 Update frontend to 20180911.0 2018-09-11 21:37:38 +02:00
Paulus Schoutsen
00d8907c5e Update translations 2018-09-11 21:37:05 +02:00
Paulus Schoutsen
3f06b4eb9b Update translations 2018-09-11 21:36:54 +02:00
Paulus Schoutsen
0db13a99aa Add websocket commands for refresh tokens (#16559)
* Add websocket commands for refresh tokens

* Comment
2018-09-11 09:08:03 -07:00
Paulus Schoutsen
4e3faf6108 Fix typo (#16556) 2018-09-11 12:55:05 +02:00
Jason Hu
9583947012 Long-lived access token (#16453)
* Allow create refresh_token with specific access_token_expiration

* Add token_type, client_name and client_icon

* Add unit test

* Add websocket API to create long-lived access token

* Allow URL use as client_id for long-lived access token

* Remove mutate_refresh_token method

* Use client name as id for long_lived_access_token type refresh token

* Minor change

* Do not allow duplicate client name

* Update docstring

* Remove unnecessary `list`
2018-09-11 12:05:15 +02:00
Jerad Meisner
50fb59477a Store notifications in component. Add ws endpoint for fetching. (#16503)
* Store notifications in component. Add ws endpoint for fetching.

* Comments
2018-09-11 11:39:30 +02:00
Diogo Gomes
20f6cb7cc7 Replace api_password in Camera.Push (#16339)
* Use access_token and user provided token instead of api_password

* address comments by @awarecan

* new tests

* add extra checks and test

* lint

* add comment
2018-09-11 11:30:20 +02:00
Zellux Wang
6b08e6e769 Fix arlo intilization when no base station available (#16529)
* Fix arlo intilization when no base station

* Fix pylint for empty camera check

* Fix typo

* Minor change to trigger CI again
2018-09-11 11:25:38 +02:00
Ville Skyttä
ee696643cd Isort preparations (#16555)
* Don't treat typing as an "in-between" module for import order

That was a < 3.5 era thing.

* Tighten scope of some pylint unused-import disables

To avoid isort moving a top level one around, undesirably broadening its
scope.
2018-09-11 11:21:48 +02:00
cgtobi
a059cc860a Update PyRMVtransport version (#16547)
* Update PyRMVtransport version

* Update requirements.
2018-09-11 05:55:02 +02:00
Paulus Schoutsen
cfe5db4350 Fail fetch auth providers if onboarding required (#16454) 2018-09-10 23:51:40 +02:00
Tom Harris
dcd7b9a529 Fix insteon Hub v1 support (#16472)
* Fix support for Hub version 1 (i.e. pre-2014 Hub model 2242)

* Bump insteonplm to 0.14.1

* Code review changes

* Clean up and better document set_default_port

* Simplify set_default_port based on code review

* Remove Callable type import

* Simplify port setup
2018-09-10 16:54:17 +02:00
mrosseel
f858938ada Make the Qnap sensor more resilient if server is not reachable (#16445)
* add CONF_ALLOW_UNREACHABLE option, retry connection if allowed

* fix linting errors

* Removing the config option, just using PlatformNotReady
2018-09-10 16:19:17 +02:00
vikramgorla
e96635b5c1 bugfix - incorrect camera type and missing sensors when multiple netatmo cameras (#16490)
fixed get_camera_type as it was originally not consuming any input, was looping with all cameras and the first camera type was retutned, modified to call cameraType using provided camera name.
2018-09-10 16:13:05 +02:00
Fabian Affolter
d2d715faa8 Upgrade wakeonlan to 1.1.6 (#16512) 2018-09-10 16:07:31 +02:00
Franck Nijhof
7a5e828f6b Updates documentation repo URL in PR template (#16537) 2018-09-10 14:28:21 +02:00
Paulus Schoutsen
99f7b7f42d Version bump to 0.79.0dev0 2018-09-10 13:46:07 +02:00
Paulus Schoutsen
3f97944f6b Bumped version to 0.78.0b0 2018-09-10 13:44:56 +02:00
Paulus Schoutsen
1d6609e386 Update translations 2018-09-10 13:44:27 +02:00
Paulus Schoutsen
fb15dbf3ba Bump frontend to 20180910.0 2018-09-10 13:44:11 +02:00
Fabian Affolter
c20f147949 Upgrade keyring to 15.0.0 (#16536) 2018-09-10 13:35:47 +02:00
Fabian Affolter
8ed4d732ac Upgrade youtube_dl to 2018.09.10 (#16534) 2018-09-10 13:35:24 +02:00
Louis-Dominique Dubeau
a1578e3c6e Add a base_url configuration setting to tts. (#16478)
* Add a base_url configuration setting to tts.

* Remove the empty string as default value for base_urls

As requested in

https://github.com/home-assistant/home-assistant/pull/16478#pullrequestreview-153526144
2018-09-10 11:50:25 +02:00
Jason Hu
253e787a1b Upgrade aiohttp to 3.4.4 (#16486) 2018-09-10 10:39:51 +02:00
Fabian Affolter
0d7ee9b93b Order imports (#16515) 2018-09-09 14:26:06 +02:00
tadly
d6d4ff6888 adds listener for OnAVStart and OnAVChange (#16495)
With Kodi 18, OnPlay is called before an item actually started playing.
For this OnAVStart and OnAVChange have been introduced.
2018-09-09 09:55:57 +02:00
Jason Hu
4291bdc6b2 Move voluptuous-serialize to core requirement (#16507) 2018-09-09 09:49:51 +02:00
Anders Melchiorsen
7d590a6b93 Update pyHS100 to 0.3.3 (#16502) 2018-09-09 00:34:22 +02:00
Ville Skyttä
e3e3ed42ec Fix Netgear LTESensor docstring (#16501) 2018-09-08 23:22:41 +02:00
Paulus Schoutsen
e7b8d2e6df Update name legacy api password (#16455) 2018-09-08 22:10:42 +02:00
Fabian Affolter
9944c60311 Check if API key is valid and users available (#16494) 2018-09-08 18:33:41 +02:00
Varga Tamas
93143384a8 Restore status attribute for xiaomi_vacuum (#16366)
* Added new states and exposed state/state code received from xiaomi vacuum

* Restore status attribute for xiaomi_vacuum
2018-09-08 18:13:24 +02:00
Florian Werner
8a2bc99f63 Add rate of change to statistics sensor (#15632)
* always export max_age/min_age

* downgrade errors of missing data
on start with empty recorder database these errors are logged multiple times:
ERROR (MainThread) [homeassistant.components.sensor.statistics] mean requires at least one data point
ERROR (MainThread) [homeassistant.components.sensor.statistics] variance requires at least two data points

downgrade them to debug as they are not meaningful to end users

* add change_rate attribute
this calculates the average change rate of all data points

* simplify count, reorder attribute calculation

* reorder initialization

* reorder attribute names

* don't use min/max for min_age/max_age

* add test case

* style

* style

* sort constants

* init variables with None

* add precision config setting

* round to precision

* test round
2018-09-08 01:10:08 +02:00
Steven Looman
6433f2e2e3 Working on igd 2018-09-08 00:24:47 +02:00
Andreas Oberritter
50266e9b91 Support SNMPv3 and asyncio in snmp sensor (#14753)
* snmp sensor: Add asyncio support and reuse SnmpEngine object

* snmp sensor: Support protocol v3

* Fix lint issue
2018-09-07 20:51:18 +02:00
mvn23
7ad094b0a7 Add OpenTherm Gateway climate platform (#16299)
* Initial OpenTherm Gateway support.

* Fix coveragerc and requirements_all*.txt
Overall cleanup and polishing

* Make hound/flake/travis happy

* Basic improvements to comply with Home Assistant's style guidelines
Changed wording from "component" to more appropriate terms where necessary
Fixed small mistakes that snuck in during testing and/or due to my own ignorance ;)

* Fixed overwriting state property

* Fixed a bug with ROOM_SETPOINT_OVRD
Updated dependency pyotgw to latest version

* Remove unit_of_measurement from OpenThermGateway class

* Cleanup after previous commits

* Moved initialisation and configuration from async_setup_platform to async_added_to_hass

* Make travis happy

* Disable polling for this platform
Improve update flow

* Small improvements/optimisations
2018-09-07 18:16:19 +02:00
Fabian Affolter
a5715c48a4 Fix GitHub change to resolve conflicts (#16477) 2018-09-07 14:41:31 +02:00
Andreas Oberritter
d0f9d125a7 Support SNMPv3 and asyncio in snmp switch (#14754)
* snmp switch: Add asyncio support and reuse SnmpEngine object

* snmp switch: Support protocol v3
2018-09-07 13:28:42 +02:00
cpw
80c77b8696 Update radiotherm (#15031)
the two device_state_attributes don't return the current actual state (fan on/off, heat/cool), but rather the mode of the fan or thermostat (generally auto). As such this change makes the actual current state visible to the system, so you can do stuff when the fan turns on, for example. I suspect this is just a bug, but maybe it was intended NOT to be able to see the actual fan state?
2018-09-07 13:21:32 +02:00
Dom
1f73840aab Add Yale Smart Alarm component (#16377)
* Add Smart Alarm Component

* Lint fixes

* Update coverage

* Update coverage with the correct file name

* PR Fixes. Update client version (to include timeouts). Cleaned up clien tcreation and improved authentication failure error. Added state map to replace nasty if blocks.

* PR Fixes. Update client version (to include timeouts). Cleaned up clien tcreation and improved authentication failure error. Added state map to replace nasty if blocks.

* Remove test file added in error.

* PR Fixes
2018-09-07 12:22:10 +02:00
Greg Laabs
fbaa489533 Update license to official GitHub template (#16470)
* Delete LICENSE.md

* Create LICENSE.md
2018-09-07 10:11:51 +02:00
Tsvi Mostovicz
cff9b1bf7e Fix waze_travel_time component startup (#16465)
* Fix waze_travel_time component startup

* Remove @Throttle decorator as per pvizelli's request

* Make the linting gods happy again
2018-09-07 09:55:53 +02:00
Ioan Loosley
ce06229c42 Added Twitch v5 support to the twitch platform (#16428)
* code not working

* did it all all at once

* removing debug stuff

* fixing lint issues

* fixing lint issues

* I am blind

* I felt small(80 by somthing) was to small, especily with lovelace

* fixing silly mistake

* this shouldnt be here

* made some changes

* corrrected client_id

* corrrected client_id

* less returns needed

* Fixing Small bugs and making stuff snake case

* Tweaking
2018-09-06 14:08:39 +02:00
Daniel Høyer Iversen
3acb3a86cf update rfxtrx lib (#16463) 2018-09-06 12:50:55 +02:00
Fabian Affolter
db1bda2975 Upgrade Sphinx to 1.7.8 (#16459) 2018-09-06 12:31:30 +02:00
Fabian Affolter
2640db1522 Upgrade shodan to 1.10.1 (#16460) 2018-09-06 12:31:08 +02:00
PhracturedBlue
cf4b72e00e Fix camera proxy to not require api_password to function (#16450) 2018-09-06 10:51:16 +02:00
Daniel Høyer Iversen
5bd9be6252 switchbot (#16396)
* switchbot

* style

* use switchbot lib

* unnecessary import

* switchbot, rename variable update requirements_all

* add line
2018-09-06 10:02:37 +02:00
kbickar
e1084e3953 Upgrade sense library to 0.4.2 (#16429)
* Added error handling for sense API timeouts

* Moved imports in function

* Moved imports to more appropriate function

* Change exception to custom package version

* Updated sense_energy library to 0.4.2
2018-09-05 08:01:36 +02:00
Jason Hu
3afc983c05 Fix openuv.config_flow unit test (#16419) 2018-09-04 21:18:35 +02:00
Paulus Schoutsen
746f4ac158 Add context to scripts and automations (#16415)
* Add context to script helper

* Update script component

* Add context to automations

* Lint
2018-09-04 21:16:24 +02:00
Paul Annekov
e1501c83f8 Fix Mi Flora median calculation (#16085)
* fixed median was based on 1.5 minute interval, not 1 hour

* ignore median and set state from first value, when previous state was None

* update before add, removed unused 'retries' and 'ble_timeout', check if platform ready

* added missing blank line

* fixed too long line

* using modern python 3.5 features, changed comment to be less verbose

* continuation line fix

* removed DEFAULT_SCAN_INTERVAL in favor of existing SCAN_INTERVAL
2018-09-04 21:03:30 +02:00
Toon Willems
3bd12fcef6 Implement correct state for RFlink cover (#16304)
* implement correct state for rflink cover

* Fix linting error

* invert logic as local testing pointed out it should be reversed

* add period at the end to satisfy the linter
2018-09-04 11:15:02 +02:00
Daniel Høyer Iversen
85658b6dd1 Clean up dlink and some bug fix (#16346)
* Update dlink.py

* style

* style
2018-09-04 10:50:12 +02:00
Rohan Kapoor
e61ac1a4a1 Delegate mqtt topic match validation to the paho mqtt client (#16403)
* Delegate mqtt match topics to the paho mqtt client

* Fixing linting error with importing MQTTMatcher
2018-09-04 10:31:45 +02:00
Robert Svensson
8fa9992589 Service to load new deCONZ devices without restart (#16308)
* New service to load new devices from deCONZ without restarting HASS

* Do not use len to check if list is empty

* Add support for scenes to be updated as well

* Rework refresh devices method

* Fix test
2018-09-04 09:24:42 +02:00
Aaron Bach
f96aee2832 Add config flow for OpenUV (#16159)
* OpenUV config flow in place

* Test folder in place

* Owner-requested comments

* Tests

* More tests

* Owner-requested changes (part 1 of 2)

* Updated requirements

* Owner-requested changes (2 of 2)

* Removed unnecessary import

* Bumping Travis

* Updated requirements

* More requirements

* Updated tests

* Owner-requested changes

* Hound

* Updated docstring
2018-09-04 09:22:44 +02:00
Robert Svensson
7a6facc875 Device and entity registry remove config entry on unload (#16247)
* Test

* Ability to remove device

* Don't remove devices, instead remove config entry from device and entity registries

* Remove print

* Remove is not the same as unload

* Add tests

* Fix hound comment
2018-09-04 09:00:14 +02:00
9R
7ea482cb1d add ExpressBus icon key to sensor.mvg (#16387) 2018-09-04 08:48:03 +02:00
Rene Nulsch
a4aa30fc73 Fix SystemMonitor IP address sensor (#16394) 2018-09-04 08:46:04 +02:00
Russell Cloran
ba63a6abc0 zha: Bump to zigpy 0.2.0/bellows 0.7.0 (#16404) 2018-09-03 22:46:27 -07:00
Daniel Høyer Iversen
2252f4a257 Bug fix for Tibber (#16397) 2018-09-04 01:11:40 +02:00
Pawel
00cba29ae1 Support for playing radio preset by Onkyo media_player (#16258)
* Added support to play radio preset by play media

* Switch radio station by preset for Onkyo

* added SUPPORT_PLAY_MEDIA
2018-09-03 21:40:04 +02:00
Steven Looman
f4a54e2483 Changes after review 2018-09-03 21:16:45 +02:00
Paulus Schoutsen
cd473643fe Merge branch 'master' into dev 2018-09-03 14:17:00 +02:00
Paulus Schoutsen
4063b24ddb Merge pull request #16390 from home-assistant/rc
0.77.3
2018-09-03 14:15:54 +02:00
Paulus Schoutsen
46734b8409 Bumped version to 0.77.3 2018-09-03 13:22:49 +02:00
Paulus Schoutsen
b9c80a6bb3 Update translations 2018-09-03 13:21:47 +02:00
Paulus Schoutsen
a2a447b466 Update translations 2018-09-03 13:21:37 +02:00
Paulus Schoutsen
669b3458b9 Update frontend to 20180903.0 2018-09-03 13:21:16 +02:00
Paulus Schoutsen
bf29cbd381 Update frontend to 20180903.0 2018-09-03 13:20:57 +02:00
Daniel Høyer Iversen
1966597d5e add_entities for switchmate (#16368) 2018-09-02 23:05:48 +02:00
Jason Hu
4685a2cd97 Update server.py (#16375) 2018-09-02 22:17:29 +02:00
Fabian Affolter
78fcea25bb Upgrade attrs to 18.2.0 (#16372) 2018-09-02 19:01:43 +02:00
Fabian Affolter
ac3700d1c4 Upgrade python-telegram-bot to 11.0.0 (#16373) 2018-09-02 19:01:25 +02:00
Totoo
03480dc779 Update discord.py (#16248)
* Update discord.py

* Update discord.py

Fixed ATTR_IMAGES checking, line length, and ATTR_DATA imported. Also fixed missing spaces.

* Update discord.py

Fix E302...
2018-09-02 18:58:31 +02:00
Steven Looman
85c40d29ee Linting 2018-09-02 17:03:31 +02:00
Martin Fuchs
a5cff9877e Add support for Tahoma Lighting Receiver on/off io (#15925)
* Add support for Tahoma light switch

* Clean up attributes and add available method

* Remove else statement
2018-09-02 17:02:51 +02:00
Lev Aronsky
b29c296ced Generic Thermostat: add support for climate.turn_on/climate.turn_off (#16080)
* Added async_turn_on and async_turn_off implementations.

* Added turning on/off tests to generic thermostat

* style

* style

* style
2018-09-02 16:42:08 +02:00
Steven Looman
a4a175440b Move ensure_domain_data 2018-09-02 16:40:39 +02:00
MarcSN311
357e5eadb8 Added 'nomapnt', 'outcurnt', 'loadapnt' fields (#16176)
* Added 'nomapnt', 'outcurnt', 'loadapnt' fields

Also added Ampere and Volt-Ampere to INFERRED_UNITS

* Fix lint issue
2018-09-02 15:51:15 +02:00
Fabian Affolter
52e922171d Upgrade to youtube_dl to 2018.09.01 (#16365) 2018-09-02 14:33:20 +02:00
Fabian Affolter
97695a30f5 Upgrade shodan to 1.10.0 (#16363) 2018-09-02 12:51:56 +02:00
Fabian Affolter
1d12c7b0e7 Upgrade mutagen to 1.41.1 (#16361) 2018-09-02 12:51:36 +02:00
Fabian Affolter
15ad82b9bd Upgrade qnapstats to 0.2.7 (#16360) 2018-09-02 12:51:25 +02:00
Fabian Affolter
03fb2b32a6 Upgrade Sphinx to 1.7.7 (#16359) 2018-09-02 12:51:07 +02:00
Jason Hu
87eb6cd25a Upgrade hbmqtt to 0.9.4 (#16356)
* Upgrade to hbmqtt 0.9.4

* Lint

* Typo
2018-09-02 12:50:30 +02:00
Tod Schmidt
3797b6b012 Snips: Added special slot values, session_id and slotname_raw (#16185)
* Added special slot values, site_id, session_id, and slotname_raw

* Update snips.py
2018-09-02 00:01:11 +02:00
Jesse Rizzo
2b0b431a2a Update to EnvoyReader 0.2, support for more hardware (#16212)
* Add support for older Envoy models

* Stop requiring envoy model name in config

* Update to envoy_reader0.2

* Minor formatting fixes

* run script/gen_requirements_all.py

* Minor formatting fixes

* Change some strings to constants, use getattr to call function
2018-09-01 23:45:47 +02:00
Maciej Bieniek
e75a1690d1 Add unique_id to MQTT Light (#16303)
* Add unique_id

* Delete whitespaces
2018-09-01 23:37:03 +02:00
Joshi
444df5b09a Add support for sound_mode for Yamaha rxv media_player (#16352) 2018-09-01 23:34:38 +02:00
Daniel Høyer Iversen
b31890c4cb Handle netatmo exception (#16344) 2018-09-01 23:30:34 +02:00
Steven Looman
8bec4a55d1 Working on igd 2018-09-01 21:20:15 +02:00
Phil Bruckner
a5d95dfbdc Make last_seen attribute a timezone aware datetime in UTC (#16348)
The last_seen attribute was a datetime in the local timezone but with
no tzinfo (i.e., a "naive" datetime.) When state changes occurred it
would be printed incorrectly in homeassistant.log because
homeassistant.util.dt.as_local assumes any datetime without tzinfo is
UTC. Also most, if not all, datetime attributes are timezone aware in
UTC. So use homeassistant.util.dt.as_utc (which assumes a naive
datetime is local) to convert last_seen to a timezone aware datetime
in UTC.
2018-09-01 18:49:03 +02:00
Steven Looman
50f63ed4c5 Fix not being able to re-add IGD when removed 2018-09-01 18:13:45 +02:00
Philipp Temminghoff
901cfef78e Support Sonos Beam HDMI input (#16340) 2018-09-01 16:02:38 +02:00
Daniel Perna
2c7d6ee6b5 Fix missing humidity sensor (#16337) 2018-09-01 10:40:16 +02:00
Steven Looman
c9e34e236d Prevent error message when component is actually ok 2018-09-01 10:14:21 +02:00
Steven Looman
38318ed15f Ensure config_flow is imported 2018-09-01 10:09:22 +02:00
Steven Looman
553d3c0179 Merge remote-tracking branch 'upstream/dev' into igd 2018-09-01 10:01:12 +02:00
Tom Harris
d3791fa45d Add Cover to the Insteon component (#16215)
* Create cover platform

* Create insteon cover platform

* Bump insteonplm to 0.13.0

* Change async_add_devices to async_add_entities

* Missing doc string

* Simplify open and set_position

* Flake8

* Bump insteonplm to 0.13.1

* Code review changes

* Flake8 updates
2018-08-31 23:56:26 +02:00
Matt Schmitt
fa81385b5c Add unique ID (#16323) 2018-08-31 16:47:37 +02:00
thomaslian
7d852a985c Upgrade Adafruit-DHT to 1.3.4 (#16327)
* Update dht.py

* Update requirements_all.txt
2018-08-31 16:47:10 +02:00
Paulus Schoutsen
976626d0ab Merge pull request #16324 from home-assistant/rc
0.77.2
2018-08-31 13:59:48 +02:00
Paulus Schoutsen
d705375a9a Update translations 2018-08-31 13:23:57 +02:00
Paulus Schoutsen
5e8a1496d7 Update translations 2018-08-31 13:23:22 +02:00
Paulus Schoutsen
bc618af193 Bumped version to 0.77.2 2018-08-31 13:17:24 +02:00
Malte Franken
0e076fb9e7 avoid error in debug log mode and rss entry without title (#16316) 2018-08-31 13:12:17 +02:00
Anders Melchiorsen
16a58bd1cf Fix LIFX effects (#16309) 2018-08-31 13:12:16 +02:00
lamiskin
8be7a0a9b9 Correct wemo static device discovery issue. (#16292)
A recent change caused an issue if a single static wemo device is offline and could not be reached, then the whole component would not initialize (and therefore all other wemo devices are not added).
2018-08-31 13:12:16 +02:00
Paulus Schoutsen
232076b41d Update frontend to 20180831.0 2018-08-31 12:59:57 +02:00
Paulus Schoutsen
efa9c82c38 Update frontend to 20180831.0 2018-08-31 12:59:39 +02:00
lamiskin
93f45779c6 Correct wemo static device discovery issue. (#16292)
A recent change caused an issue if a single static wemo device is offline and could not be reached, then the whole component would not initialize (and therefore all other wemo devices are not added).
2018-08-31 12:57:07 +02:00
Malte Franken
26d39d39ea avoid error in debug log mode and rss entry without title (#16316) 2018-08-31 12:54:25 +02:00
Anders Melchiorsen
b43c47cb17 Fix LIFX effects (#16309) 2018-08-31 10:17:11 +02:00
PhracturedBlue
67d8db2c9f Use asterisk_mbox 0.5.0 client (#16296) 2018-08-30 18:44:37 +02:00
Teemu R
3cbf8e4f87 Bump songpal dependency (#16297)
Fixes #14936
2018-08-30 18:21:37 +02:00
Steven Looman
20879726b0 Working on IGD 2018-08-30 17:41:11 +02:00
Malte Franken
f20a3313b0 Geo Location component (#15953)
* initial working version of a geo location component and georss platform

* ensure that custom attributes don't override built-in ones

* bugfixes and tests

* fixing tests because of introduction of new component using same fixture

* improving test cases

* removing potentially unavailable attribute from debug message output

* completing test suite

* cleaning up debug messages; sorting entries in group view by distance

* ability to define the desired state attribute and corresponding unit of measurement; sort devices in group by configured state; find centroid for map if event is defined by polygon; updated tests

* sort entries in group; code clean-ups

* fixing indentation

* added requirements of new component and platform

* fixed various lint issues

* fixed more lint issues

* introducing demo geo location platform; refactored geo location component and geo rss platform to fit

* removing geo rss events platform; added unit tests for geo location platform and demo platform

* reverting change in debug message for feedreader to avoid confusion with new geo location component

* updated requirements after removing georss platform

* removed unused imports

* fixing a lint issue and a test case

* simplifying component code; moving code into demo platform; fixing tests

* removed grouping from demo platform; small refactorings

* automating the entity id generation (the use of an entity namespace achieves the same thing)

* undoing changes made for the georss platform

* simplified test cases

* small tweaks to test case

* rounding all state attribute values

* fixing lint; removing distance from state attributes

* fixed test

* renamed add_devices to add_entities; tweaked test to gain more control over the timed update in the demo platform

* reusing utcnow variable instead of patched method

* fixed test by avoiding to make assumptions about order of list of entity ids

* adding test for the geo location event class
2018-08-30 13:58:23 +02:00
Robbie Trencheny
54c3f4f001 Fix spelling mistake in recorder migration [ci skip] 2018-08-29 14:59:48 -07:00
Paulus Schoutsen
7289d5b656 Merge branch 'master' into dev 2018-08-29 23:37:53 +02:00
Paulus Schoutsen
e77344f029 Merge pull request #16284 from home-assistant/rc
0.77.1
2018-08-29 23:33:53 +02:00
Paulus Schoutsen
60438067f8 Bumped version to 0.77.1 2018-08-29 23:22:56 +02:00
Conrad Juhl Andersen
9062de0704 Fix error when vacuum is idling (#16282) 2018-08-29 23:22:44 +02:00
Sebastian Muszynski
64453638bb Fix data_key override by parent class (#16278) 2018-08-29 23:22:43 +02:00
Paulus Schoutsen
5f1282a4ab Bump frontend to 20180829.1 2018-08-29 23:22:21 +02:00
Robert Svensson
645c3a67d8 Fix so that entities are properly unloaded with config entry (#16281) 2018-08-29 23:18:20 +02:00
Conrad Juhl Andersen
88f72a654a Fix error when vacuum is idling (#16282) 2018-08-29 23:17:18 +02:00
Paulus Schoutsen
87df102772 Bump frontend to 20180829.1 2018-08-29 22:59:55 +02:00
Sebastian Muszynski
25ee8e551c Fix data_key override by parent class (#16278) 2018-08-29 22:29:34 +02:00
Steven Looman
3db766e2ec Merge remote-tracking branch 'upstream/dev' into igd 2018-08-29 21:19:27 +02:00
Steven Looman
1eac6408f5 Working on IGD 2018-08-29 21:19:04 +02:00
Pavel Pletenev
99d48795b9 Add support for Habitica (#15744)
* Added support for Habitica

Second refactoring
Moved all config to component.
Sensors are autodiscovered.
Signed-off-by: delphi <cpp.create@gmail.com>

* Apply requested changes

Signed-off-by: delphi <cpp.create@gmail.com>

* Made event fire async. Made `sensors` config implicit and opt-out-style.

Signed-off-by: delphi <cpp.create@gmail.com>

* Removed unneeded check and await.

Signed-off-by: delphi <cpp.create@gmail.com>

* Moved into separate component package and added service.yaml

Signed-off-by: delphi <cpp.create@gmail.com>

* Fix coveralls

Signed-off-by: delphi <cpp.create@gmail.com>
2018-08-29 21:13:01 +02:00
Jason Hu
5681fa8f07 Nest Thermostat has software version (#16275) 2018-08-29 13:00:40 -06:00
Paulus Schoutsen
867d17b03d Add Hue device info (#16267)
* Add Hue device info

* Set with tuples

* Fix tests
2018-08-29 17:04:04 +02:00
Paulus Schoutsen
7751dd7535 Add device info Nest (#16265)
* Add device info Nest

* Sets
2018-08-29 16:44:10 +02:00
Paulus Schoutsen
16a885824d Add device info for sonos (#16263)
* Add device info for sonos

* Sets
2018-08-29 16:27:08 +02:00
Paulus Schoutsen
3934f7bf3a Add device info to Chromecast (#16261) 2018-08-29 15:46:09 +02:00
cgtobi
96cf6d59a3 Replace Authorization by Authentication (#16259) 2018-08-29 15:43:01 +02:00
Diogo Gomes
d46a1a266d bump version (#16262) 2018-08-29 15:32:47 +02:00
Matt Schmitt
aaa1ebeed5 Add support for discrete states to MyQ cover (#16251)
* Add discrete states and update dependency

* Add translation dict
2018-08-29 14:33:09 +02:00
Daniel Høyer Iversen
18ba50bc2d Switchmate (#15535)
* Switchmate

* switchmate

* swithcmate

* switchmate

* switchmate

* fix comments

* Update switchmate.py

* change error log
2018-08-29 12:56:15 +02:00
Paulus Schoutsen
3df8840fee Version bump to 0.78.0.dev0 2018-08-29 12:20:05 +02:00
Paulus Schoutsen
630b5df59a Merge branch 'master' into dev 2018-08-29 12:19:30 +02:00
Paulus Schoutsen
9db15aab92 Merge pull request #16256 from home-assistant/rc
0.77
2018-08-29 12:10:44 +02:00
Paulus Schoutsen
f01e1ef0aa Version bump to 0.77.0 2018-08-29 10:29:51 +02:00
Robert Svensson
b5919ce92c def device shouldnt call it self but self._device (#16255) 2018-08-29 10:29:24 +02:00
Jason Hu
f9b1fb5906 Tweak MFA login flow (#16254)
* Tweak MFA login flow

* Fix typo
2018-08-29 10:29:24 +02:00
Paulus Schoutsen
9238261e17 Update translations 2018-08-29 10:28:44 +02:00
Paulus Schoutsen
e8801ee22f Update translations 2018-08-29 10:28:34 +02:00
Paulus Schoutsen
8ec109d255 Bump frontend to 20180829.0 2018-08-29 10:28:11 +02:00
Paulus Schoutsen
74c0429437 Bump frontend to 20180829.0 2018-08-29 10:27:34 +02:00
Jason Hu
563588651c Tweak MFA login flow (#16254)
* Tweak MFA login flow

* Fix typo
2018-08-29 10:16:54 +02:00
Robert Svensson
63614a477a def device shouldnt call it self but self._device (#16255) 2018-08-29 10:07:32 +02:00
Paulus Schoutsen
2ea2bcab77 Bumped version to 0.77.0b4 2018-08-28 20:59:38 +02:00
Jason Hu
8d38016b0c Blow up startup if init auth providers or modules failed (#16240)
* Blow up startup if init auth providers or modules failed

* Delete core.entity_registry
2018-08-28 20:59:30 +02:00
Jason Hu
d994d6bfad Change log level to error when auth provider failed loading (#16235) 2018-08-28 20:59:29 +02:00
Paulus Schoutsen
573f5de148 Avoid insecure pycryptodome (#16238) 2018-08-28 20:57:46 +02:00
Paulus Schoutsen
667f9c6fe4 Package loadable: compare case insensitive (#16234) 2018-08-28 20:57:46 +02:00
Paulus Schoutsen
f708292015 Warning missed a space (#16233) 2018-08-28 20:57:45 +02:00
Paulus Schoutsen
c50a7deb92 Fix hangouts (#16232) 2018-08-28 20:57:45 +02:00
Paulus Schoutsen
11fcffda4c Update translations 2018-08-28 20:56:12 +02:00
Paulus Schoutsen
f891d0f5be Update translations 2018-08-28 20:55:58 +02:00
Jason Hu
257b8b9b80 Blow up startup if init auth providers or modules failed (#16240)
* Blow up startup if init auth providers or modules failed

* Delete core.entity_registry
2018-08-28 20:54:01 +02:00
Paulus Schoutsen
9a786e449b Fix hangouts (#16232) 2018-08-28 15:44:06 +02:00
Paulus Schoutsen
09dc4d663d Improve package loadable (#16237)
* Add caching to package loadable

* Fix tests

* Improve package loadable

* Lint

* Typing
2018-08-28 12:52:18 +02:00
Paulus Schoutsen
12709ceaa3 Avoid insecure pycryptodome (#16238) 2018-08-28 12:49:50 +02:00
Jason Hu
67df162bcc Change log level to error when auth provider failed loading (#16235) 2018-08-28 11:23:58 +02:00
Paulus Schoutsen
a14980716d Package loadable: compare case insensitive (#16234) 2018-08-28 10:53:12 +02:00
Paulus Schoutsen
376d4e4fa0 Warning missed a space (#16233) 2018-08-28 09:32:50 +02:00
Paulus Schoutsen
e9cc359abe Bumped version to 0.77.0b3 2018-08-28 00:38:23 +02:00
Paulus Schoutsen
9b01972b41 Update trusted networks flow (#16227)
* Update the trusted networks flow

* Fix tests

* Remove errors
2018-08-28 00:38:08 +02:00
Paulus Schoutsen
3e65009ea9 Fix device telldus (#16224) 2018-08-28 00:38:08 +02:00
Marcel Hoppe
a953601abd rewrite hangouts to use intents instead of commands (#16220)
* rewrite hangouts to use intents instead of commands

* small fixes

* remove configured_hangouts check and CONFIG_SCHEMA

* Lint

* add import from .config_flow
2018-08-28 00:38:07 +02:00
Paulus Schoutsen
2744702f9b Change auth warning (#16216) 2018-08-28 00:38:07 +02:00
Dan Klaffenbach
9c7d4381a1 homematic: Make device avilable again when UNREACH becomes False (#16202) 2018-08-28 00:38:06 +02:00
Paulus Schoutsen
5397c0d73a Update trusted networks flow (#16227)
* Update the trusted networks flow

* Fix tests

* Remove errors
2018-08-28 00:37:15 +02:00
Robert Svensson
8ab31fe139 Store devices as dict instead of list (#16229)
* Store devices as dict instead of list

* Use OrderedDict
2018-08-28 00:37:04 +02:00
Marcel Hoppe
45649824ca rewrite hangouts to use intents instead of commands (#16220)
* rewrite hangouts to use intents instead of commands

* small fixes

* remove configured_hangouts check and CONFIG_SCHEMA

* Lint

* add import from .config_flow
2018-08-28 00:20:12 +02:00
Paulus Schoutsen
914436f3d5 Bump frontend to 20180827.0 2018-08-27 22:28:37 +02:00
Paulus Schoutsen
6f0c30ff84 Bump frontend to 20180827.0 2018-08-27 22:28:17 +02:00
Fabian Affolter
943260fcd6 Upgrade alpha_vantage to 2.1.0 (#16217) 2018-08-27 22:00:20 +02:00
Paulus Schoutsen
24aa580b63 Fix device telldus (#16224) 2018-08-27 21:56:28 +02:00
Daniel Bowman
8435d2f53d openalpr flag WITH_TEST should be WITH_TESTS (#16218)
Removes warning from openalpr build and saves a few seconds from build
time as tests weren't being bypassed as intended
2018-08-27 17:17:43 +02:00
Fabian Affolter
c51170ef6d Add Volkszaehler sensor (#16188)
* Add Volkszaehler sensor

* Update icons

* Improve code
2018-08-27 15:05:36 +02:00
Paulus Schoutsen
9d491f5322 Change auth warning (#16216) 2018-08-27 10:37:03 +02:00
Paulus Schoutsen
adb5579690 Update translations 2018-08-27 10:17:10 +02:00
Paulus Schoutsen
94662620e2 Update translations 2018-08-27 10:16:59 +02:00
Paulus Schoutsen
f1e378bff8 Add new translations 2018-08-27 09:48:17 +02:00
Julian Kahnert
2e9db1f5c4 Fix geizhals price parsing (#15990)
* fix geizhals price parsing

* Fix lint issue

* switch to the geizhals pypi package

* throttle updates

* update geizhals version

* initialize empty device

* minor changes to trigger another TravisCI test

* device => _device

* bump geizhals version
2018-08-27 09:39:11 +02:00
Hunter Horsman
dec2d8d5b0 Add device_tracker.bluetooth_update service (#15252)
* Add device_tracker.bluetooth_update service

Will immediately scan for Bluetooth devices outside of the interval timer. Allows for less frequent scanning, with scanning on demand via automation.

* remove excess whitespace per bot comments

* Refactored update_bluetooth to call new function update_bluetooth_once

* Change service name to bluetooth_tracker_update to reflect platform name

* Reformat for line length

* Linting fix, pydoc, first line should end with a period

* Fixed a method call, and removed some more unsused parameters
2018-08-27 09:08:23 +02:00
Jonas Karlsson
a439690bd7 Rewrite of Trafikverket weather - Multiple sensor types supported (#15935)
* Added precipitation type from API

Enables users to see type of precipitation.
Value returned from API is a string in swedish.

* Corrected tox verification errors

Correction of tox findings

* Missed in tox - fixed

* Hound witespace fix

* Updated comment to trigger travis rebuild

Travis tox failed due to problem with tox build process. 
Correcting in a comment to trigger retry in travis..

* Try to retrigger travis/tox successful rebuild

* Cleaning

* Cleaning more

* Trafikverket rebuilt for library

Extended pytrafikverket with weather sensor collction
Changed behaviour of sensor component to use pytrafikverket.
Added more sensors.

User need to change config to use new version.
 [] Documentation needs to be updated

* Cleaned up based on Martins input

Appreciate the feedback
2018-08-27 06:19:51 +02:00
Maikel Punie
5d7a2f92df Add temperature sensors to the velbus component (#16203)
* Added support for velbus temperature sensors

* Bumped the required version

* updated requirements_all.txt

* Auto review comments fixed

* Updated after comments

* Updated after comments

* Fix travis

* Fix travis
2018-08-27 06:06:46 +02:00
Paulus Schoutsen
8413101148 Bumped version to 0.77.0b2 2018-08-26 22:53:20 +02:00
Paulus Schoutsen
8fb66c351e Add new translations 2018-08-26 22:53:08 +02:00
Paulus Schoutsen
16ad9c2ae6 Update translations 2018-08-26 22:53:08 +02:00
Paulus Schoutsen
4da719f43c Update translations 2018-08-26 22:52:21 +02:00
Robert Svensson
2ad938ed44 Revert changes to platforms using self.device (#16209)
* Revert tank_utility

* Fix Soundtouch

* Fix Plex

* Fix Emby

* Fix Radiotherm

* Fix Juicenet

* Fix Qwikswitch

* Fix Xiaomi miio

* Fix Nest

* Fix Tellduslive

* Fix KNX
2018-08-26 22:51:19 +02:00
Penny Wood
969b15a297 Update aiohttp to version 3.4.0. (#16198) 2018-08-26 22:51:18 +02:00
Marcel Hoppe
c8449d8f8a remove hangouts.users state, simplifies hangouts.conversations (#16191) 2018-08-26 22:51:18 +02:00
PhracturedBlue
6992a6fe6d Handle exception from pillow (#16190) 2018-08-26 22:51:17 +02:00
Jason Hu
2ece671bfd Add Time-based Onetime Password Multi-factor Authentication Module (#16129)
* Add Time-based Onetime Password Multi-factor Auth

Add TOTP setup flow, generate QR code

* Resolve rebase issue

* Use svg instead png for QR code

* Lint and typing

* Fix translation

* Load totp auth module by default

* use <svg> tag instead markdown image

* Update strings

* Cleanup
2018-08-26 22:51:17 +02:00
Matt Hamilton
c13e5fcb92 Replace pbkdf2 with bcrypt (#16071)
* Replace pbkdf2 with bcrypt

bcrypt isn't inherently better than pbkdf2, but everything "just works"
out of the box.

  * the hash verification routine now only computes one hash per call
  * a per-user salt is built into the hash as opposed to the current
  global salt
  * bcrypt.checkpw() is immune to timing attacks regardless of input
  * hash strength is a function of real time benchmarks and a
  "difficulty" level, meaning we won't have to ever update the iteration
  count

* WIP: add hash upgrade mechanism

* WIP: clarify decode issue

* remove stale testing code

* Fix test

* Ensure incorrect legacy passwords fail

* Add better invalid legacy password test

* Lint

* Run tests in async scope
2018-08-26 22:51:16 +02:00
Matt Hamilton
bacecb4249 Replace pbkdf2 with bcrypt (#16071)
* Replace pbkdf2 with bcrypt

bcrypt isn't inherently better than pbkdf2, but everything "just works"
out of the box.

  * the hash verification routine now only computes one hash per call
  * a per-user salt is built into the hash as opposed to the current
  global salt
  * bcrypt.checkpw() is immune to timing attacks regardless of input
  * hash strength is a function of real time benchmarks and a
  "difficulty" level, meaning we won't have to ever update the iteration
  count

* WIP: add hash upgrade mechanism

* WIP: clarify decode issue

* remove stale testing code

* Fix test

* Ensure incorrect legacy passwords fail

* Add better invalid legacy password test

* Lint

* Run tests in async scope
2018-08-26 22:50:31 +02:00
Jason Hu
47755fb1e9 Add Time-based Onetime Password Multi-factor Authentication Module (#16129)
* Add Time-based Onetime Password Multi-factor Auth

Add TOTP setup flow, generate QR code

* Resolve rebase issue

* Use svg instead png for QR code

* Lint and typing

* Fix translation

* Load totp auth module by default

* use <svg> tag instead markdown image

* Update strings

* Cleanup
2018-08-26 22:38:52 +02:00
Penny Wood
69d104bcb6 Update aiohttp to version 3.4.0. (#16198) 2018-08-26 21:35:06 +02:00
Paulus Schoutsen
3783d1ce90 Update frontend to 20180826.0 2018-08-26 21:30:29 +02:00
Paulus Schoutsen
b043ac0f7f Update frontend to 20180826.0 2018-08-26 21:30:14 +02:00
PhracturedBlue
499bb3f4a2 Handle exception from pillow (#16190) 2018-08-26 21:29:15 +02:00
Marcel Hoppe
d166f2da80 remove hangouts.users state, simplifies hangouts.conversations (#16191) 2018-08-26 21:28:42 +02:00
Antoine GRÉA
3032de1dc1 Inconsistent entity_id when multiple sensors (#16205)
* Inconsistent entity_id when multiple sensors

I am submitting a change to fix a [bug](https://github.com/home-assistant/home-assistant/issues/16204) for when there are several sensors for the same hostname. For example I want to track my IPv4 and IPv6 address. It creates two entities that regularly switch ids based on the order they get initialized.

To fix this I comform to the way other componnents have addressed the issue by adding an optional `name` attribute.

* Line too long

* Removing trailing whitespace
2018-08-26 21:27:03 +02:00
Robert Svensson
5341785aae Revert changes to platforms using self.device (#16209)
* Revert tank_utility

* Fix Soundtouch

* Fix Plex

* Fix Emby

* Fix Radiotherm

* Fix Juicenet

* Fix Qwikswitch

* Fix Xiaomi miio

* Fix Nest

* Fix Tellduslive

* Fix KNX
2018-08-26 21:25:39 +02:00
Martin Fuchs
289b1802fd Add battery warning, rssi level and check for availability (#16193) 2018-08-26 21:20:34 +02:00
Fabian Affolter
0da3e73765 Upgrade sqlalchemy to 1.2.11 (#16192) 2018-08-26 12:28:44 +02:00
Dan Klaffenbach
0a7055d475 homematic: Make device avilable again when UNREACH becomes False (#16202) 2018-08-26 12:00:20 +02:00
Matthias Urlichs
a1ce14e70f MQTT: Log transmitted as well as received messages (#16195) 2018-08-26 10:04:51 +02:00
Thomas Delaet
2f2bcf0058 update python-velbus library version (#16194) 2018-08-25 20:42:26 +02:00
djm300
f929c38e98 Zoneminder SSL fix (#16157)
* Update zoneminder.py

Added a verify_ssl parameter for zoneminder

* PEP8 fixup

* PEP8 indenting fix

* Fix lint issue

* Remove whitespace
2018-08-25 11:21:57 +02:00
Paulus Schoutsen
9ffcd2d86a Bumped version to 0.77.0b1 2018-08-25 11:16:01 +02:00
Jason Hu
66a8bede12 Default load trusted_network auth provider if configured trusted networks (#16184) 2018-08-25 11:15:55 +02:00
Jason Hu
c2891b9905 Tweak log level for bearer token warning (#16182) 2018-08-25 11:15:54 +02:00
Paulus Schoutsen
b8c272258e Fix hangouts (#16180) 2018-08-25 11:15:54 +02:00
Nate Clark
4cb9ac72b4 fix error message for cv.matches_regex (#16175) 2018-08-25 11:15:53 +02:00
Robert Svensson
cf8bd92d4d Device registry store config entry (#16152)
* Allow device registry to optionally store config entries

* Connections and identifiers are now sets with tupels

* Make config entries mandatory

* Fix duplicate keys in test

* Rename device to device_info

* Entity platform should only create device entries if config_entry_id exists

* Fix Soundtouch tests

* Revert soundtouch to use self.device

* Fix baloobs comments

* Correct type in test
2018-08-25 11:15:53 +02:00
Nate Clark
90b2257347 Decouple Konnected entity setup from discovery (#16146)
* decouple entity setup from discovery

* validate that device_id is a full MAC address
2018-08-25 11:15:52 +02:00
Jason Hu
914d90a2bc Add multi-factor auth module setup flow (#16141)
* Add mfa setup flow

* Lint

* Address code review comment

* Fix unit test

* Add assertion for WS response ordering

* Missed a return

* Remove setup_schema from MFA base class

* Move auth.util.validate_current_user -> webscoket_api.ws_require_user
2018-08-25 11:15:52 +02:00
Robert Svensson
e567b2281d deCONZ - Support device registry (#16115)
Add support for device registry in deCONZ component
2018-08-25 11:15:51 +02:00
Paulus Schoutsen
bb6567f84c Bump frontend to 20180825.0 2018-08-25 11:15:19 +02:00
Paulus Schoutsen
617802653f Bump frontend to 20180825.0 2018-08-25 11:15:01 +02:00
Jason Hu
26a485d43c Default load trusted_network auth provider if configured trusted networks (#16184) 2018-08-25 11:09:48 +02:00
Paulus Schoutsen
456aa5a2b2 Fix hangouts (#16180) 2018-08-25 11:01:32 +02:00
Robert Svensson
97173f495c Device registry store config entry (#16152)
* Allow device registry to optionally store config entries

* Connections and identifiers are now sets with tupels

* Make config entries mandatory

* Fix duplicate keys in test

* Rename device to device_info

* Entity platform should only create device entries if config_entry_id exists

* Fix Soundtouch tests

* Revert soundtouch to use self.device

* Fix baloobs comments

* Correct type in test
2018-08-25 10:59:28 +02:00
Jason Hu
24a8d60566 Tweak log level for bearer token warning (#16182) 2018-08-25 07:57:36 +02:00
Fabian Affolter
69cea6001f Add 'moon_phase' to Dark Sky sensor (#16179) 2018-08-24 17:05:53 -06:00
Nate Clark
647b3ff0fe Decouple Konnected entity setup from discovery (#16146)
* decouple entity setup from discovery

* validate that device_id is a full MAC address
2018-08-24 23:29:25 +02:00
Nate Clark
84365cde07 fix error message for cv.matches_regex (#16175) 2018-08-24 23:27:12 +02:00
Robert Svensson
e91a1529e4 deCONZ - Support device registry (#16115)
Add support for device registry in deCONZ component
2018-08-24 19:37:22 +02:00
Jason Hu
e8775ba2b4 Add multi-factor auth module setup flow (#16141)
* Add mfa setup flow

* Lint

* Address code review comment

* Fix unit test

* Add assertion for WS response ordering

* Missed a return

* Remove setup_schema from MFA base class

* Move auth.util.validate_current_user -> webscoket_api.ws_require_user
2018-08-24 10:17:43 -07:00
Paulus Schoutsen
4f8fec6494 Bumped version to 0.77.0b0 2018-08-24 17:03:05 +02:00
Paulus Schoutsen
57979faa9c Merge remote-tracking branch 'origin/master' into dev 2018-08-24 17:02:44 +02:00
Paulus Schoutsen
994b829cb4 add_devices -> add_entities (#16171)
* add_devices -> add_entities

* Lint

* PyLint

* Revert external method in scsgate
2018-08-24 16:37:30 +02:00
Robert Svensson
37fd438717 deCONZ - Allow sub second light transitions (#16170)
Solves https://github.com/home-assistant/home-assistant/issues/16075
2018-08-24 16:15:28 +02:00
Adam Mills
5e301dd599 Hangouts localization typo fix (#16174) 2018-08-24 16:13:58 +02:00
Paulus Schoutsen
3d5b3fb6ff Update translations 2018-08-24 15:54:47 +02:00
Paulus Schoutsen
6c5f98668e Bump frontend to 20180824.0 2018-08-24 15:54:04 +02:00
Steven Looman
e73f31d829 Merge remote-tracking branch 'upstream/dev' into igd 2018-08-24 15:25:07 +02:00
Marcel Hoppe
ef0eab0f40 Hangouts (#16049)
* add a component for hangouts

* add a notify component for hangouts

* add an extra message as title

* add support to listen to all conversations hangouts has

* move hangouts to package and add parameter documentation

* update .coveragerc and requirements_all.txt

* makes linter happy again

* bugfix

* add conversations parameter to command words

* Move the resolution of conversation names to conversations in own a function

* typo

* rename group of exclusion form 'id' to 'id or name'

* refactoring and use config_flow

* makes linter happy again

* remove unused imports

* fix not working regex commands

* fix translations

* cleanup

* remove step_init

* remove logging entry

* clean up events

* move constant

* remove unsed import

* add new files to .converagerc

* isort imports

* add hangouts_utils to ignored packages

* upadte doc and format

* fix I/O not in executor jon

* rename SERVICE_UPDATE_USERS_AND_CONVERSATIONS to SERVICE_UPDATE

* move EVENT_HANGOUTS_{CONNECTED,DISCONNECTED} to dispatcher

* add config flow tests

* Update tox.ini
2018-08-24 10:39:35 +02:00
Ville Skyttä
dd9d53c83e Update pydocstyle to 2.1.1 and flake8-docstrings to 1.3.0 (#14557)
* Update pydocstyle to 2.1.1 and flake8-docstrings to 1.3.0

* Pydocstyle D401 fixes
2018-08-24 10:28:43 +02:00
Ville Skyttä
89d856d147 Spelling fixes (#16150) 2018-08-23 22:56:18 +02:00
Paulus Schoutsen
156c6e2025 Remove commented out API password from default config (#16147) 2018-08-23 22:16:31 +02:00
Paulus Schoutsen
d21d7cef4c Enable auth by default 🙈 (#16107)
* Enable auth by default

* Only default legacy_api_password if api_password set

* Tweak bool check

* typing
2018-08-23 13:38:08 +02:00
Paulus Schoutsen
249981de96 Prevent legacy api password with empty password (#16127)
* Prevent legacy api password with empty password

* Typing
2018-08-23 12:56:01 +02:00
squidwardy
8e173f1658 Add support for JS modules in custom panels (#16096)
* Added backend support for JavaScript modules in custom panels.

* Fixed test_panel_custom.py

* Delete core.entity_registry

* Update panel_custom.py

* Corrected panel_custom.py with module_url.

* Rebase

* Missed elif

* Add vol.Exclusive module_url

* Correct vol.Exclusive usage

* Test for js module

* Corrected line continuation indentation

* Added webcomponent path to exclusive group

* Corrected line length

* Line break

* Test for conflicting url options

* Self -> hass fix

* Fix self -> hass again

* Use assert_setup_component

* Setup missing

* Correct test

* Fix again

* Fix

* Mising async

* Fix

* test real

* Test real

* Final

* check

* Final check

* safety

* Final commit and check

* Removed unused dependencies

* Test for multiple url options in config
2018-08-23 11:14:18 +02:00
Diogo Gomes
ced5eeacc2 Adds support for routers implementing IGDv2 (#16108)
* Adds support for IGDv2

* bump pyupnp_async

* bump pyupnp_async

* better debug

* fix test
2018-08-23 10:54:02 +02:00
JC Connell
4155e8a31f Add support for NOAA tide information (new PR) (#15947)
* Adding noaa-tides changes to new branch.

* Fix typo in .coverageac

* Incorporate @MartinHjelmare and @fabaff changes.

* Disable pylint error and add error message for unavailable station.

* Two spaces before inline comments

* Increment py_noaa version to 0.3.0

* Updated requirements.py

* Minor changes
2018-08-23 00:21:46 +02:00
Eduard van Valkenburg
4ad76d8916 Upgrade brunt package (#16130)
* Bumped package version to fix a bug with Python before 3.6

* update of package version in requirements_all
2018-08-22 20:12:54 +02:00
Sebastian Muszynski
478eb48e93 Fix the protocol v2 data_key of several aqara devices (#16112)
* Fix the protocol v2 data_key of several aqara devices

* Incorporate review
2018-08-22 15:29:43 +02:00
Fabian Affolter
d8ae079757 Upgrade youtube_dl to 2018.08.22 (#16125) 2018-08-22 13:55:48 +02:00
Fabian Affolter
b0c2d24997 Upgrade numpy to 1.15.1 (#16126) 2018-08-22 13:55:11 +02:00
Paulus Schoutsen
2e6cb2235c Check correctly if package is loadable (#16121) 2018-08-22 12:17:14 +02:00
Robert Svensson
0009be595c Device Registry (#15980)
* First draft

* Generate device id

* No obscure registry

* Dont store config_entry_id in device

* Storage

* Small mistake on rebase

* Do storage more like entity registry

* Improve device identification

* Add tests

* Remove deconz device support from PR

* Fix hound comments, voff!

* Fix comments and clean up

* Fix proper indentation

* Fix pydoc issues

* Fix mochad component to not use self.device

* Fix mochad light platform to not use self.device

* Fix TankUtilitySensor to not use self.device

* Fix Soundtouch to not use self.device

* Fix Plex to not use self.device

* Fix Emby to not use self.device

* Fix Heatmiser to not use self.device

* Fix Wemo lights to not use self.device

* Fix Lifx to not use self.device

* Fix Radiotherm to not use self.device

* Fix Juicenet to not use self.device

* Fix Qwikswitch to not use self.device

* Fix Xiaomi miio to not use self.device

* Fix Nest to not use self.device

* Fix Tellduslive to not use self.device

* Fix Knx to not use self.device

* Clean up a small mistake in soundtouch

* Fix comment from Ballob

* Fix bad indentation

* Fix indentatin

* Lint

* Remove unused variable

* Lint
2018-08-22 10:46:37 +02:00
Jason Hu
7e7f9bc6ac Add multi-factor authentication modules (#15489)
* Get user after login flow finished

* Add multi factor authentication support

* Typings
2018-08-22 09:52:34 +02:00
Jerad Meisner
ae63980152 Remove unit_of_measurement from climate entities (#16012)
* Remove unit_of_measurement from climate base class.

* Updated google_assistant component and tests to use core temp units.

* Fixes

* Convert Alexa component to use core temp units for climate entities.

* Fix tests.

* Converted prometheus component.

* Remove unit_of_measurement from homekit thermostat tests.

* Small fix.
2018-08-22 09:17:29 +02:00
Tom Harris
a31501d99e Merge insteon_plm and insteon_local to insteon component (#16102)
* 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

* Add host to config options

* Add username and password to config for hub connectivity

* Add username and password to config for hub

* Convert port to int if host is defined

* Add KeypadLinc

* Update config schema to require either port or host

* Solidify Hub and PLM configuration to ensure proper settings

* Update hub schema

* Bump insteonplm version

* Fix pylint and flake issues

* Bump insteonplm to 0.12.1

* Merge insteon_plm and insteon_local to insteon

* Rename insteon_plm to insteon

* Bump insteonplm to 0.12.2

* Flake8 cleanup

* Update .coveragerc for insteon_plm, insteon_local and insteon changes

* Add persistent notification

* Fix reference to insteon_plm

* Fix indentation

* Shorten message and fix grammer

* Add comment to remove in release 0.90

* Hound fix
2018-08-22 09:09:04 +02:00
Fabian Affolter
6864a44b5a Upgrade sendgrid to 5.6.0 (#16111) 2018-08-22 07:16:21 +02:00
Fabian Affolter
523af4fbca Upgrade shodan to 1.9.1 (#16113) 2018-08-22 07:15:56 +02:00
Dan Klaffenbach
b9733d0d99 homematic: Add homematic.put_paramset service (#16024)
Service to call putParamset method of XML-RPC API
2018-08-22 00:20:26 +02:00
Sebastian Muszynski
7ed8ed83e3 Bump python-miio version (#16110) 2018-08-21 21:25:48 +02:00
Fabian Affolter
0e1fb74e1b Minor updates (#16106) 2018-08-21 21:25:16 +02:00
Jason Hu
1ce51bfbd6 Refactoring login flow (#16104)
* Abstract LoginFlow

* Lint and typings
2018-08-21 11:03:38 -07:00
Paulus Schoutsen
cdb8361050 Add support for revoking refresh tokens (#16095)
* Add support for revoking refresh tokens

* Lint

* Split revoke logic in own method

* Simplify

* Update docs
2018-08-21 11:02:55 -07:00
Jason Hu
00c6f56cc8 Allow finish_flow callback to change data entry result type (#16100)
* Allow finish_flow callback to change data entry result type

* Add unit test
2018-08-21 10:48:24 -07:00
Paulus Schoutsen
b26506ad4a Use new session when fetching remote urls (#16093) 2018-08-21 19:03:46 +02:00
Paulus Schoutsen
7bb5344942 Remove homeassistant.remote (#16099)
* Remove homeassistant.remote

* Use direct import for API

* Fix docstring
2018-08-21 15:49:58 +02:00
Krasimir Zhelev
ae5c4c7e13 Upgrade afsapi to 0.0.4, prevents aiohttp session close message, Fixes #13099 (#16098) 2018-08-21 15:30:40 +02:00
Flip Hess
507e8f8f12 Add verify ssl to generic camera (#15949)
* Add verify_ssl option to generic camera

* Remove flake8 errors

* Add test for ssl verification on and off

* Fix lint errors
2018-08-21 15:29:11 +02:00
Paulus Schoutsen
5a15b2c036 Merge pull request #16094 from home-assistant/rc
0.76.2
2018-08-21 12:20:06 +02:00
Paulus Schoutsen
977d86e7ca Bumped version to 0.76.2 2018-08-21 11:43:14 +02:00
Paulus Schoutsen
bd776c84bc Forgiving add index in migration (#16092) 2018-08-21 11:43:08 +02:00
Paulus Schoutsen
68cd65567d Forgiving add index in migration (#16092) 2018-08-21 11:41:52 +02:00
Ville Skyttä
ef07460792 Upgrade pytest to 3.7.2 (#16091) 2018-08-21 10:56:28 +02:00
Jason Hu
f84a31871e Get user after login flow finished (#16047)
* Get user after login flow finished

* Add optional parameter 'type' to /auth/login_flow

* Update __init__.py
2018-08-21 10:18:04 +02:00
Daniel Perna
b1ba11510b Update pyhomematic to 0.1.47 (#16083) 2018-08-20 23:43:04 +02:00
Daniel Høyer Iversen
439f7978c3 fritzdect change to current_power_w (#16079) 2018-08-20 22:42:48 +02:00
Andrey Kupreychik
85a724e289 Bumped NDMS2 client library to 0.0.4 to get compatible with python 3.5 (#16077) 2018-08-20 18:51:25 +02:00
Greg Laabs
df6239e0fc Add ecovacs component (#15520)
* Ecovacs Deebot vacuums

* All core features implemented

Getting fan speed and locating the vac are still unsupported until sucks adds support

* Move init queries to the added_to_hass method

* Adding support for subscribing to events from the sucks library

This support does not exist in sucks yet; this commit serves as a sort of TDD approach of what such support COULD look like.

* Add OverloadUT as ecovacs code owner

* Full support for Ecovacs vacuums (Deebot)

* Add requirements

* Linting fixes

* Make API Device ID random on each boot

* Fix unique ID

Never worked before, as it should have been looking for a key, not an attribute

* Fix random string generation to work in Python 3.5 (thanks, Travis!)

* Add new files to .coveragerc

* Code review changes

(Will require a sucks version bump in a coming commit; waiting for it to release)

* Bump sucks to 0.9.1 now that it has released

* Update requirements_all.txt as well

* Bump sucks version to fix lifespan value errors

* Revert to sucks 0.9.1 and include a fix for a bug in that release

Sucks is being slow to release currently, so doing this so we can get a version out the door.

* Switch state_attributes to device_state_attributes
2018-08-20 17:42:53 +02:00
Paulus Schoutsen
1be61df9c0 Add recent context (#15989)
* Add recent context

* Add async_set_context to components not using new services
2018-08-20 17:39:53 +02:00
Paulus Schoutsen
d1e1b9b38a Deprecated stuff (#16019)
* Use async with for locks

* Fix regex in template test

* Close session correctly

* Use correct current_task method

* push camera cleanup

* Lint

* Revert current_task

* Update websocket_api.py

* Mock executor_job betteR

* Fix async_create_task mock
2018-08-20 16:34:18 +02:00
Tim Bailey
975befd136 TpLink Device Tracker Error (#15918)
* Fix 'Error setting up platform tplink' error when a scanner fails even if another scanner would succeed

* Try to fix tox errors

* Adjust code based on PR comments
2018-08-20 14:34:52 +02:00
Oleksii Serdiuk
a708a81fa8 openuv: Add Current UV Level to list of conditions (#16042)
Calculated from UV index, based on table from
https://www.openuv.io/kb/uv-index-levels-colors
2018-08-20 14:22:41 +02:00
Paulus Schoutsen
18d19fde0b Alexa: context + log events (#16023) 2018-08-20 14:18:07 +02:00
Kevin Siml
1f0d113688 Update pushsafer.py (#16060) 2018-08-20 14:11:52 +02:00
Paulus Schoutsen
121abb450a Use aiohttp web.AppRunner (#16020)
* Use aiohttp web.AppRunner

* Stop site
2018-08-20 14:03:35 +02:00
Paulus Schoutsen
1be388c587 Update frontend to 20180820.0 2018-08-20 11:52:47 +02:00
Paulus Schoutsen
994e2b6624 Update frontend to 20180820.0 2018-08-20 11:52:36 +02:00
Ville Skyttä
dbd0763f83 Grammar and spelling fixes (#16065) 2018-08-19 22:29:08 +02:00
Paulus Schoutsen
e1b2e00cf6 Merge pull request #16064 from home-assistant/rc
0.76.1
2018-08-19 20:07:44 +02:00
Paulus Schoutsen
3c5e62d47e Column syntax fix + Add a file if migration in progress (#16061)
* Add a file if migration in progress

* Warning

* Convert message for migration to warning
2018-08-19 19:01:11 +02:00
Paulus Schoutsen
3be301fac9 Bumped version to 0.76.1 2018-08-19 18:58:21 +02:00
Paulus Schoutsen
9c3251b5f0 Add notify platforms to loaded components (#16063) 2018-08-19 18:58:13 +02:00
huangyupeng
cb44607e96 Tuya fix login problem and add login platform param (#16058)
* add a platform param to distinguish different app's account.

* fix requirements
2018-08-19 18:58:12 +02:00
Paulus Schoutsen
1c8ef4e196 Add forgiving add column (#16057)
* Add forgiving add column

* Lint
2018-08-19 18:58:12 +02:00
Paulus Schoutsen
9e1fa7ef42 Column syntax fix + Add a file if migration in progress (#16061)
* Add a file if migration in progress

* Warning

* Convert message for migration to warning
2018-08-19 18:57:06 +02:00
Paulus Schoutsen
7c95e96ce8 Add notify platforms to loaded components (#16063) 2018-08-19 18:56:31 +02:00
huangyupeng
21b88f2fe8 Tuya fix login problem and add login platform param (#16058)
* add a platform param to distinguish different app's account.

* fix requirements
2018-08-19 18:55:10 +02:00
Paulus Schoutsen
81d3161a5e Add forgiving add column (#16057)
* Add forgiving add column

* Lint
2018-08-19 17:22:09 +02:00
Dan Klaffenbach
8beb349e88 vacuum/xiaomi_miio: Expose "sensor_dirty_left" attribute (#16003) 2018-08-19 13:57:28 +02:00
Daniel Shokouhi
c105045dab Update neato to support new StateVacuumDevice (#16035)
* Update neato to support new vacuum states

* Remove changes submitted in error

* Lint

* Review comments and fix resume cleaning

* Lint
2018-08-18 20:20:32 +02:00
Paulus Schoutsen
b901a26c47 Attempt to fix flaky TTS test (#16025) 2018-08-18 13:35:51 +02:00
Paulus Schoutsen
8ec550d6e0 Storage entity registry (#16018)
* Split out storage delayed write

* Update code using delayed save

* Fix tests

* Fix typing test

* Add callback decorator

* Migrate entity registry to storage helper

* Make double loading protection easier

* Lint

* Fix tests

* Ordered Dict
2018-08-18 13:34:33 +02:00
Paulus Schoutsen
6827256586 Bump frontend to 20180818.0 2018-08-18 11:15:46 +02:00
Paulus Schoutsen
ef193b0f64 Bump frontend to 20180818.0 2018-08-18 11:15:33 +02:00
Ed Marshall
e782e2c0f3 Handle missing mpd capabilities (#15945)
* Handle missing mpd capabilities

It is possible to configure mpd without volume or playlist support.
Gracefully degrade when either of these features appears to be missing.

Resolves: #14459, #15927

* Use longer name for exception

* Only return support flags post-connection

* Small consistency fixes to mpd.py for review.
2018-08-18 09:54:23 +02:00
Wim Haanstra
ec2e94425e Update RitAssist to support maximum speed and current address (#16037)
Update RitAssist dependency to 0.9.2 so we support fetching the current maximum speed and address for a device.
2018-08-18 09:40:29 +02:00
Diogo Gomes
9f0adc16ad Merge pull request #16031 from StevenLooman/dev
Upgrade to async_upnp_client==0.12.4
2018-08-17 22:36:03 +01:00
Steven Looman
fa88d918b1 Upgrade to async_upnp_client==0.12.4 2018-08-17 21:29:31 +02:00
Steven Looman
839b58c600 Working on IGD 2018-08-17 21:28:29 +02:00
Steven Looman
71d00062e5 Upgrade to async_upnp_client 0.12.4 2018-08-17 21:25:08 +02:00
Ville Skyttä
3800f00564 Disable assuming Optional type for values with None default (#16029)
https://www.python.org/dev/peps/pep-0484/#union-types
"Type checkers should move towards requiring the optional type to be
made explicit."
2018-08-17 20:22:49 +02:00
Paulus Schoutsen
2ad0bd4036 Split out storage delay save (#16017)
* Split out storage delayed write

* Update code using delayed save

* Fix tests

* Fix typing test

* Add callback decorator
2018-08-17 20:18:21 +02:00
Paulus Schoutsen
70412fc0ba Merge pull request #16027 from home-assistant/rc
0.76.0
2018-08-17 18:41:40 +02:00
Paulus Schoutsen
e4425e6a37 Version 0.76.0 2018-08-17 17:23:20 +02:00
Fabian Affolter
fdbab3e20c Upgrade sendgrid to 5.5.0 (#16021) 2018-08-17 16:39:41 +02:00
Anders Melchiorsen
f2e399ccf3 Update SoCo to 0.16 (#16007) 2018-08-17 07:41:56 +02:00
Martin Hjelmare
07840f5397 Fix check config packages key error (#15840)
* Fix packages deletion in check_config script

* The config key for packages is not present if core config validation
  failed. We need to do a safe dict deletion using dict.pop.

* Add check_config test for bad core config
2018-08-17 05:28:00 +02:00
Paulus Schoutsen
c09e7e620f Bumped version to 0.76.0b5 2018-08-16 23:02:34 +02:00
Paulus Schoutsen
92e26495da Disable the DLNA component discovery (#16006) 2018-08-16 23:02:26 +02:00
Steven Looman
061859cc4d Fix message "Updating dlna_dmr media_player took longer than ..." (#16005) 2018-08-16 23:02:26 +02:00
Steven Looman
45452e510c Fix message "Updating dlna_dmr media_player took longer than ..." (#16005) 2018-08-16 22:42:11 +02:00
Paulus Schoutsen
279ead2085 Disable the DLNA component discovery (#16006) 2018-08-16 22:41:44 +02:00
Ville Skyttä
649f17fe47 Add type hints to homeassistant.auth (#15853)
* Always load users in auth store before use

* Use namedtuple instead of dict for user meta

* Ignore auth store tokens with invalid created_at

* Add type hints to homeassistant.auth
2018-08-16 22:25:41 +02:00
Alexxander0
e9e5bce10c BMW Connected drive: option to disable the services (#15993)
* Update __init__.py

* Update bmw_connected_drive.py

* Update __init__.py

* Update bmw_connected_drive.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update bmw_connected_drive.py
2018-08-16 22:19:29 +02:00
Paulus Schoutsen
e41ce1d6ec Bump frontend to 20180816.1 2018-08-16 22:18:10 +02:00
Paulus Schoutsen
bc21a1b944 Bump frontend to 20180816.1 2018-08-16 22:17:55 +02:00
Paulus Schoutsen
834077190f Clean up input-datetime (#16000) 2018-08-16 22:17:14 +02:00
Steven Looman
80f98b9ea2 Fix message "Updating dlna_dmr media_player took longer than ..." 2018-08-16 22:14:13 +02:00
Max Prokhorov
2a210607d3 Wemo custom ports and network errors handling (#14516)
* Update wemo component

* Support custom ports in static addresses
* Handle device_from_description exceptions
* Process static addresses before doing discovery
* Fail on inaccessable static address
* str.format instead of old formatting

* Validate static host[:port] earlier

* Fix comment formatting

* slice looks ambiguous in the log, keep voluptuous exception path intact
2018-08-16 08:14:54 -06:00
Paulus Schoutsen
1ff1639cef More entity service (#15998)
* Camera use entity service

* Convert climate services

* Convert light

* Convert media player

* Migrate fan
2018-08-16 14:28:59 +02:00
Paulus Schoutsen
5eccfc2604 Bumped version to 0.76.0b4 2018-08-16 14:26:52 +02:00
Paulus Schoutsen
2469bc7e2e Fix Nest async from sync (#15997) 2018-08-16 14:26:44 +02:00
Martin Hjelmare
d540a084dd Fix mysensors connection task blocking setup (#15938)
* Fix mysensors connection task blocking setup

* Schedule the connection task without having the core track the task
  to avoid blocking setup.
* Cancel the connection task, if not cancelled already, when
  home assistant stops.

* Use done instead of cancelled
2018-08-16 14:26:44 +02:00
Paulus Schoutsen
11eb29f520 Bump frontend to 20180816.0 2018-08-16 14:22:01 +02:00
Paulus Schoutsen
e4d41fe313 Bump frontend to 20180816.0 2018-08-16 14:21:49 +02:00
Martin Hjelmare
b5e7414be2 Fix mysensors connection task blocking setup (#15938)
* Fix mysensors connection task blocking setup

* Schedule the connection task without having the core track the task
  to avoid blocking setup.
* Cancel the connection task, if not cancelled already, when
  home assistant stops.

* Use done instead of cancelled
2018-08-16 14:19:42 +02:00
Paulus Schoutsen
83b0ef4e26 Fix Nest async from sync (#15997) 2018-08-16 13:46:43 +02:00
Paulus Schoutsen
b682e48e12 Entity service (#15991)
* Add entity service helper

* Use entity service helper

* Context
2018-08-16 09:50:11 +02:00
Josh Shoemaker
e52ba87af1 Upgrade aladdin_connect to 0.3 and provide Unique ID (#15986)
* Upgrade aladdin_connect to 0.2 and set unique_id

* update code to be Python 3.5 compatible
2018-08-16 07:18:29 +02:00
Paulus Schoutsen
6da0ae4d23 Bumped version to 0.76.0b3 2018-08-15 10:55:03 +02:00
Jason Hu
f8051a5698 Fix 0.76 beta2 hassio token issue (#15987) 2018-08-15 10:53:39 +02:00
Jason Hu
2306d14b5d Teak mqtt error message for 0.76 release (#15983) 2018-08-15 10:53:38 +02:00
Paulus Schoutsen
4035880003 Update translations 2018-08-15 10:52:27 +02:00
Paulus Schoutsen
9cfbd067d3 Update translations 2018-08-15 10:52:06 +02:00
Fabian Affolter
39fd70231f Upgrade psutil to 5.4.7 (#15982) 2018-08-15 10:47:58 +02:00
Jason Hu
dc460f4d6a Fix 0.76 beta2 hassio token issue (#15987) 2018-08-15 09:56:05 +02:00
Jason Hu
c31035d348 Teak mqtt error message for 0.76 release (#15983) 2018-08-15 08:09:19 +02:00
Fabian Affolter
555184a4b7 Update Glances sensor (#15981)
* Refactor Glances sensor

* Add glances_api to requirements_all.txt

* Add support for version as configuration option
2018-08-15 07:49:34 +02:00
Paulus Schoutsen
e64e84ad7a Bumped version to 0.76.0b2 2018-08-14 22:06:57 +02:00
Paulus Schoutsen
1777270aa2 Pin crypto (#15978)
* Pin crypto

* Fix PyJWT import once
2018-08-14 22:06:44 +02:00
Paulus Schoutsen
f5df567d09 Use JWT for access tokens (#15972)
* Use JWT for access tokens

* Update requirements

* Improvements
2018-08-14 22:06:44 +02:00
Paulus Schoutsen
899c2057b7 Switch to intermediate Mozilla cert profile (#15957)
* Allow choosing intermediate SSL profile

* Fix tests
2018-08-14 22:06:43 +02:00
Jason Hu
1b384c322a Remove remote.API from core.Config (#15951)
* Use core.ApiConfig replace remote.API in core.Config

* Move ApiConfig to http
2018-08-14 22:06:43 +02:00
Daniel Bowman
f4e84fbf84 remove-phantomjs-from-docker (#15936) 2018-08-14 22:06:42 +02:00
Khalid
d393380122 Fix issue when reading worxlandroid pin code (#15930)
Fixes #14050
2018-08-14 22:06:41 +02:00
Jason Hu
d0e4c95bbc MQTT embedded broker has to set its own password. (#15929) 2018-08-14 22:06:41 +02:00
Jason Hu
34e1f1b6da Add context to login flow (#15914)
* Add context to login flow

* source -> context

* Fix unit test

* Update comment
2018-08-14 22:06:40 +02:00
kbickar
6d432d19fe Added error handling for sense API timeouts (#15789)
* Added error handling for sense API timeouts

* Moved imports in function

* Moved imports to more appropriate function

* Change exception to custom package version
2018-08-14 22:06:40 +02:00
Paulus Schoutsen
486efa9aba Pin crypto (#15978)
* Pin crypto

* Fix PyJWT import once
2018-08-14 22:02:01 +02:00
Daniel Bowman
0ad9fcd8a0 Add -j$(nproc) make option to speed up build time (#15928)
Adding `-j$(nproc)` reduces build time on the external dependencies by
approximately 25%.
2018-08-14 21:28:29 +02:00
Nate Clark
c7f7912bca adds support for momentary and beep/blink switches (#15973) 2018-08-14 21:15:33 +02:00
Paulus Schoutsen
e776f88eec Use JWT for access tokens (#15972)
* Use JWT for access tokens

* Update requirements

* Improvements
2018-08-14 21:14:12 +02:00
kbickar
ee5d49a033 Added error handling for sense API timeouts (#15789)
* Added error handling for sense API timeouts

* Moved imports in function

* Moved imports to more appropriate function

* Change exception to custom package version
2018-08-14 15:50:44 +02:00
Sean Dague
051903d30c Update waterfurnace library to 0.7, add reconnect logic (#15657)
One of the features of the waterfurnace 0.7 is timingout out stuck
connections on the websocket (which tends to happen after 48 - 96
hours of operation). This requires the homeassistant component to
catch and reconnect under these circumstances. This has turned out to
be pretty robust in preventing stuck sockets over the last month.
2018-08-14 07:49:04 -04:00
Paulus Schoutsen
91e1ae035e Remove warning (#15969) 2018-08-14 12:29:55 +02:00
Fabian Affolter
10a2ecd1d6 Make setup fail if location is not available (#15967)
* Make setup fail if location is not available

* Lint
2018-08-14 12:29:31 +02:00
Khalid
800eb4d86a Fix issue when reading worxlandroid pin code (#15930)
Fixes #14050
2018-08-14 11:55:40 +02:00
Daniel Bowman
e3a2e58623 remove-phantomjs-from-docker (#15936) 2018-08-14 11:53:08 +02:00
Colin Frei
ea073b5e87 Remove unnecessary log (#15966) 2018-08-14 11:46:41 +02:00
cgtobi
619d01150f Fix google calendar documentation link. (#15968) 2018-08-14 11:44:27 +02:00
Paulus Schoutsen
6540d2e073 Switch to intermediate Mozilla cert profile (#15957)
* Allow choosing intermediate SSL profile

* Fix tests
2018-08-14 08:20:17 +02:00
Daniel Perna
69b694ff26 HomeMatic: Enable entity registry (#15950)
* Set unique ID

* Excluding setups that resolve names

* Added support for resolvenames again
2018-08-14 07:43:16 +02:00
Paulus Schoutsen
9e21765173 Bumped version to 0.76.0b1 2018-08-13 23:17:30 +02:00
Paulus Schoutsen
c0830f1c20 Deprecate remote.api (#15955) 2018-08-13 23:17:21 +02:00
Martin Hjelmare
985f96662e Upgrade pymysensors to 0.17.0 (#15942) 2018-08-13 23:17:21 +02:00
Paulus Schoutsen
e0229b799d Update frontend to 20180813.0 2018-08-13 23:11:56 +02:00
Paulus Schoutsen
3fbb56d5fb Update frontend to 20180813.0 2018-08-13 23:11:22 +02:00
Conrad Juhl Andersen
3a60c8bbed Update Xiaomi Vacuum to new StateVacuumDevice (#15643)
* Add support for states

* Woof?

* Fixed some errors

* VacuumDevice -> StateVacuumDevice

* VacuumDevice -> StateVacuumDevice

* Added split of start and pause
2018-08-13 22:50:23 +02:00
Colin Frei
f411fb89e6 Netatmo public (#15684)
* Add a sensor for netatmo public data

* A bit of cleanup before submitting pull request

* Add netatmo_public file to .coveragerc, as per pull request template instructions

* Fixes for tox complaining

* make calculations simpler, based on review feedback

* explicitly pass required_data parameter to netatmo API

* remove unnecessary spaces

* remove debug code

* code style fix
2018-08-13 22:44:20 +02:00
Paulus Schoutsen
1b5cfa7331 Deprecate remote.api (#15955) 2018-08-13 22:39:13 +02:00
Charles Garwood
39647a15ae Add monitored conditions for Unifi device_tracker (#15888)
* Add support for monitored_conditions for attributes

* Update unifi tests

* Add list of available attrs
2018-08-13 21:18:25 +02:00
Matthew Garrett
ba2e43600e Merge pull request #15959 from mjg59/eufy
Bump python-lakeside dependency
2018-08-13 10:53:44 -07:00
Matthew Garrett
c998a55fe7 Bump python-lakeside dependency
This should fix https://github.com/home-assistant/home-assistant/issues/15374
2018-08-13 10:39:48 -07:00
Jason Hu
da8f93dca2 Add trusted networks auth provider (#15812)
* Add context to login flow

* Add trusted networks auth provider

* source -> context
2018-08-13 12:40:06 +02:00
Jason Hu
50daef9a52 Add context to login flow (#15914)
* Add context to login flow

* source -> context

* Fix unit test

* Update comment
2018-08-13 11:27:18 +02:00
Jason Hu
45f12dd3c7 MQTT embedded broker has to set its own password. (#15929) 2018-08-13 11:26:06 +02:00
Hovo (Luke)
6aee535d7c Allow wait template to run the remainder of the script (#15836)
* Adding new feature to allow a wait template to run the remainer of the script on timeout

* Styling changes

* Fixing file permissions, adding test for new code

* changed variable name, refactored script to pass information into async_set_timeout

* Changing the default behaviour to continue to run the script after timeout
2018-08-13 11:23:27 +02:00
Fabian Affolter
2342709803 Upgrade beautifulsoup4 to 4.6.3 (#15946) 2018-08-13 10:52:47 +02:00
Jason Hu
272be7cdae Remove remote.API from core.Config (#15951)
* Use core.ApiConfig replace remote.API in core.Config

* Move ApiConfig to http
2018-08-13 09:26:20 +02:00
Sebastian Muszynski
31fbfed0a6 Fix magic cube support of the Aqara LAN Protocol V2 (#15940) 2018-08-13 08:17:15 +02:00
Lev Aronsky
b7486e5605 Fixed race condition in Generic Thermostat (#15784)
* Fixed race condition in Generic Thermostat

* Added a comment to clarify the meaning of the `time` argument.
2018-08-12 22:28:47 +02:00
Martin Hjelmare
e8218c4b29 Upgrade pymysensors to 0.17.0 (#15942) 2018-08-12 20:22:54 +02:00
Thom Troy
d3fed52254 Eph ember support operation modes (#15820)
* add operation mode support for climate.EphEmber

* fix linting errors from py3.5

* remove STATE_ALL_DAY and cleanup some code based on review

* use explicit None return with get

* fix none return
2018-08-12 17:48:15 +02:00
Paulus Schoutsen
1205eaaa22 Version bump to 0.77.0dev0 2018-08-11 08:59:46 +02:00
Paulus Schoutsen
69934a9598 Bumped version to 0.76.0b0 2018-08-11 08:58:52 +02:00
Paulus Schoutsen
f24773933c Update frontend to 20180811.0 2018-08-11 08:58:20 +02:00
Franck Nijhof
e17e080639 ✏️ Corrects typo in code comments (#15923)
`MomematicIP` -> `HomematicIP`
2018-08-11 08:47:41 +02:00
cgtobi
055e35b297 Add RMV public transport sensor (#15814)
* Add new public transport sensor for RMV (Rhein-Main area).

* Add required module.

* Fix naming problem.

* Add unit test.

* Update dependency version to 0.0.5.

* Add new requirements.

* Fix variable name.

* Fix issues pointed out in review.

* Remove unnecessary code.

* Fix linter error.

* Fix config value validation.

* Replace minutes as state by departure timestamp. (see ##14983)

* More work on the timestamp. (see ##14983)

* Revert timestamp work until #14983 gets merged.

* Simplify product validation.

* Remove redundant code.

* Address code change requests.

* Address more code change requests.

* Address even more code change requests.

* Simplify destination check.

* Fix linter problem.

* Bump dependency version to 0.0.7.

* Name variable more explicit.

* Only query once a minute.

* Update test case.

* Fix config validation.

* Remove unneeded import.
2018-08-10 19:35:09 +02:00
Robert Svensson
81604a9326 deCONZ - Add support for sirens (#15896)
* Add support for sirenes

* Too quick...

* Fix test

* Use siren instead of sirene
2018-08-10 19:22:12 +02:00
Paulus Schoutsen
a0e9f9f218 Merge remote-tracking branch 'origin/master' into dev 2018-08-10 18:10:56 +02:00
Paulus Schoutsen
0ab3e7a92a Add IndieAuth 4.2.2 redirect uri at client id (#15911)
* Add IndieAuth 4.2.2 redirect uri at client id

* Fix tests

* Add comment

* Limit to first 10kB of each page
2018-08-10 18:09:42 +02:00
Paulus Schoutsen
9512bb9587 Add and restore context in recorder (#15859) 2018-08-10 18:09:01 +02:00
Adam Mills
da916d7b27 Fix bug in translations upload script (#15922) 2018-08-10 11:35:01 -04:00
clayton craft
b370b6a4e4 Update radiotherm to 1.4.1 (#15910) 2018-08-10 16:10:19 +02:00
Ville Skyttä
1911168855 Misc cleanups (#15907)
* device_tracker.huawei_router: Pylint logging-not-lazy fix

* sensor.irish_rail_transport: Clean up redundant self.info test
2018-08-10 16:09:08 +02:00
Joe Lu
f98629b895 Update August component to use py-august:0.6.0 (#15916) 2018-08-10 07:27:49 +02:00
Ville Skyttä
dc01b17260 Some typing related fixes (#15899)
* Fix FlowManager.async_init handler type

It's not a Callable, but typically a key pointing to one in a dict.

* Mark pip_kwargs return type hint as Any-valued dict

install_package takes other than str args too.
2018-08-09 22:53:12 +02:00
Benoit Louy
ef61c0c3a4 Add PJLink media player platform (#15083)
* add pjlink media player component

* retrieve pjlink device name from projector if name isn't specified in configuration

* update .coveragerc

* fix style

* add missing docstrings

* address PR comments from @MartinHjelmare

* fix code style

* use snake case string for source names

* add missing period at the end of comment string

* rewrite method as function

* revert to use source name provided by projector
2018-08-09 19:58:16 +02:00
mountainsandcode
664eae72d1 Add realtime true/false switch for Waze (#15228) 2018-08-09 16:27:29 +02:00
rafale77
86658f310d Fix for multiple camera switches naming of entity (#14028)
* Fix for multiple camera switches naming of entity

appended camera name to the switch entity name.

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Update amcrest.py

* Add digest authentification

* Update rest_command.py

* Update config.py

* Update rest_command.py

* Update config.py
2018-08-09 15:59:23 +02:00
Mattias Welponer
a29f867908 Add HomematicIP Cloud smoke detector device (#15621)
* Add smoke detector device

* Remove not needed __init__ functions
2018-08-09 14:43:13 +02:00
Jason Hu
2233d7ca98 Fix downgrade hassio cannot get refresh_token issue (#15874)
* Fix downgrade hassio issue

* Update __init__.py
2018-08-09 13:31:48 +02:00
Jason Hu
f58425dd3c Refactor data entry flow (#15883)
* Refactoring data_entry_flow and config_entry_flow

Move SOURCE_* to config_entries
Change data_entry_flow.FlowManager.async_init() source param default
 to None
Change this first step_id as source or init if source is None
_BaseFlowManagerView pass in SOURCE_USER as default source

* First step of data entry flow decided by _async_create_flow() now

* Lint

* Change helpers.config_entry_flow.DiscoveryFlowHandler default step

* Change FlowManager.async_init source param to context dict param
2018-08-09 13:24:14 +02:00
Fabian Affolter
39d19f2183 Upgrade locationsharinglib to 2.0.11 (#15902) 2018-08-09 13:05:28 +02:00
Paulus Schoutsen
99c4c65f69 Add auth/authorize endpoint (#15887) 2018-08-09 09:27:54 +02:00
Fabian Affolter
61901496ec Upgrade pylast to 2.4.0 (#15886) 2018-08-08 22:32:21 +02:00
Steven Looman
0ab65f1ac5 Follow changes to netdisco, separating DLNA into DLNA_DMS and DLNA_DMR (#15877)
* Follow changes to netdisco, separating DLNA into DLNA_DMS and DLNA_DMR

* No uppercase for names of netdisco discoverables
2018-08-08 11:54:22 +02:00
Fabian Affolter
debdc707e9 Upgrade netdisco to 2.0.0 (#15885) 2018-08-08 11:53:43 +02:00
DubhAd
fcc918a146 Update based upon forum post (#15876)
Based upon [this post](https://community.home-assistant.io/t/device-tracker-ping-on-windows-not-working-solved/61474/3) it looks like we've found why people couldn't get the ping tracker working on Windows.
2018-08-07 18:12:36 +02:00
Fabian Affolter
b6bc0097b8 Upgrade requests_mock to 1.5.2 (#15867) 2018-08-07 16:12:16 +02:00
Fabian Affolter
d556edae31 Upgrade Sphinx to 1.7.6 (#15868) 2018-08-07 16:12:01 +02:00
Fabian Affolter
1fb2ea70c2 Upgrade asynctest to 0.12.2 (#15869) 2018-08-07 16:11:47 +02:00
Ville Skyttä
4cbcb4c3a2 Upgrade pylint to 2.1.1 (#15872) 2018-08-07 16:09:19 +02:00
Paulus Schoutsen
d071df0dec Do not make internet connection during tests (#15858)
* Do not make internet connection

* Small improvement
2018-08-07 09:27:40 +02:00
cdce8p
f09f153014 Fix HomeKit test (#15860)
* Don't raise NotImplementedError during test
2018-08-07 09:26:58 +02:00
Fabian Affolter
1d8678c431 Upgrade pysnmp to 4.4.5 (#15854) 2018-08-07 09:13:01 +02:00
Fabian Affolter
51c30980df Upgrade holidays to 0.9.6 (#15831) 2018-08-07 09:12:09 +02:00
Fabian Affolter
cb20c9b1ea Revert "Upgrade requests_mock to 1.5.2"
This reverts commit a7db2ebbe1.
2018-08-07 09:02:54 +02:00
Fabian Affolter
a7db2ebbe1 Upgrade requests_mock to 1.5.2 2018-08-07 09:01:32 +02:00
Robin
61721478f3 Add facebox auth (#15439)
* Adds auth

* Update facebox.py

* Update test_facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py

* Remove TIMEOUT

* Update test_facebox.py

* fix lint

* Update facebox.py

* Update test_facebox.py

* Update facebox.py

* Adds check_box_health

* Adds test auth

* Update test_facebox.py

* Update test_facebox.py

* Update test_facebox.py

* Update test_facebox.py

* Ups coverage

* Update test_facebox.py

* Update facebox.py

* Update test_facebox.py

* Update facebox.py

* Update test_facebox.py

* Update facebox.py

* Update facebox.py

* Update facebox.py
2018-08-07 07:30:36 +02:00
Paulus Schoutsen
47fa928425 Bumped version to 0.76.0.dev0 2018-08-06 13:01:32 +02:00
Paulus Schoutsen
10a7accd00 Merge branch 'master' into dev 2018-08-06 13:01:04 +02:00
psike
6031801206 Fix error when Series missing 'episodeFileCount' or 'episodeCount' (#15824)
* Fix error when Series missing 'episodeFileCount' or 'episodeCount'

* Update sonarr.py

* Update sonarr.py
2018-08-06 12:18:36 +02:00
John Arild Berentsen
9cfe0db3c8 Add different pop 012501 ID (#15838) 2018-08-06 11:10:26 +02:00
Jason Hu
8ef2cfa364 Try to fix coveralls unstable result (#15800)
* Create one tox env for code coverage report

pytest-cov generated report in project root folder, not tox env folder.

* Add cov tox env to travis

* Coveralls seems expecting all build jobs upload

* Only upload coverage after cov env success
2018-08-06 10:51:37 +02:00
Jason Hu
12e69202f8 Change to call_service async_stop non-blocking to allow service call finish (#15803)
* Call later sync_stop to allow service call finish

* Change to use non-blocking service all for restart and stop
2018-08-06 10:25:37 +02:00
ahobsonsayers
e4b2ae29bd Fix bt_home_hub_5 device tracker (#15096)
* Fix bt_home_hub_5 device tracker

Updated BT Home Hub 5 device tracker component to get it working again. The old parsing method of the DNS table has been broken for a while causing the component to fail to get connected devices. A new parsing method has been implemened and fixes all previous issues.

* Moved part of code to a published PyPi library

* Fixed Violations

* Fixed bugs in device tracker

* Moved API Specific Code to PyPi Repository

* Updated to fit requested changes, removed test as it is no longer valid and updated requirement_all.txt

* Update to fit style requirements and remove redundant code

* Removed Unnecessary Comment
2018-08-06 07:38:02 +02:00
Ryan Davies
ac4674fdb0 Add max_gps_accuracy option to Google Maps (#15833)
* Google Maps - Add max_gps_accuracy option

* Remove else statement and add continue
2018-08-06 07:17:21 +02:00
Fabian Affolter
f86702e8ab Upgrade shodan to 1.9.0 (#15839) 2018-08-05 22:48:14 +02:00
mattwing
9a84f8b763 Remove 'volume' from return dict (#15842)
https://github.com/home-assistant/home-assistant/issues/15271

intraday results do not return the volume. See https://www.alphavantage.co/documentation/#intraday
2018-08-05 22:11:51 +02:00
Dan Cinnamon
6a32b9bf87 Fix envisalink reconnect (#15832)
* Fix logic for handling connection lost/reconnect

* Fixed line length issue.
2018-08-05 18:51:23 +02:00
Steven Looman
b152becbe0 Add media_player.dlna_dmr component (#14749)
* Add media_player.dlna_dmr component

* PEP 492

* Move DIDL-template up

* Remove max_volume-override option

* Remove picky_device support

* Use DEFAULT_NAME

* Make supported_features static

* Remove unneeded argument

* Proper module-docstring

* Add http dependency

* Remove additional_configuration options, no longer used

* Change default name to 'DLNA Digital Media Renderer'

* Use python-didl-lite for DIDL-Lite-xml construction/parsing

* Handle NOT_IMPLEMENTED for UPnP state variables RelativeTimePosition and CurrentMediaDuration

* Use UPnP-UDN for unique_id

* Proper handling of upnp events

* Keeping flake8 happy

* Update requirements_all.txt

* Make UDN optional

* Ensure NotifyView is started, before using it

* Only subscribe to services we're interested in

* Don't update state_variables if value has not been changed + minor refactoring

* Improve play_media, follow flow of DLNA more closely

* Hopefully fix ClientOSError problems

* Flake8 fixes

* Keep pylint happy

* Catch errors and report gracefully

* Update async_upnp_client to 0.11.0

* Don't be so noisy

* Define/use constants for HTTP status codes

* Add discovery entry for dlna_dmr

* More robustness with regard to state variable not being set (yet)

* Keep privates hidden

* Handle NOT_IMPLEMENTED for CurrentTrackMetaData state variable

* Fixes in async_upnp_client + renew UPnP subscriptions regularly

* Not too eager

* Refactor duplicate code to _current_transport_actions and improve parsing of actions

* Support RC:1 to RC:3 and AVT:1 to AVT:3

* Moved DLNA-specifics to async_upnp_client.dlna.DmrDevice

* Use our own HTTP server to listen for events.

* More clear and explicit log message for easier troubleshooting

* Follow changes by hass, fixes traceback

* Fix not being able to do next

* Changes after review by @MartinHjelmare

* Linting

* Use homeassistant.util.get_local_ip

* Moved upnp event handling to async_upnp_client

* Keeping pylint happy

* Changes after review by @MartinHjelmare
2018-08-05 14:41:18 +02:00
Fabian Affolter
c41aa12d1d Upgrade youtube_dl to 2018.08.04 (#15837) 2018-08-05 13:29:06 +02:00
Thomas Delaet
8a81ee3b4f Velbus auto-discovery (#13742)
* remove velbus fan and light platforms

these platforms should not be there since they can be created with template components based on switch platform

* use latest version of python-velbus which supports auto-discovery of modules

* fix linting errors

* fix linting errors

* fix linting errors

* address review comments from @MartinHjelmare

* update based on automatic feedback

* fix linting errors

* update dependency

* syntax corrections

* fix lint warning

* split out common functionality in VelbusEntity
use sync methods for loading platforms
support unique_ids so that entities are registred in entity registry

* fix linting errors

* fix linting errors

* fix linting errors

* integrate review comments (common functionality in VelbusEntity class)

* rename DOMAIN import to VELBUS_DOMAIN

* revert change created by requirements script

* regen
2018-08-05 10:47:17 +02:00
fucm
5e1836f3a2 Add support for 2 Tahoma IO awning covers (#15660)
* Add Tahoma io:VerticalExteriorAwningIOComponent and io:HorizontalAwningIOComponent

* Fix position of horizontal awning cover

* Add timestamps for lock time

* Adjust open-close actions for horizontal awning cover

* Fix stop action for io:RollerShutterGenericIOComponent

* Remove redundant information

* Use get for dict lookup
2018-08-05 10:44:57 +02:00
Fabian Affolter
9ea3be4dc1 Upgrade voluptuous to 0.11.5 (#15830) 2018-08-04 17:46:14 -07:00
Martin Hjelmare
bce47eb9a4 Fix frontend requirements after bump (#15829) 2018-08-04 22:35:41 +02:00
Pascal Vizeli
018bd8544c Fix lint with wrong frontend version inside requirements_test_all 2018-08-04 22:26:13 +02:00
Pascal Vizeli
bfb9f2a00b Fix lint with wrong frontend version inside requirements_all 2018-08-04 22:24:17 +02:00
Daniel Høyer Iversen
c7a8f1143c Fix rfxtrx device id matching (#15819)
* Issue #15773

Fix PT2262 devices are incorrectly matched in rfxtrx component

* style
2018-08-04 15:23:57 +02:00
Ville Skyttä
dbe44c076e Upgrade pytest to 3.7.1 and pytest-timeout to 1.3.1 (#15809) 2018-08-04 15:22:37 +02:00
Ville Skyttä
3246b49a45 Upgrade pylint to 2.1.0 (#15811)
* Upgrade pylint to 2.1.0

* Remove no longer needed pylint disables
2018-08-04 15:22:22 +02:00
Paulus Schoutsen
0c7d46927e Bump frontend to 20180804.0 2018-08-04 15:21:11 +02:00
Jason Hu
f6935b5d27 Upgrade voluptuous-serialize to 2.0.0 (#15763)
* Upgrade voluptuous-serialize to 2.0.0

* Change to 2.0.0
2018-08-03 05:23:26 -07:00
Robert Svensson
91e8680fc5 Only report color temp when in the correct color mode (#15791) 2018-08-03 13:56:54 +02:00
Jason Hu
6f2000f5e2 Make sure use_x_forward_for and trusted_proxies must config together (#15804)
* Make sure use_x_forward_for and trusted_proxies must config together

* Fix unit test
2018-08-03 13:52:34 +02:00
Paulus Schoutsen
ee180c51cf Bump frontend to 20180803.0 2018-08-03 13:48:32 +02:00
Conrad Juhl Andersen
b63312ff2e Vacuum component: start_pause to individual start and pause commands. (#15751)
* Add start and pause to StateVacuumDevice, move start_pause to VacuumDevice

* Updated demo vacuum and tests

* Add a few more tests
2018-08-02 19:49:38 -07:00
Paulus Schoutsen
59f8a73676 Return True from Nest setup (#15797) 2018-08-02 16:36:37 -06:00
Jesse Rizzo
affd4e7df3 Add Enphase Envoy component (#15081)
* add enphase envoy component

* Add Enphase Envoy component for energy monitoring

* Fix formatting problems

* Fix formatting errors

* Fix formatting errors

* Fix formatting errors

* Change unit of measurement to W or Wh. Return sensor states as integers

* Fix formatting errors

* Fix formatting errors

* Fix formatting errors

* Move import json to update function

* Fix formatting. Add file to .coveragerc

* Add new component to requirements_all.txt

* Move API call to third party library on PyPi

* Refactor

* Run gen_requirements_all.py

* Minor refactor

* Fix indentation

* Fix indentation
2018-08-02 23:14:43 +02:00
Bryan York
38928c4c0e 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-02 22:09:19 +02:00
Diogo Gomes
48af5116b3 Update pymediaroom to 0.6.4 (#15786)
* Dependency version bump

* bump version
2018-08-02 20:13:48 +02:00
Paulus Schoutsen
eb5f6efb43 Update frontend to 20180802.0 2018-08-02 14:23:40 +02:00
Paulus Schoutsen
7972d6a0c6 Update translations 2018-08-02 13:42:45 +02:00
Aaron Bach
bdea9e1333 Add support for OpenUV binary sensors and sensors (#15769)
* Initial commit

* Adjusted ownership and coverage

* Member-requested changes

* Updated Ozone to a value, not an index

* Verbiage update
2018-08-02 07:42:12 +02:00
Wim Haanstra
2f8d66ef2b RitAssist / FleetGO support (#15780)
* RitAssist / FleetGO support

* Fix lint issue
Add to .coveragerc
2018-08-02 07:01:40 +02:00
Jason Hu
589b23b7e2 Revert "Add support for STATE_AUTO of generic_thermostat (#15678)" (#15783)
This reverts commit 2e5131bb21.
2018-08-01 10:04:41 -07:00
Niklas
2e5131bb21 Add support for STATE_AUTO of generic_thermostat (#15678)
Add support for STATE_AUTO of generic_thermostat
2018-08-01 08:07:27 -07:00
Conrad Juhl Andersen
2ff5b4ce95 Add support for STATES of vacuums (#15573)
* Vacuum: Added support for STATES

* Added debug logging and corrected state order

* typo

* Fix travis error, STATE = STATE for readability

* status -> state

* Changed to Entity instead of ToogleEntity

* Updated some vacuums

* Revert changes

* Revert Changes

* added SUPPORT_STATE

* Woof?

* Implement on/off if STATE not supported

* Moved new state vaccum to Class StateVacuumDevice

* Error: I should go to bed

* Moved around methods for easier reading

* Added StateVacuumDevice demo vacuum

* Added tests for StateVacuumDevice demo vacuum

* Fix styling errors

* Refactored to BaseVaccum

* Vacuum will now go back to dock

* Class BaseVacuum is for internal use only

* return -> await

* return -> await
2018-08-01 05:51:38 -07:00
Robert Svensson
f8a478946e deCONZ - support for power plugs (#15752)
* Initial commit for deCONZ switch support

* Fix hound comment

* Fix martins comment; platforms shouldn't depend on another platform

* Fix existing tests

* New tests

* Clean up unnecessary methods

* Bump requirement to v43

* Added device state attributes to light
2018-08-01 11:03:08 +02:00
Oscar Tin Yiu Lai
623f6c841b Expose internal states and fixed on/off state of Dyson Fans (#15716)
* exposing internal state and fixed onoff state

* fixed styling

* revert file mode changes

* removed self type hints

* better unit test and changed the way to return attributes

* made wolfie happy
2018-07-31 21:38:34 -07:00
Ioan Loosley
0b6f2f5b91 Opensky altitude (#15273)
* Added Altitude to opensky

* decided to take all metadata

* Final Tidy

* More formatting

* moving CONF_ALTITUDE to platform

* Moved CONF_ALTITUDE to platform
2018-07-31 21:45:18 +02:00
Mathieu Velten
3445dc1f00 Update pynetgear to 0.4.1 (bugfixes) (#15768) 2018-07-31 21:40:13 +02:00
Fabian Affolter
a11c2a0bd8 Fix docstrings (#15770) 2018-07-31 21:39:37 +02:00
Diogo Gomes
95da41aa15 This component API has been decomissioned on the 31st of May 2018 by Telstra (#15757)
See #15668
2018-07-31 21:27:43 +02:00
Fabian Affolter
27401f4975 Upgrade Mastodon.py to 1.3.1 (#15766) 2018-07-31 21:17:55 +02:00
Scott Albertson
d902a9f279 Add a "Reviewed by Hound" badge (#15767) 2018-07-31 21:17:33 +02:00
priiduonu
03847e6c41 Round precipitation forecast to 1 decimal place (#15759)
The OWM returns precipitation forecast values as they are submitted to their network. It can lead to values like `0.0025000000000004 mm` which does not make sense and messes up the display card. This PR rounds the value to 1 decimal place.
2018-07-31 19:18:11 +02:00
Fabian Affolter
a4f9602405 Convert wind speed to km/h (fixes #15710) (#15740)
* Convert wind speed to km/h (fixes #15710)

* Round speed
2018-07-31 19:11:29 +02:00
John Arild Berentsen
5f214ffa98 Update pyozw to 0.4.9 (#15758)
* update pyozw to 0.4.8

* add requirements_all.txt

* use 0.4.9
2018-07-31 15:14:14 +01:00
Andrey
8ee3b535ef Add disallow_untyped_calls to mypy check. (#15661)
* Add disallow_untyped_calls to mypy check.

* Fix generator
2018-07-31 15:00:17 +01:00
Andrey Kupreychik
951372491c Fixed NDMS for latest firmware (#15511)
* Fixed NDMS for latest firmware.
Now using telnet instead of Web Interface

* Using external library for NDMS interactions

* updated requirements_all

* renamed `mac` to `device` back

* Using generators for name and attributes fetching
2018-07-31 11:14:49 +02:00
Jason Hu
eeb79476de Decouple login flow view and data entry flow view (#15715) 2018-07-30 21:59:18 -07:00
Aaron Bach
1b2d0e7a6f Better handling of Yi camera being disconnected (#15754)
* Better handling of Yi camera being disconnected

* Handling video processing as well

* Cleanup

* Member-requested changes

* Member-requested changes
2018-07-30 21:56:52 -07:00
Teemu R
3208ad27ac Add kodi unique id based on discovery (#15093)
* kodi add unique id based on discovery

* initialize unique_id to None

* use netdisco-extracted mac_address

* use an uuid instead of mac for real uniqueness

* add missing docstring

* verify that there is no entity already for the given unique id

* whitespace fix
2018-07-30 17:09:38 +02:00
superpuffin
cf87b76b0c 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-07-30 15:15:13 +01:00
2184 changed files with 65473 additions and 22471 deletions

View File

@@ -28,6 +28,9 @@ omit =
homeassistant/components/apple_tv.py
homeassistant/components/*/apple_tv.py
homeassistant/components/aqualogic.py
homeassistant/components/*/aqualogic.py
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
@@ -42,6 +45,7 @@ omit =
homeassistant/components/asterisk_mbox.py
homeassistant/components/*/asterisk_mbox.py
homeassistant/components/*/asterisk_cdr.py
homeassistant/components/august.py
homeassistant/components/*/august.py
@@ -52,7 +56,7 @@ omit =
homeassistant/components/bbb_gpio.py
homeassistant/components/*/bbb_gpio.py
homeassistant/components/blink.py
homeassistant/components/blink/*
homeassistant/components/*/blink.py
homeassistant/components/bloomsky.py
@@ -92,17 +96,29 @@ omit =
homeassistant/components/ecobee.py
homeassistant/components/*/ecobee.py
homeassistant/components/edp_redy.py
homeassistant/components/*/edp_redy.py
homeassistant/components/egardia.py
homeassistant/components/*/egardia.py
homeassistant/components/elkm1/*
homeassistant/components/*/elkm1.py
homeassistant/components/enocean.py
homeassistant/components/*/enocean.py
homeassistant/components/envisalink.py
homeassistant/components/*/envisalink.py
homeassistant/components/evohome.py
homeassistant/components/*/evohome.py
homeassistant/components/fritzbox.py
homeassistant/components/switch/fritzbox.py
homeassistant/components/*/fritzbox.py
homeassistant/components/ecovacs.py
homeassistant/components/*/ecovacs.py
homeassistant/components/eufy.py
homeassistant/components/*/eufy.py
@@ -113,6 +129,16 @@ omit =
homeassistant/components/google.py
homeassistant/components/*/google.py
homeassistant/components/habitica/*
homeassistant/components/*/habitica.py
homeassistant/components/hangouts/__init__.py
homeassistant/components/hangouts/const.py
homeassistant/components/hangouts/hangouts_bot.py
homeassistant/components/hangouts/hangups_utils.py
homeassistant/components/hangouts/intents.py
homeassistant/components/*/hangouts.py
homeassistant/components/hdmi_cec.py
homeassistant/components/*/hdmi_cec.py
@@ -128,17 +154,21 @@ omit =
homeassistant/components/homematicip_cloud.py
homeassistant/components/*/homematicip_cloud.py
homeassistant/components/huawei_lte.py
homeassistant/components/*/huawei_lte.py
homeassistant/components/hydrawise.py
homeassistant/components/*/hydrawise.py
homeassistant/components/ihc/*
homeassistant/components/*/ihc.py
homeassistant/components/insteon_local.py
homeassistant/components/*/insteon_local.py
homeassistant/components/insteon/*
homeassistant/components/*/insteon.py
homeassistant/components/insteon_plm/*
homeassistant/components/*/insteon_plm.py
homeassistant/components/insteon_local.py
homeassistant/components/insteon_plm.py
homeassistant/components/ios.py
homeassistant/components/*/ios.py
@@ -170,6 +200,9 @@ omit =
homeassistant/components/linode.py
homeassistant/components/*/linode.py
homeassistant/components/logi_circle.py
homeassistant/components/*/logi_circle.py
homeassistant/components/lutron.py
homeassistant/components/*/lutron.py
@@ -215,6 +248,12 @@ omit =
homeassistant/components/opencv.py
homeassistant/components/*/opencv.py
homeassistant/components/opentherm_gw.py
homeassistant/components/*/opentherm_gw.py
homeassistant/components/openuv/__init__.py
homeassistant/components/*/openuv.py
homeassistant/components/pilight.py
homeassistant/components/*/pilight.py
@@ -251,6 +290,9 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py
homeassistant/components/simplisafe/__init__.py
homeassistant/components/*/simplisafe.py
homeassistant/components/sisyphus.py
homeassistant/components/*/sisyphus.py
@@ -283,6 +325,9 @@ omit =
homeassistant/components/*/thinkingcleaner.py
homeassistant/components/tibber/*
homeassistant/components/*/tibber.py
homeassistant/components/toon.py
homeassistant/components/*/toon.py
@@ -343,7 +388,7 @@ omit =
homeassistant/components/zigbee.py
homeassistant/components/*/zigbee.py
homeassistant/components/zoneminder.py
homeassistant/components/zoneminder/*
homeassistant/components/*/zoneminder.py
homeassistant/components/tuya.py
@@ -359,8 +404,8 @@ omit =
homeassistant/components/alarm_control_panel/ifttt.py
homeassistant/components/alarm_control_panel/manual_mqtt.py
homeassistant/components/alarm_control_panel/nx584.py
homeassistant/components/alarm_control_panel/simplisafe.py
homeassistant/components/alarm_control_panel/totalconnect.py
homeassistant/components/alarm_control_panel/yale_smart_alarm.py
homeassistant/components/apiai.py
homeassistant/components/binary_sensor/arest.py
homeassistant/components/binary_sensor/concord232.py
@@ -389,7 +434,6 @@ omit =
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
homeassistant/components/climate/eq3btsmart.py
homeassistant/components/climate/flexit.py
@@ -397,6 +441,7 @@ omit =
homeassistant/components/climate/homematic.py
homeassistant/components/climate/honeywell.py
homeassistant/components/climate/knx.py
homeassistant/components/climate/mill.py
homeassistant/components/climate/oem.py
homeassistant/components/climate/proliphix.py
homeassistant/components/climate/radiotherm.py
@@ -413,7 +458,6 @@ omit =
homeassistant/components/cover/myq.py
homeassistant/components/cover/opengarage.py
homeassistant/components/cover/rpi_gpio.py
homeassistant/components/cover/ryobi_gdo.py
homeassistant/components/cover/scsgate.py
homeassistant/components/device_tracker/actiontec.py
homeassistant/components/device_tracker/aruba.py
@@ -440,6 +484,8 @@ omit =
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/device_tracker/ping.py
homeassistant/components/device_tracker/quantum_gateway.py
homeassistant/components/device_tracker/ritassist.py
homeassistant/components/device_tracker/sky_hub.py
homeassistant/components/device_tracker/snmp.py
homeassistant/components/device_tracker/swisscom.py
@@ -480,6 +526,7 @@ omit =
homeassistant/components/light/lw12wifi.py
homeassistant/components/light/mystrom.py
homeassistant/components/light/nanoleaf_aurora.py
homeassistant/components/light/opple.py
homeassistant/components/light/osramlightify.py
homeassistant/components/light/piglow.py
homeassistant/components/light/rpi_gpio_pwm.py
@@ -509,6 +556,7 @@ omit =
homeassistant/components/media_player/denon.py
homeassistant/components/media_player/denonavr.py
homeassistant/components/media_player/directv.py
homeassistant/components/media_player/dlna_dmr.py
homeassistant/components/media_player/dunehd.py
homeassistant/components/media_player/emby.py
homeassistant/components/media_player/epson.py
@@ -520,6 +568,7 @@ omit =
homeassistant/components/media_player/itunes.py
homeassistant/components/media_player/kodi.py
homeassistant/components/media_player/lg_netcast.py
homeassistant/components/media_player/lg_soundbar.py
homeassistant/components/media_player/liveboxplaytv.py
homeassistant/components/media_player/mediaroom.py
homeassistant/components/media_player/mpchc.py
@@ -532,6 +581,7 @@ omit =
homeassistant/components/media_player/pandora.py
homeassistant/components/media_player/philips_js.py
homeassistant/components/media_player/pioneer.py
homeassistant/components/media_player/pjlink.py
homeassistant/components/media_player/plex.py
homeassistant/components/media_player/roku.py
homeassistant/components/media_player/russound_rio.py
@@ -562,6 +612,7 @@ omit =
homeassistant/components/notify/gntp.py
homeassistant/components/notify/group.py
homeassistant/components/notify/hipchat.py
homeassistant/components/notify/homematic.py
homeassistant/components/notify/instapush.py
homeassistant/components/notify/kodi.py
homeassistant/components/notify/lannouncer.py
@@ -588,13 +639,13 @@ omit =
homeassistant/components/notify/telstra.py
homeassistant/components/notify/twitter.py
homeassistant/components/notify/xmpp.py
homeassistant/components/notify/yessssms.py
homeassistant/components/nuimo_controller.py
homeassistant/components/prometheus.py
homeassistant/components/rainbird.py
homeassistant/components/remember_the_milk/__init__.py
homeassistant/components/remote/harmony.py
homeassistant/components/remote/itach.py
homeassistant/components/route53.py
homeassistant/components/scene/hunterdouglas_powerview.py
homeassistant/components/scene/lifx_cloud.py
homeassistant/components/sensor/airvisual.py
@@ -632,6 +683,7 @@ omit =
homeassistant/components/sensor/eddystone_temperature.py
homeassistant/components/sensor/eliqonline.py
homeassistant/components/sensor/emoncms.py
homeassistant/components/sensor/enphase_envoy.py
homeassistant/components/sensor/envirophat.py
homeassistant/components/sensor/etherscan.py
homeassistant/components/sensor/fastdotcom.py
@@ -646,6 +698,7 @@ omit =
homeassistant/components/sensor/fritzbox_netmonitor.py
homeassistant/components/sensor/gearbest.py
homeassistant/components/sensor/geizhals.py
homeassistant/components/sensor/gitlab_ci.py
homeassistant/components/sensor/gitter.py
homeassistant/components/sensor/glances.py
homeassistant/components/sensor/google_travel_time.py
@@ -654,6 +707,7 @@ omit =
homeassistant/components/sensor/haveibeenpwned.py
homeassistant/components/sensor/hp_ilo.py
homeassistant/components/sensor/htu21d.py
homeassistant/components/sensor/upnp.py
homeassistant/components/sensor/imap_email_content.py
homeassistant/components/sensor/imap.py
homeassistant/components/sensor/influxdb.py
@@ -662,6 +716,7 @@ omit =
homeassistant/components/sensor/kwb.py
homeassistant/components/sensor/lacrosse.py
homeassistant/components/sensor/lastfm.py
homeassistant/components/sensor/linky.py
homeassistant/components/sensor/linux_battery.py
homeassistant/components/sensor/loopenergy.py
homeassistant/components/sensor/luftdaten.py
@@ -675,8 +730,11 @@ omit =
homeassistant/components/sensor/mqtt_room.py
homeassistant/components/sensor/mvglive.py
homeassistant/components/sensor/nederlandse_spoorwegen.py
homeassistant/components/sensor/netatmo_public.py
homeassistant/components/sensor/netdata.py
homeassistant/components/sensor/netdata_public.py
homeassistant/components/sensor/neurio_energy.py
homeassistant/components/sensor/noaa_tides.py
homeassistant/components/sensor/nsw_fuel_station.py
homeassistant/components/sensor/nut.py
homeassistant/components/sensor/nzbget.py
@@ -699,6 +757,7 @@ omit =
homeassistant/components/sensor/radarr.py
homeassistant/components/sensor/rainbird.py
homeassistant/components/sensor/ripple.py
homeassistant/components/sensor/rtorrent.py
homeassistant/components/sensor/scrape.py
homeassistant/components/sensor/sense.py
homeassistant/components/sensor/sensehat.py
@@ -716,6 +775,7 @@ omit =
homeassistant/components/sensor/sonarr.py
homeassistant/components/sensor/speedtest.py
homeassistant/components/sensor/spotcrime.py
homeassistant/components/sensor/starlingbank.py
homeassistant/components/sensor/steam_online.py
homeassistant/components/sensor/supervisord.py
homeassistant/components/sensor/swiss_hydrological_data.py
@@ -727,7 +787,7 @@ omit =
homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/temper.py
homeassistant/components/sensor/tibber.py
homeassistant/components/sensor/thermoworks_smoke.py
homeassistant/components/sensor/time_date.py
homeassistant/components/sensor/torque.py
homeassistant/components/sensor/trafikverket_weatherstation.py
@@ -735,11 +795,11 @@ omit =
homeassistant/components/sensor/travisci.py
homeassistant/components/sensor/twitch.py
homeassistant/components/sensor/uber.py
homeassistant/components/sensor/upnp.py
homeassistant/components/sensor/ups.py
homeassistant/components/sensor/uscis.py
homeassistant/components/sensor/vasttrafik.py
homeassistant/components/sensor/viaggiatreno.py
homeassistant/components/sensor/volkszaehler.py
homeassistant/components/sensor/waqi.py
homeassistant/components/sensor/waze_travel_time.py
homeassistant/components/sensor/whois.py
@@ -768,8 +828,11 @@ omit =
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rainbird.py
homeassistant/components/switch/rest.py
homeassistant/components/switch/recswitch.py
homeassistant/components/switch/rpi_rf.py
homeassistant/components/switch/snmp.py
homeassistant/components/switch/switchbot.py
homeassistant/components/switch/switchmate.py
homeassistant/components/switch/telnet.py
homeassistant/components/switch/tplink.py
homeassistant/components/switch/transmission.py
@@ -782,10 +845,12 @@ omit =
homeassistant/components/tts/picotts.py
homeassistant/components/vacuum/mqtt.py
homeassistant/components/vacuum/roomba.py
homeassistant/components/water_heater/econet.py
homeassistant/components/watson_iot.py
homeassistant/components/weather/bom.py
homeassistant/components/weather/buienradar.py
homeassistant/components/weather/darksky.py
homeassistant/components/weather/met.py
homeassistant/components/weather/metoffice.py
homeassistant/components/weather/openweathermap.py
homeassistant/components/weather/zamg.py

View File

@@ -3,7 +3,7 @@
**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>
**Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io#<home-assistant.io PR number goes here>
## Example entry for `configuration.yaml` (if applicable):
```yaml
@@ -15,7 +15,7 @@
- [ ] 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)
- [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.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]).

View File

@@ -13,7 +13,8 @@ matrix:
- python: "3.5.3"
env: TOXENV=typing
- python: "3.5.3"
env: TOXENV=py35
env: TOXENV=cov
after_success: coveralls
- python: "3.6"
env: TOXENV=py36
- python: "3.7"
@@ -45,4 +46,3 @@ deploy:
on:
branch: dev
condition: $TOXENV = lint
after_success: coveralls

View File

@@ -2,56 +2,69 @@
# when the code that they own is touched.
# https://github.com/blog/2392-introducing-code-owners
# Home Assistant Core
setup.py @home-assistant/core
homeassistant/*.py @home-assistant/core
homeassistant/helpers/* @home-assistant/core
homeassistant/util/* @home-assistant/core
homeassistant/components/api.py @home-assistant/core
homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automation/* @home-assistant/core
homeassistant/components/cloud/* @home-assistant/core
homeassistant/components/config/* @home-assistant/core
homeassistant/components/configurator.py @home-assistant/core
homeassistant/components/group.py @home-assistant/core
homeassistant/components/conversation/* @home-assistant/core
homeassistant/components/frontend/* @home-assistant/core
homeassistant/components/group/* @home-assistant/core
homeassistant/components/history.py @home-assistant/core
homeassistant/components/http/* @home-assistant/core
homeassistant/components/input_*.py @home-assistant/core
homeassistant/components/introduction.py @home-assistant/core
homeassistant/components/logger.py @home-assistant/core
homeassistant/components/lovelace/* @home-assistant/core
homeassistant/components/mqtt/* @home-assistant/core
homeassistant/components/panel_custom.py @home-assistant/core
homeassistant/components/panel_iframe.py @home-assistant/core
homeassistant/components/persistent_notification.py @home-assistant/core
homeassistant/components/onboarding/* @home-assistant/core
homeassistant/components/persistent_notification/* @home-assistant/core
homeassistant/components/scene/__init__.py @home-assistant/core
homeassistant/components/scene/hass.py @home-assistant/core
homeassistant/components/script.py @home-assistant/core
homeassistant/components/shell_command.py @home-assistant/core
homeassistant/components/sun.py @home-assistant/core
homeassistant/components/updater.py @home-assistant/core
homeassistant/components/weblink.py @home-assistant/core
homeassistant/components/weblink/* @home-assistant/core
homeassistant/components/websocket_api.py @home-assistant/core
homeassistant/components/zone.py @home-assistant/core
homeassistant/components/zone/* @home-assistant/core
# HomeAssistant developer Teams
# Home Assistant Developer Teams
Dockerfile @home-assistant/docker
virtualization/Docker/* @home-assistant/docker
homeassistant/components/zwave/* @home-assistant/z-wave
homeassistant/components/*/zwave.py @home-assistant/z-wave
homeassistant/components/hassio.py @home-assistant/hassio
homeassistant/components/hassio/* @home-assistant/hassio
# Individual components
# Individual platforms
homeassistant/components/alarm_control_panel/egardia.py @jeroenterheerdt
homeassistant/components/alarm_control_panel/manual_mqtt.py @colinodell
homeassistant/components/binary_sensor/hikvision.py @mezz64
homeassistant/components/bmw_connected_drive.py @ChristianKuehnel
homeassistant/components/binary_sensor/threshold.py @fabaff
homeassistant/components/camera/yi.py @bachya
homeassistant/components/climate/ephember.py @ttroy50
homeassistant/components/climate/eq3btsmart.py @rytilahti
homeassistant/components/climate/mill.py @danielhiversen
homeassistant/components/climate/sensibo.py @andrey-git
homeassistant/components/cover/group.py @cdce8p
homeassistant/components/cover/template.py @PhracturedBlue
homeassistant/components/device_tracker/automatic.py @armills
homeassistant/components/device_tracker/huawei_router.py @abmantis
homeassistant/components/device_tracker/quantum_gateway.py @cisasteelersfan
homeassistant/components/device_tracker/tile.py @bachya
homeassistant/components/history_graph.py @andrey-git
homeassistant/components/influx.py @fabaff
homeassistant/components/light/lifx_legacy.py @amelchio
homeassistant/components/light/tplink.py @rytilahti
homeassistant/components/light/yeelight.py @rytilahti
homeassistant/components/lock/nello.py @pschmitt
@@ -61,58 +74,173 @@ homeassistant/components/media_player/kodi.py @armills
homeassistant/components/media_player/liveboxplaytv.py @pschmitt
homeassistant/components/media_player/mediaroom.py @dgomes
homeassistant/components/media_player/monoprice.py @etsinko
homeassistant/components/media_player/mpd.py @fabaff
homeassistant/components/media_player/sonos.py @amelchio
homeassistant/components/media_player/xiaomi_tv.py @fattdev
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
homeassistant/components/no_ip.py @fabaff
homeassistant/components/notify/file.py @fabaff
homeassistant/components/notify/flock.py @fabaff
homeassistant/components/notify/instapush.py @fabaff
homeassistant/components/notify/mastodon.py @fabaff
homeassistant/components/notify/smtp.py @fabaff
homeassistant/components/notify/syslog.py @fabaff
homeassistant/components/notify/xmpp.py @fabaff
homeassistant/components/plant.py @ChristianKuehnel
homeassistant/components/scene/lifx_cloud.py @amelchio
homeassistant/components/sensor/airvisual.py @bachya
homeassistant/components/sensor/alpha_vantage.py @fabaff
homeassistant/components/sensor/bitcoin.py @fabaff
homeassistant/components/sensor/cpuspeed.py @fabaff
homeassistant/components/sensor/cups.py @fabaff
homeassistant/components/sensor/darksky.py @fabaff
homeassistant/components/sensor/file.py @fabaff
homeassistant/components/sensor/filter.py @dgomes
homeassistant/components/sensor/fixer.py @fabaff
homeassistant/components/sensor/gearbest.py @HerrHofrat
homeassistant/components/sensor/gitter.py @fabaff
homeassistant/components/sensor/glances.py @fabaff
homeassistant/components/sensor/gpsd.py @fabaff
homeassistant/components/sensor/irish_rail_transport.py @ttroy50
homeassistant/components/sensor/jewish_calendar.py @tsvi
homeassistant/components/sensor/linux_battery.py @fabaff
homeassistant/components/sensor/luftdaten.py @fabaff
homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel
homeassistant/components/sensor/min_max.py @fabaff
homeassistant/components/sensor/moon.py @fabaff
homeassistant/components/sensor/netdata.py @fabaff
homeassistant/components/sensor/nsw_fuel_station.py @nickw444
homeassistant/components/sensor/pi_hole.py @fabaff
homeassistant/components/sensor/pollen.py @bachya
homeassistant/components/sensor/pvoutput.py @fabaff
homeassistant/components/sensor/qnap.py @colinodell
homeassistant/components/sensor/scrape.py @fabaff
homeassistant/components/sensor/serial.py @fabaff
homeassistant/components/sensor/shodan.py @fabaff
homeassistant/components/sensor/sma.py @kellerza
homeassistant/components/sensor/sql.py @dgomes
homeassistant/components/sensor/statistics.py @fabaff
homeassistant/components/sensor/swiss*.py @fabaff
homeassistant/components/sensor/sytadin.py @gautric
homeassistant/components/sensor/tibber.py @danielhiversen
homeassistant/components/sensor/upnp.py @dgomes
homeassistant/components/sensor/time_data.py @fabaff
homeassistant/components/sensor/version.py @fabaff
homeassistant/components/sensor/waqi.py @andrey-git
homeassistant/components/sensor/worldclock.py @fabaff
homeassistant/components/shiftr.py @fabaff
homeassistant/components/spaceapi.py @fabaff
homeassistant/components/switch/tplink.py @rytilahti
homeassistant/components/vacuum/roomba.py @pschmitt
homeassistant/components/weather/__init__.py @fabaff
homeassistant/components/weather/darksky.py @fabaff
homeassistant/components/weather/demo.py @fabaff
homeassistant/components/weather/met.py @danielhiversen
homeassistant/components/weather/openweathermap.py @fabaff
homeassistant/components/xiaomi_aqara.py @danielhiversen @syssi
# A
homeassistant/components/arduino.py @fabaff
homeassistant/components/*/arduino.py @fabaff
homeassistant/components/*/arest.py @fabaff
homeassistant/components/*/axis.py @kane610
# B
homeassistant/components/blink/* @fronzbot
homeassistant/components/*/blink.py @fronzbot
homeassistant/components/bmw_connected_drive.py @ChristianKuehnel
homeassistant/components/*/bmw_connected_drive.py @ChristianKuehnel
homeassistant/components/*/broadlink.py @danielhiversen
# C
homeassistant/components/counter/* @fabaff
# D
homeassistant/components/*/deconz.py @kane610
homeassistant/components/digital_ocean.py @fabaff
homeassistant/components/*/digital_ocean.py @fabaff
homeassistant/components/dweet.py @fabaff
homeassistant/components/*/dweet.py @fabaff
# E
homeassistant/components/ecovacs.py @OverloadUT
homeassistant/components/*/ecovacs.py @OverloadUT
homeassistant/components/*/edp_redy.py @abmantis
homeassistant/components/edp_redy.py @abmantis
homeassistant/components/eight_sleep.py @mezz64
homeassistant/components/*/eight_sleep.py @mezz64
# H
homeassistant/components/hive.py @Rendili @KJonline
homeassistant/components/*/hive.py @Rendili @KJonline
homeassistant/components/homekit/* @cdce8p
homeassistant/components/huawei_lte.py @scop
homeassistant/components/*/huawei_lte.py @scop
# K
homeassistant/components/knx.py @Julius2342
homeassistant/components/*/knx.py @Julius2342
homeassistant/components/konnected.py @heythisisnate
homeassistant/components/*/konnected.py @heythisisnate
# L
homeassistant/components/lifx.py @amelchio
homeassistant/components/*/lifx.py @amelchio
# M
homeassistant/components/matrix.py @tinloaf
homeassistant/components/*/matrix.py @tinloaf
homeassistant/components/melissa.py @kennedyshead
homeassistant/components/*/melissa.py @kennedyshead
homeassistant/components/*/mystrom.py @fabaff
# O
homeassistant/components/openuv/* @bachya
homeassistant/components/*/openuv.py @bachya
# Q
homeassistant/components/qwikswitch.py @kellerza
homeassistant/components/*/qwikswitch.py @kellerza
# R
homeassistant/components/rainmachine/* @bachya
homeassistant/components/*/rainmachine.py @bachya
homeassistant/components/*/random.py @fabaff
homeassistant/components/*/rfxtrx.py @danielhiversen
# S
homeassistant/components/simplisafe/* @bachya
homeassistant/components/*/simplisafe.py @bachya
# T
homeassistant/components/tahoma.py @philklei
homeassistant/components/*/tahoma.py @philklei
homeassistant/components/tesla.py @zabuldon
homeassistant/components/*/tesla.py @zabuldon
homeassistant/components/tellduslive.py @molobrakos @fredrike
homeassistant/components/*/tellduslive.py @molobrakos @fredrike
homeassistant/components/tesla.py @zabuldon
homeassistant/components/*/tesla.py @zabuldon
homeassistant/components/thethingsnetwork.py @fabaff
homeassistant/components/*/thethingsnetwork.py @fabaff
homeassistant/components/tibber/* @danielhiversen
homeassistant/components/*/tibber.py @danielhiversen
homeassistant/components/tradfri/* @ggravlingen
homeassistant/components/*/tradfri.py @ggravlingen
# U
homeassistant/components/unifi.py @kane610
homeassistant/components/switch/unifi.py @kane610
homeassistant/components/upcloud.py @scop
homeassistant/components/*/upcloud.py @scop
# V
homeassistant/components/velux.py @Julius2342
homeassistant/components/*/velux.py @Julius2342
# X
homeassistant/components/*/xiaomi_aqara.py @danielhiversen @syssi
homeassistant/components/*/xiaomi_miio.py @rytilahti @syssi
# Z
homeassistant/components/zoneminder/ @rohankapoorcom
homeassistant/components/*/zoneminder.py @rohankapoorcom
# Other code
homeassistant/scripts/check_config.py @kellerza

View File

@@ -10,5 +10,5 @@ The process is straight-forward.
- Ensure tests work.
- Create a Pull Request against the [**dev**](https://github.com/home-assistant/home-assistant/tree/dev) branch of Home Assistant.
Still interested? Then you should take a peek at the [developer documentation](https://home-assistant.io/developers/) to get more details.
Still interested? Then you should take a peek at the [developer documentation](https://developers.home-assistant.io/) to get more details.

View File

@@ -10,7 +10,6 @@ LABEL maintainer="Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>"
#ENV INSTALL_OPENALPR no
#ENV INSTALL_FFMPEG no
#ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no
#ENV INSTALL_SSOCR no
#ENV INSTALL_IPERF3 no

View File

@@ -1,194 +1,201 @@
Apache License
==============
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
### Terms and Conditions for use, reproduction, and distribution
1. Definitions.
#### 1. Definitions
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
Work shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
#### 2. Grant of Copyright License
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
#### 3. Grant of Patent License
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
#### 4. Redistribution
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
#### 5. Submission of Contributions
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
#### 6. Trademarks
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
#### 7. Disclaimer of Warranty
END OF TERMS AND CONDITIONS
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
APPENDIX: How to apply the Apache License to your work.
#### 8. Limitation of Liability
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
Copyright [yyyy] [name of copyright owner]
#### 9. Accepting Warranty or Additional Liability
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
http://www.apache.org/licenses/LICENSE-2.0
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,5 +1,5 @@
Home Assistant |Build Status| |Coverage Status| |Chat Status|
=============================================================
Home Assistant |Build Status| |Coverage Status| |Chat Status| |Reviewed by Hound|
=================================================================================
Home Assistant is a home automation platform running on Python 3. It is able to track and control all devices at home and offer a platform for automating control.
@@ -21,8 +21,8 @@ Featured integrations
|screenshot-components|
The system is built using a modular approach so support for other devices or actions can be implemented easily. See also the `section on architecture <https://home-assistant.io/developers/architecture/>`__ and the `section on creating your own
components <https://home-assistant.io/developers/creating_components/>`__.
The system is built using a modular approach so support for other devices or actions can be implemented easily. See also the `section on architecture <https://developers.home-assistant.io/docs/en/architecture_index.html>`__ and the `section on creating your own
components <https://developers.home-assistant.io/docs/en/creating_component_index.html>`__.
If you run into issues while using Home Assistant or during development
of a component, check the `Home Assistant help section <https://home-assistant.io/help/>`__ of our website for further help and information.
@@ -33,6 +33,8 @@ of a component, check the `Home Assistant help section <https://home-assistant.i
:target: https://coveralls.io/r/home-assistant/home-assistant?branch=master
.. |Chat Status| image:: https://img.shields.io/discord/330944238910963714.svg
:target: https://discord.gg/c5DvZ4e
.. |Reviewed by Hound| image:: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg
:target: https://houndci.com
.. |screenshot-states| image:: https://raw.github.com/home-assistant/home-assistant/master/docs/screenshots.png
:target: https://home-assistant.io/demo/
.. |screenshot-components| image:: https://raw.github.com/home-assistant/home-assistant/dev/docs/screenshot-components.png

View File

@@ -60,14 +60,6 @@ loader module
:undoc-members:
:show-inheritance:
remote module
---------------------------
.. automodule:: homeassistant.remote
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------

View File

@@ -19,4 +19,4 @@ Indices and tables
* :ref:`modindex`
* :ref:`search`
.. _Home Assistant developers: https://home-assistant.io/developers/
.. _Home Assistant developers: https://developers.home-assistant.io/

View File

@@ -7,7 +7,6 @@ import platform
import subprocess
import sys
import threading
from typing import List, Dict, Any # noqa pylint: disable=unused-import
@@ -20,15 +19,34 @@ from homeassistant.const import (
)
def attempt_use_uvloop() -> None:
def set_loop() -> None:
"""Attempt to use uvloop."""
import asyncio
from asyncio.events import BaseDefaultEventLoopPolicy
try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
except ImportError:
pass
policy = None
if sys.platform == 'win32':
if hasattr(asyncio, 'WindowsProactorEventLoopPolicy'):
# pylint: disable=no-member
policy = asyncio.WindowsProactorEventLoopPolicy()
else:
class ProactorPolicy(BaseDefaultEventLoopPolicy):
"""Event loop policy to create proactor loops."""
_loop_factory = asyncio.ProactorEventLoop
policy = ProactorPolicy()
else:
try:
import uvloop
except ImportError:
pass
else:
policy = uvloop.EventLoopPolicy()
if policy is not None:
asyncio.set_event_loop_policy(policy)
def validate_python() -> None:
@@ -240,51 +258,39 @@ def cmdline() -> List[str]:
return [arg for arg in sys.argv if arg != '--daemon']
def setup_and_run_hass(config_dir: str,
args: argparse.Namespace) -> int:
async def setup_and_run_hass(config_dir: str,
args: argparse.Namespace) -> int:
"""Set up HASS and run."""
from homeassistant import bootstrap
from homeassistant import bootstrap, core
# Run a simple daemon runner process on Windows to handle restarts
if os.name == 'nt' and '--runner' not in sys.argv:
nt_args = cmdline() + ['--runner']
while True:
try:
subprocess.check_call(nt_args)
sys.exit(0)
except subprocess.CalledProcessError as exc:
if exc.returncode != RESTART_EXIT_CODE:
sys.exit(exc.returncode)
hass = core.HomeAssistant()
if args.demo_mode:
config = {
'frontend': {},
'demo': {}
} # type: Dict[str, Any]
hass = bootstrap.from_config_dict(
config, config_dir=config_dir, verbose=args.verbose,
bootstrap.async_from_config_dict(
config, hass, config_dir=config_dir, verbose=args.verbose,
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,
log_file=args.log_file, log_no_color=args.log_no_color)
else:
config_file = ensure_config_file(config_dir)
print('Config directory:', config_dir)
hass = bootstrap.from_config_file(
config_file, verbose=args.verbose, skip_pip=args.skip_pip,
await bootstrap.async_from_config_file(
config_file, hass, verbose=args.verbose, skip_pip=args.skip_pip,
log_rotate_days=args.log_rotate_days, log_file=args.log_file,
log_no_color=args.log_no_color)
if hass is 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(_: Any) -> None:
"""Open the web interface in a browser."""
if hass.config.api is not None: # type: ignore
if hass.config.api is not None:
import webbrowser
webbrowser.open(hass.config.api.base_url) # type: ignore
webbrowser.open(hass.config.api.base_url)
run_callback_threadsafe(
hass.loop,
@@ -292,7 +298,7 @@ def setup_and_run_hass(config_dir: str,
EVENT_HOMEASSISTANT_START, open_browser
)
return hass.start()
return await hass.async_run()
def try_to_restart() -> None:
@@ -347,7 +353,20 @@ def main() -> int:
monkey_patch.disable_c_asyncio()
monkey_patch.patch_weakref_tasks()
attempt_use_uvloop()
set_loop()
# Run a simple daemon runner process on Windows to handle restarts
if os.name == 'nt' and '--runner' not in sys.argv:
nt_args = cmdline() + ['--runner']
while True:
try:
subprocess.check_call(nt_args)
sys.exit(0)
except KeyboardInterrupt:
sys.exit(0)
except subprocess.CalledProcessError as exc:
if exc.returncode != RESTART_EXIT_CODE:
sys.exit(exc.returncode)
args = get_arguments()
@@ -366,11 +385,12 @@ def main() -> int:
if args.pid_file:
write_pid(args.pid_file)
exit_code = setup_and_run_hass(config_dir, args)
from homeassistant.util.async_ import asyncio_run
exit_code = asyncio_run(setup_and_run_hass(config_dir, args))
if exit_code == RESTART_EXIT_CODE and not args.runner:
try_to_restart()
return exit_code
return exit_code # type: ignore # mypy cannot yet infer it
if __name__ == "__main__":

View File

@@ -2,64 +2,88 @@
import asyncio
import logging
from collections import OrderedDict
from datetime import timedelta
from typing import Any, Dict, List, Optional, Tuple, cast
import jwt
from homeassistant import data_entry_flow
from homeassistant.core import callback
from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION
from homeassistant.core import callback, HomeAssistant
from homeassistant.util import dt as dt_util
from . import models
from . import auth_store
from .providers import auth_provider_from_config
from . import auth_store, models
from .mfa_modules import auth_mfa_module_from_config, MultiFactorAuthModule
from .providers import auth_provider_from_config, AuthProvider, LoginFlow
EVENT_USER_ADDED = 'user_added'
EVENT_USER_REMOVED = 'user_removed'
_LOGGER = logging.getLogger(__name__)
_MfaModuleDict = Dict[str, MultiFactorAuthModule]
_ProviderKey = Tuple[str, Optional[str]]
_ProviderDict = Dict[_ProviderKey, AuthProvider]
async def auth_manager_from_config(hass, provider_configs):
"""Initialize an auth manager from config."""
async def auth_manager_from_config(
hass: HomeAssistant,
provider_configs: List[Dict[str, Any]],
module_configs: List[Dict[str, Any]]) -> 'AuthManager':
"""Initialize an auth manager from config.
CORE_CONFIG_SCHEMA will make sure do duplicated auth providers or
mfa modules exist in configs.
"""
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 = []
providers = ()
# So returned auth providers are in same order as config
provider_hash = OrderedDict()
provider_hash = OrderedDict() # type: _ProviderDict
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)
if module_configs:
modules = await asyncio.gather(
*[auth_mfa_module_from_config(hass, config)
for config in module_configs])
else:
modules = ()
# So returned auth modules are in same order as config
module_hash = OrderedDict() # type: _MfaModuleDict
for module in modules:
module_hash[module.id] = module
manager = AuthManager(hass, store, provider_hash, module_hash)
return manager
class AuthManager:
"""Manage the authentication for Home Assistant."""
def __init__(self, hass, store, providers):
def __init__(self, hass: HomeAssistant, store: auth_store.AuthStore,
providers: _ProviderDict, mfa_modules: _MfaModuleDict) \
-> None:
"""Initialize the auth manager."""
self.hass = hass
self._store = store
self._providers = providers
self._mfa_modules = mfa_modules
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):
def active(self) -> bool:
"""Return if any auth providers are registered."""
return bool(self._providers)
@property
def support_legacy(self):
def support_legacy(self) -> bool:
"""
Return if legacy_api_password auth providers are registered.
@@ -71,47 +95,82 @@ class AuthManager:
return False
@property
def auth_providers(self):
def auth_providers(self) -> List[AuthProvider]:
"""Return a list of available auth providers."""
return list(self._providers.values())
async def async_get_users(self):
@property
def auth_mfa_modules(self) -> List[MultiFactorAuthModule]:
"""Return a list of available auth modules."""
return list(self._mfa_modules.values())
def get_auth_mfa_module(self, module_id: str) \
-> Optional[MultiFactorAuthModule]:
"""Return an multi-factor auth module, None if not found."""
return self._mfa_modules.get(module_id)
async def async_get_users(self) -> List[models.User]:
"""Retrieve all users."""
return await self._store.async_get_users()
async def async_get_user(self, user_id):
async def async_get_user(self, user_id: str) -> Optional[models.User]:
"""Retrieve a user."""
return await self._store.async_get_user(user_id)
async def async_create_system_user(self, name):
async def async_get_user_by_credentials(
self, credentials: models.Credentials) -> Optional[models.User]:
"""Get a user by credential, return None if not found."""
for user in await self.async_get_users():
for creds in user.credentials:
if creds.id == credentials.id:
return user
return None
async def async_create_system_user(self, name: str) -> models.User:
"""Create a system user."""
return await self._store.async_create_user(
user = await self._store.async_create_user(
name=name,
system_generated=True,
is_active=True,
groups=[],
)
async def async_create_user(self, name):
self.hass.bus.async_fire(EVENT_USER_ADDED, {
'user_id': user.id
})
return user
async def async_create_user(self, name: str) -> models.User:
"""Create a user."""
group = (await self._store.async_get_groups())[0]
kwargs = {
'name': name,
'is_active': True,
}
'groups': [group]
} # type: Dict[str, Any]
if await self._user_should_be_owner():
kwargs['is_owner'] = True
return await self._store.async_create_user(**kwargs)
user = await self._store.async_create_user(**kwargs)
async def async_get_or_create_user(self, credentials):
self.hass.bus.async_fire(EVENT_USER_ADDED, {
'user_id': user.id
})
return user
async def async_get_or_create_user(self, credentials: models.Credentials) \
-> models.User:
"""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.')
user = await self.async_get_user_by_credentials(credentials)
if user is None:
raise ValueError('Unable to find the user.')
else:
return user
auth_provider = self._async_get_auth_provider(credentials)
@@ -121,17 +180,24 @@ class AuthManager:
info = await auth_provider.async_user_meta_for_credentials(
credentials)
return await self._store.async_create_user(
user = await self._store.async_create_user(
credentials=credentials,
name=info.get('name'),
is_active=info.get('is_active', False)
name=info.name,
is_active=info.is_active,
)
async def async_link_user(self, user, credentials):
self.hass.bus.async_fire(EVENT_USER_ADDED, {
'user_id': user.id
})
return user
async def async_link_user(self, user: models.User,
credentials: models.Credentials) -> None:
"""Link credentials to an existing user."""
await self._store.async_link_user(user, credentials)
async def async_remove_user(self, user):
async def async_remove_user(self, user: models.User) -> None:
"""Remove a user."""
tasks = [
self.async_remove_credentials(credentials)
@@ -143,27 +209,76 @@ class AuthManager:
await self._store.async_remove_user(user)
async def async_activate_user(self, user):
self.hass.bus.async_fire(EVENT_USER_REMOVED, {
'user_id': user.id
})
async def async_activate_user(self, user: models.User) -> None:
"""Activate a user."""
await self._store.async_activate_user(user)
async def async_deactivate_user(self, user):
async def async_deactivate_user(self, user: models.User) -> None:
"""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):
async def async_remove_credentials(
self, credentials: models.Credentials) -> None:
"""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)
# https://github.com/python/mypy/issues/1424
await provider.async_will_remove_credentials( # type: ignore
credentials)
await self._store.async_remove_credentials(credentials)
async def async_create_refresh_token(self, user, client_id=None):
async def async_enable_user_mfa(self, user: models.User,
mfa_module_id: str, data: Any) -> None:
"""Enable a multi-factor auth module for user."""
if user.system_generated:
raise ValueError('System generated users cannot enable '
'multi-factor auth module.')
module = self.get_auth_mfa_module(mfa_module_id)
if module is None:
raise ValueError('Unable find multi-factor auth module: {}'
.format(mfa_module_id))
await module.async_setup_user(user.id, data)
async def async_disable_user_mfa(self, user: models.User,
mfa_module_id: str) -> None:
"""Disable a multi-factor auth module for user."""
if user.system_generated:
raise ValueError('System generated users cannot disable '
'multi-factor auth module.')
module = self.get_auth_mfa_module(mfa_module_id)
if module is None:
raise ValueError('Unable find multi-factor auth module: {}'
.format(mfa_module_id))
await module.async_depose_user(user.id)
async def async_get_enabled_mfa(self, user: models.User) -> Dict[str, str]:
"""List enabled mfa modules for user."""
modules = OrderedDict() # type: Dict[str, str]
for module_id, module in self._mfa_modules.items():
if await module.async_is_user_setup(user.id):
modules[module_id] = module.name
return modules
async def async_create_refresh_token(
self, user: models.User, client_id: Optional[str] = None,
client_name: Optional[str] = None,
client_icon: Optional[str] = None,
token_type: Optional[str] = None,
access_token_expiration: timedelta = ACCESS_TOKEN_EXPIRATION) \
-> models.RefreshToken:
"""Create a new refresh token for a user."""
if not user.is_active:
raise ValueError('User is not active')
@@ -173,64 +288,154 @@ class AuthManager:
'System generated users cannot have refresh tokens connected '
'to a client.')
if not user.system_generated and client_id is None:
if token_type is None:
if user.system_generated:
token_type = models.TOKEN_TYPE_SYSTEM
else:
token_type = models.TOKEN_TYPE_NORMAL
if user.system_generated != (token_type == models.TOKEN_TYPE_SYSTEM):
raise ValueError(
'System generated users can only have system type '
'refresh tokens')
if token_type == models.TOKEN_TYPE_NORMAL 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)
if (token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN and
client_name is None):
raise ValueError('Client_name is required for long-lived access '
'token')
async def async_get_refresh_token(self, token):
if token_type == models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN:
for token in user.refresh_tokens.values():
if (token.client_name == client_name and token.token_type ==
models.TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN):
# Each client_name can only have one
# long_lived_access_token type of refresh token
raise ValueError('{} already exists'.format(client_name))
return await self._store.async_create_refresh_token(
user, client_id, client_name, client_icon,
token_type, access_token_expiration)
async def async_get_refresh_token(
self, token_id: str) -> Optional[models.RefreshToken]:
"""Get refresh token by id."""
return await self._store.async_get_refresh_token(token_id)
async def async_get_refresh_token_by_token(
self, token: str) -> Optional[models.RefreshToken]:
"""Get refresh token by token."""
return await self._store.async_get_refresh_token(token)
return await self._store.async_get_refresh_token_by_token(token)
async def async_remove_refresh_token(self,
refresh_token: models.RefreshToken) \
-> None:
"""Delete a refresh token."""
await self._store.async_remove_refresh_token(refresh_token)
@callback
def async_create_access_token(self, refresh_token):
def async_create_access_token(self,
refresh_token: models.RefreshToken,
remote_ip: Optional[str] = None) -> str:
"""Create a new access token."""
access_token = models.AccessToken(refresh_token=refresh_token)
self._access_tokens[access_token.token] = access_token
return access_token
self._store.async_log_refresh_token_usage(refresh_token, remote_ip)
@callback
def async_get_access_token(self, token):
"""Get an access token."""
tkn = self._access_tokens.get(token)
# pylint: disable=no-self-use
now = dt_util.utcnow()
return jwt.encode({
'iss': refresh_token.id,
'iat': now,
'exp': now + refresh_token.access_token_expiration,
}, refresh_token.jwt_key, algorithm='HS256').decode()
if tkn is None:
_LOGGER.debug('Attempt to get non-existing access token')
async def async_validate_access_token(
self, token: str) -> Optional[models.RefreshToken]:
"""Return refresh token if an access token is valid."""
try:
unverif_claims = jwt.decode(token, verify=False)
except jwt.InvalidTokenError:
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)
refresh_token = await self.async_get_refresh_token(
cast(str, unverif_claims.get('iss')))
if refresh_token is None:
jwt_key = ''
issuer = ''
else:
jwt_key = refresh_token.jwt_key
issuer = refresh_token.id
try:
jwt.decode(
token,
jwt_key,
leeway=10,
issuer=issuer,
algorithms=['HS256']
)
except jwt.InvalidTokenError:
return None
return tkn
if refresh_token is None or not refresh_token.user.is_active:
return None
async def _async_create_login_flow(self, handler, *, source, data):
return refresh_token
async def _async_create_login_flow(
self, handler: _ProviderKey, *, context: Optional[Dict],
data: Optional[Any]) -> data_entry_flow.FlowHandler:
"""Create a login flow."""
auth_provider = self._providers[handler]
return await auth_provider.async_credential_flow()
return await auth_provider.async_login_flow(context)
async def _async_finish_login_flow(self, result):
"""Result of a credential login flow."""
async def _async_finish_login_flow(
self, flow: LoginFlow, result: Dict[str, Any]) \
-> Dict[str, Any]:
"""Return a user as result of login flow."""
if result['type'] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
return None
return result
# we got final result
if isinstance(result['data'], models.User):
result['result'] = result['data']
return result
auth_provider = self._providers[result['handler']]
return await auth_provider.async_get_or_create_credentials(
credentials = await auth_provider.async_get_or_create_credentials(
result['data'])
if flow.context is not None and flow.context.get('credential_only'):
result['result'] = credentials
return result
# multi-factor module cannot enabled for new credential
# which has not linked to a user yet
if auth_provider.support_mfa and not credentials.is_new:
user = await self.async_get_user_by_credentials(credentials)
if user is not None:
modules = await self.async_get_enabled_mfa(user)
if modules:
flow.user = user
flow.available_mfa_modules = modules
return await flow.async_step_select_mfa_module()
result['result'] = await self.async_get_or_create_user(credentials)
return result
@callback
def _async_get_auth_provider(self, credentials):
"""Helper to get auth provider from a set of credentials."""
def _async_get_auth_provider(
self, credentials: models.Credentials) -> Optional[AuthProvider]:
"""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):
async def _user_should_be_owner(self) -> bool:
"""Determine if user should be owner.
A user should be an owner if it is the first non-system user that is

View File

@@ -1,13 +1,20 @@
"""Storage for auth models."""
from collections import OrderedDict
from datetime import timedelta
import hmac
from logging import getLogger
from typing import Any, Dict, List, Optional # noqa: F401
from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION
from homeassistant.core import HomeAssistant, callback
from homeassistant.util import dt as dt_util
from . import models
from .permissions import DEFAULT_POLICY
STORAGE_VERSION = 1
STORAGE_KEY = 'auth'
INITIAL_GROUP_NAME = 'All Access'
class AuthStore:
@@ -19,35 +26,57 @@ class AuthStore:
called that needs it.
"""
def __init__(self, hass):
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the auth store."""
self.hass = hass
self._users = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
self._users = None # type: Optional[Dict[str, models.User]]
self._groups = None # type: Optional[Dict[str, models.Group]]
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY,
private=True)
async def async_get_users(self):
async def async_get_groups(self) -> List[models.Group]:
"""Retrieve all users."""
if self._groups is None:
await self._async_load()
assert self._groups is not None
return list(self._groups.values())
async def async_get_users(self) -> List[models.User]:
"""Retrieve all users."""
if self._users is None:
await self.async_load()
await self._async_load()
assert self._users is not None
return list(self._users.values())
async def async_get_user(self, user_id):
async def async_get_user(self, user_id: str) -> Optional[models.User]:
"""Retrieve a user by id."""
if self._users is None:
await self.async_load()
await self._async_load()
assert self._users is not None
return self._users.get(user_id)
async def async_create_user(self, name, is_owner=None, is_active=None,
system_generated=None, credentials=None):
async def async_create_user(
self, name: Optional[str], is_owner: Optional[bool] = None,
is_active: Optional[bool] = None,
system_generated: Optional[bool] = None,
credentials: Optional[models.Credentials] = None,
groups: Optional[List[models.Group]] = None) -> models.User:
"""Create a new user."""
if self._users is None:
await self.async_load()
await self._async_load()
assert self._users is not None
assert self._groups is not None
kwargs = {
'name': name
}
'name': name,
# Until we get group management, we just put everyone in the
# same group.
'groups': groups or [],
} # type: Dict[str, Any]
if is_owner is not None:
kwargs['is_owner'] = is_owner
@@ -63,36 +92,46 @@ class AuthStore:
self._users[new_user.id] = new_user
if credentials is None:
await self.async_save()
self._async_schedule_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):
async def async_link_user(self, user: models.User,
credentials: models.Credentials) -> None:
"""Add credentials to an existing user."""
user.credentials.append(credentials)
await self.async_save()
self._async_schedule_save()
credentials.is_new = False
async def async_remove_user(self, user):
async def async_remove_user(self, user: models.User) -> None:
"""Remove a user."""
self._users.pop(user.id)
await self.async_save()
if self._users is None:
await self._async_load()
assert self._users is not None
async def async_activate_user(self, user):
self._users.pop(user.id)
self._async_schedule_save()
async def async_activate_user(self, user: models.User) -> None:
"""Activate a user."""
user.is_active = True
await self.async_save()
self._async_schedule_save()
async def async_deactivate_user(self, user):
async def async_deactivate_user(self, user: models.User) -> None:
"""Activate a user."""
user.is_active = False
await self.async_save()
self._async_schedule_save()
async def async_remove_credentials(self, credentials):
async def async_remove_credentials(
self, credentials: models.Credentials) -> None:
"""Remove credentials."""
if self._users is None:
await self._async_load()
assert self._users is not None
for user in self._users.values():
found = None
@@ -105,28 +144,85 @@ class AuthStore:
user.credentials.pop(found)
break
await self.async_save()
self._async_schedule_save()
async def async_create_refresh_token(self, user, client_id=None):
async def async_create_refresh_token(
self, user: models.User, client_id: Optional[str] = None,
client_name: Optional[str] = None,
client_icon: Optional[str] = None,
token_type: str = models.TOKEN_TYPE_NORMAL,
access_token_expiration: timedelta = ACCESS_TOKEN_EXPIRATION) \
-> models.RefreshToken:
"""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()
kwargs = {
'user': user,
'client_id': client_id,
'token_type': token_type,
'access_token_expiration': access_token_expiration
} # type: Dict[str, Any]
if client_name:
kwargs['client_name'] = client_name
if client_icon:
kwargs['client_icon'] = client_icon
refresh_token = models.RefreshToken(**kwargs)
user.refresh_tokens[refresh_token.id] = refresh_token
self._async_schedule_save()
return refresh_token
async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
async def async_remove_refresh_token(
self, refresh_token: models.RefreshToken) -> None:
"""Remove a refresh token."""
if self._users is None:
await self.async_load()
await self._async_load()
assert self._users is not None
for user in self._users.values():
refresh_token = user.refresh_tokens.get(token)
if user.refresh_tokens.pop(refresh_token.id, None):
self._async_schedule_save()
break
async def async_get_refresh_token(
self, token_id: str) -> Optional[models.RefreshToken]:
"""Get refresh token by id."""
if self._users is None:
await self._async_load()
assert self._users is not None
for user in self._users.values():
refresh_token = user.refresh_tokens.get(token_id)
if refresh_token is not None:
return refresh_token
return None
async def async_load(self):
async def async_get_refresh_token_by_token(
self, token: str) -> Optional[models.RefreshToken]:
"""Get refresh token by token."""
if self._users is None:
await self._async_load()
assert self._users is not None
found = None
for user in self._users.values():
for refresh_token in user.refresh_tokens.values():
if hmac.compare_digest(refresh_token.token, token):
found = refresh_token
return found
@callback
def async_log_refresh_token_usage(
self, refresh_token: models.RefreshToken,
remote_ip: Optional[str] = None) -> None:
"""Update refresh token last used information."""
refresh_token.last_used_at = dt_util.utcnow()
refresh_token.last_used_ip = remote_ip
self._async_schedule_save()
async def _async_load(self) -> None:
"""Load the users."""
data = await self._store.async_load()
@@ -135,14 +231,45 @@ class AuthStore:
if self._users is not None:
return
users = OrderedDict()
if data is None:
self._users = users
self._set_defaults()
return
users = OrderedDict() # type: Dict[str, models.User]
groups = OrderedDict() # type: Dict[str, models.Group]
# When creating objects we mention each attribute explicetely. This
# prevents crashing if user rolls back HA version after a new property
# was added.
for group_dict in data.get('groups', []):
groups[group_dict['id']] = models.Group(
name=group_dict['name'],
id=group_dict['id'],
policy=group_dict.get('policy', DEFAULT_POLICY),
)
migrate_group = None
if not groups:
migrate_group = models.Group(
name=INITIAL_GROUP_NAME,
policy=DEFAULT_POLICY
)
groups[migrate_group.id] = migrate_group
for user_dict in data['users']:
users[user_dict['id']] = models.User(**user_dict)
users[user_dict['id']] = models.User(
name=user_dict['name'],
groups=[groups[group_id] for group_id
in user_dict.get('group_ids', [])],
id=user_dict['id'],
is_owner=user_dict['is_owner'],
is_active=user_dict['is_active'],
system_generated=user_dict['system_generated'],
)
if migrate_group is not None and not user_dict['system_generated']:
users[user_dict['id']].groups = [migrate_group]
for cred_dict in data['credentials']:
users[cred_dict['user_id']].credentials.append(models.Credentials(
@@ -153,37 +280,71 @@ class AuthStore:
data=cred_dict['data'],
))
refresh_tokens = OrderedDict()
for rt_dict in data['refresh_tokens']:
# Filter out the old keys that don't have jwt_key (pre-0.76)
if 'jwt_key' not in rt_dict:
continue
created_at = dt_util.parse_datetime(rt_dict['created_at'])
if created_at is None:
getLogger(__name__).error(
'Ignoring refresh token %(id)s with invalid created_at '
'%(created_at)s for user_id %(user_id)s', rt_dict)
continue
token_type = rt_dict.get('token_type')
if token_type is None:
if rt_dict['client_id'] is None:
token_type = models.TOKEN_TYPE_SYSTEM
else:
token_type = models.TOKEN_TYPE_NORMAL
# old refresh_token don't have last_used_at (pre-0.78)
last_used_at_str = rt_dict.get('last_used_at')
if last_used_at_str:
last_used_at = dt_util.parse_datetime(last_used_at_str)
else:
last_used_at = None
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']),
# use dict.get to keep backward compatibility
client_name=rt_dict.get('client_name'),
client_icon=rt_dict.get('client_icon'),
token_type=token_type,
created_at=created_at,
access_token_expiration=timedelta(
seconds=rt_dict['access_token_expiration']),
token=rt_dict['token'],
jwt_key=rt_dict['jwt_key'],
last_used_at=last_used_at,
last_used_ip=rt_dict.get('last_used_ip'),
)
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)
users[rt_dict['user_id']].refresh_tokens[token.id] = token
self._groups = groups
self._users = users
async def async_save(self):
@callback
def _async_schedule_save(self) -> None:
"""Save users."""
if self._users is None:
return
self._store.async_delay_save(self._data_to_save, 1)
@callback
def _data_to_save(self) -> Dict:
"""Return the data to store."""
assert self._users is not None
assert self._groups is not None
users = [
{
'id': user.id,
'group_ids': [group.id for group in user.groups],
'is_owner': user.is_owner,
'is_active': user.is_active,
'name': user.name,
@@ -192,6 +353,18 @@ class AuthStore:
for user in self._users.values()
]
groups = []
for group in self._groups.values():
g_dict = {
'name': group.name,
'id': group.id,
} # type: Dict[str, Any]
if group.policy is not DEFAULT_POLICY:
g_dict['policy'] = group.policy
groups.append(g_dict)
credentials = [
{
'id': credential.id,
@@ -209,32 +382,41 @@ class AuthStore:
'id': refresh_token.id,
'user_id': user.id,
'client_id': refresh_token.client_id,
'client_name': refresh_token.client_name,
'client_icon': refresh_token.client_icon,
'token_type': refresh_token.token_type,
'created_at': refresh_token.created_at.isoformat(),
'access_token_expiration':
refresh_token.access_token_expiration.total_seconds(),
'token': refresh_token.token,
'jwt_key': refresh_token.jwt_key,
'last_used_at':
refresh_token.last_used_at.isoformat()
if refresh_token.last_used_at else None,
'last_used_ip': refresh_token.last_used_ip,
}
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 = {
return {
'users': users,
'groups': groups,
'credentials': credentials,
'access_tokens': access_tokens,
'refresh_tokens': refresh_tokens,
}
await self._store.async_save(data, delay=1)
def _set_defaults(self) -> None:
"""Set default values for auth store."""
self._users = OrderedDict() # type: Dict[str, models.User]
# Add default group
all_access_group = models.Group(
name=INITIAL_GROUP_NAME,
policy=DEFAULT_POLICY,
)
groups = OrderedDict() # type: Dict[str, models.Group]
groups[all_access_group.id] = all_access_group
self._groups = groups

View File

@@ -2,3 +2,4 @@
from datetime import timedelta
ACCESS_TOKEN_EXPIRATION = timedelta(minutes=30)
MFA_SESSION_EXPIRATION = timedelta(minutes=5)

View File

@@ -0,0 +1,175 @@
"""Plugable auth modules for Home Assistant."""
import importlib
import logging
import types
from typing import Any, Dict, Optional
import voluptuous as vol
from voluptuous.humanize import humanize_error
from homeassistant import requirements, data_entry_flow
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.decorator import Registry
MULTI_FACTOR_AUTH_MODULES = Registry()
MULTI_FACTOR_AUTH_MODULE_SCHEMA = vol.Schema({
vol.Required(CONF_TYPE): str,
vol.Optional(CONF_NAME): str,
# Specify ID if you have two mfa auth module for same type.
vol.Optional(CONF_ID): str,
}, extra=vol.ALLOW_EXTRA)
DATA_REQS = 'mfa_auth_module_reqs_processed'
_LOGGER = logging.getLogger(__name__)
class MultiFactorAuthModule:
"""Multi-factor Auth Module of validation function."""
DEFAULT_TITLE = 'Unnamed auth module'
MAX_RETRY_TIME = 3
def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None:
"""Initialize an auth module."""
self.hass = hass
self.config = config
@property
def id(self) -> str: # pylint: disable=invalid-name
"""Return id of the auth module.
Default is same as type
"""
return self.config.get(CONF_ID, self.type)
@property
def type(self) -> str:
"""Return type of the module."""
return self.config[CONF_TYPE] # type: ignore
@property
def name(self) -> str:
"""Return the name of the auth module."""
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
# Implement by extending class
@property
def input_schema(self) -> vol.Schema:
"""Return a voluptuous schema to define mfa auth module's input."""
raise NotImplementedError
async def async_setup_flow(self, user_id: str) -> 'SetupFlow':
"""Return a data entry flow handler for setup module.
Mfa module should extend SetupFlow
"""
raise NotImplementedError
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up user for mfa auth module."""
raise NotImplementedError
async def async_depose_user(self, user_id: str) -> None:
"""Remove user from mfa module."""
raise NotImplementedError
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
raise NotImplementedError
async def async_validate(
self, user_id: str, user_input: Dict[str, Any]) -> bool:
"""Return True if validation passed."""
raise NotImplementedError
class SetupFlow(data_entry_flow.FlowHandler):
"""Handler for the setup flow."""
def __init__(self, auth_module: MultiFactorAuthModule,
setup_schema: vol.Schema,
user_id: str) -> None:
"""Initialize the setup flow."""
self._auth_module = auth_module
self._setup_schema = setup_schema
self._user_id = user_id
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the first step of setup flow.
Return self.async_show_form(step_id='init') if user_input == None.
Return self.async_create_entry(data={'result': result}) if finish.
"""
errors = {} # type: Dict[str, str]
if user_input:
result = await self._auth_module.async_setup_user(
self._user_id, user_input)
return self.async_create_entry(
title=self._auth_module.name,
data={'result': result}
)
return self.async_show_form(
step_id='init',
data_schema=self._setup_schema,
errors=errors
)
async def auth_mfa_module_from_config(
hass: HomeAssistant, config: Dict[str, Any]) \
-> MultiFactorAuthModule:
"""Initialize an auth module from a config."""
module_name = config[CONF_TYPE]
module = await _load_mfa_module(hass, module_name)
try:
config = module.CONFIG_SCHEMA(config) # type: ignore
except vol.Invalid as err:
_LOGGER.error('Invalid configuration for multi-factor module %s: %s',
module_name, humanize_error(config, err))
raise
return MULTI_FACTOR_AUTH_MODULES[module_name](hass, config) # type: ignore
async def _load_mfa_module(hass: HomeAssistant, module_name: str) \
-> types.ModuleType:
"""Load an mfa auth module."""
module_path = 'homeassistant.auth.mfa_modules.{}'.format(module_name)
try:
module = importlib.import_module(module_path)
except ImportError as err:
_LOGGER.error('Unable to load mfa module %s: %s', module_name, err)
raise HomeAssistantError('Unable to load mfa module {}: {}'.format(
module_name, err))
if hass.config.skip_pip or not hasattr(module, 'REQUIREMENTS'):
return module
processed = hass.data.get(DATA_REQS)
if processed and module_name in processed:
return module
processed = hass.data[DATA_REQS] = set()
# https://github.com/python/mypy/issues/1424
req_success = await requirements.async_process_requirements(
hass, module_path, module.REQUIREMENTS) # type: ignore
if not req_success:
raise HomeAssistantError(
'Unable to process requirements of mfa module {}'.format(
module_name))
processed.add(module_name)
return module

View File

@@ -0,0 +1,89 @@
"""Example auth module."""
import logging
from typing import Any, Dict
import voluptuous as vol
from homeassistant.core import HomeAssistant
from . import MultiFactorAuthModule, MULTI_FACTOR_AUTH_MODULES, \
MULTI_FACTOR_AUTH_MODULE_SCHEMA, SetupFlow
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({
vol.Required('data'): [vol.Schema({
vol.Required('user_id'): str,
vol.Required('pin'): str,
})]
}, extra=vol.PREVENT_EXTRA)
_LOGGER = logging.getLogger(__name__)
@MULTI_FACTOR_AUTH_MODULES.register('insecure_example')
class InsecureExampleModule(MultiFactorAuthModule):
"""Example auth module validate pin."""
DEFAULT_TITLE = 'Insecure Personal Identify Number'
def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None:
"""Initialize the user data store."""
super().__init__(hass, config)
self._data = config['data']
@property
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({'pin': str})
@property
def setup_schema(self) -> vol.Schema:
"""Validate async_setup_user input data."""
return vol.Schema({'pin': str})
async def async_setup_flow(self, user_id: str) -> SetupFlow:
"""Return a data entry flow handler for setup module.
Mfa module should extend SetupFlow
"""
return SetupFlow(self, self.setup_schema, user_id)
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up user to use mfa module."""
# data shall has been validate in caller
pin = setup_data['pin']
for data in self._data:
if data['user_id'] == user_id:
# already setup, override
data['pin'] = pin
return
self._data.append({'user_id': user_id, 'pin': pin})
async def async_depose_user(self, user_id: str) -> None:
"""Remove user from mfa module."""
found = None
for data in self._data:
if data['user_id'] == user_id:
found = data
break
if found:
self._data.remove(found)
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
for data in self._data:
if data['user_id'] == user_id:
return True
return False
async def async_validate(
self, user_id: str, user_input: Dict[str, Any]) -> bool:
"""Return True if validation passed."""
for data in self._data:
if data['user_id'] == user_id:
# user_input has been validate in caller
if data['pin'] == user_input['pin']:
return True
return False

View File

@@ -0,0 +1,325 @@
"""HMAC-based One-time Password auth module.
Sending HOTP through notify service
"""
import logging
from collections import OrderedDict
from typing import Any, Dict, Optional, Tuple, List # noqa: F401
import attr
import voluptuous as vol
from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from . import MultiFactorAuthModule, MULTI_FACTOR_AUTH_MODULES, \
MULTI_FACTOR_AUTH_MODULE_SCHEMA, SetupFlow
REQUIREMENTS = ['pyotp==2.2.6']
CONF_MESSAGE = 'message'
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({
vol.Optional(CONF_INCLUDE): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_EXCLUDE): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_MESSAGE,
default='{} is your Home Assistant login code'): str
}, extra=vol.PREVENT_EXTRA)
STORAGE_VERSION = 1
STORAGE_KEY = 'auth_module.notify'
STORAGE_USERS = 'users'
STORAGE_USER_ID = 'user_id'
INPUT_FIELD_CODE = 'code'
_LOGGER = logging.getLogger(__name__)
def _generate_secret() -> str:
"""Generate a secret."""
import pyotp
return str(pyotp.random_base32())
def _generate_random() -> int:
"""Generate a 8 digit number."""
import pyotp
return int(pyotp.random_base32(length=8, chars=list('1234567890')))
def _generate_otp(secret: str, count: int) -> str:
"""Generate one time password."""
import pyotp
return str(pyotp.HOTP(secret).at(count))
def _verify_otp(secret: str, otp: str, count: int) -> bool:
"""Verify one time password."""
import pyotp
return bool(pyotp.HOTP(secret).verify(otp, count))
@attr.s(slots=True)
class NotifySetting:
"""Store notify setting for one user."""
secret = attr.ib(type=str, factory=_generate_secret) # not persistent
counter = attr.ib(type=int, factory=_generate_random) # not persistent
notify_service = attr.ib(type=Optional[str], default=None)
target = attr.ib(type=Optional[str], default=None)
_UsersDict = Dict[str, NotifySetting]
@MULTI_FACTOR_AUTH_MODULES.register('notify')
class NotifyAuthModule(MultiFactorAuthModule):
"""Auth module send hmac-based one time password by notify service."""
DEFAULT_TITLE = 'Notify One-Time Password'
def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None:
"""Initialize the user data store."""
super().__init__(hass, config)
self._user_settings = None # type: Optional[_UsersDict]
self._user_store = hass.helpers.storage.Store(
STORAGE_VERSION, STORAGE_KEY, private=True)
self._include = config.get(CONF_INCLUDE, [])
self._exclude = config.get(CONF_EXCLUDE, [])
self._message_template = config[CONF_MESSAGE]
@property
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({INPUT_FIELD_CODE: str})
async def _async_load(self) -> None:
"""Load stored data."""
data = await self._user_store.async_load()
if data is None:
data = {STORAGE_USERS: {}}
self._user_settings = {
user_id: NotifySetting(**setting)
for user_id, setting in data.get(STORAGE_USERS, {}).items()
}
async def _async_save(self) -> None:
"""Save data."""
if self._user_settings is None:
return
await self._user_store.async_save({STORAGE_USERS: {
user_id: attr.asdict(
notify_setting, filter=attr.filters.exclude(
attr.fields(NotifySetting).secret,
attr.fields(NotifySetting).counter,
))
for user_id, notify_setting
in self._user_settings.items()
}})
@callback
def aync_get_available_notify_services(self) -> List[str]:
"""Return list of notify services."""
unordered_services = set()
for service in self.hass.services.async_services().get('notify', {}):
if service not in self._exclude:
unordered_services.add(service)
if self._include:
unordered_services &= set(self._include)
return sorted(unordered_services)
async def async_setup_flow(self, user_id: str) -> SetupFlow:
"""Return a data entry flow handler for setup module.
Mfa module should extend SetupFlow
"""
return NotifySetupFlow(
self, self.input_schema, user_id,
self.aync_get_available_notify_services())
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up auth module for user."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
self._user_settings[user_id] = NotifySetting(
notify_service=setup_data.get('notify_service'),
target=setup_data.get('target'),
)
await self._async_save()
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
if self._user_settings.pop(user_id, None):
await self._async_save()
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
return user_id in self._user_settings
async def async_validate(
self, user_id: str, user_input: Dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
notify_setting = self._user_settings.get(user_id, None)
if notify_setting is None:
return False
# user_input has been validate in caller
return await self.hass.async_add_executor_job(
_verify_otp, notify_setting.secret,
user_input.get(INPUT_FIELD_CODE, ''),
notify_setting.counter)
async def async_initialize_login_mfa_step(self, user_id: str) -> None:
"""Generate code and notify user."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
notify_setting = self._user_settings.get(user_id, None)
if notify_setting is None:
raise ValueError('Cannot find user_id')
def generate_secret_and_one_time_password() -> str:
"""Generate and send one time password."""
assert notify_setting
# secret and counter are not persistent
notify_setting.secret = _generate_secret()
notify_setting.counter = _generate_random()
return _generate_otp(
notify_setting.secret, notify_setting.counter)
code = await self.hass.async_add_executor_job(
generate_secret_and_one_time_password)
await self.async_notify_user(user_id, code)
async def async_notify_user(self, user_id: str, code: str) -> None:
"""Send code by user's notify service."""
if self._user_settings is None:
await self._async_load()
assert self._user_settings is not None
notify_setting = self._user_settings.get(user_id, None)
if notify_setting is None:
_LOGGER.error('Cannot find user %s', user_id)
return
await self.async_notify( # type: ignore
code, notify_setting.notify_service, notify_setting.target)
async def async_notify(self, code: str, notify_service: str,
target: Optional[str] = None) -> None:
"""Send code by notify service."""
data = {'message': self._message_template.format(code)}
if target:
data['target'] = [target]
await self.hass.services.async_call('notify', notify_service, data)
class NotifySetupFlow(SetupFlow):
"""Handler for the setup flow."""
def __init__(self, auth_module: NotifyAuthModule,
setup_schema: vol.Schema,
user_id: str,
available_notify_services: List[str]) -> None:
"""Initialize the setup flow."""
super().__init__(auth_module, setup_schema, user_id)
# to fix typing complaint
self._auth_module = auth_module # type: NotifyAuthModule
self._available_notify_services = available_notify_services
self._secret = None # type: Optional[str]
self._count = None # type: Optional[int]
self._notify_service = None # type: Optional[str]
self._target = None # type: Optional[str]
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Let user select available notify services."""
errors = {} # type: Dict[str, str]
hass = self._auth_module.hass
if user_input:
self._notify_service = user_input['notify_service']
self._target = user_input.get('target')
self._secret = await hass.async_add_executor_job(_generate_secret)
self._count = await hass.async_add_executor_job(_generate_random)
return await self.async_step_setup()
if not self._available_notify_services:
return self.async_abort(reason='no_available_service')
schema = OrderedDict() # type: Dict[str, Any]
schema['notify_service'] = vol.In(self._available_notify_services)
schema['target'] = vol.Optional(str)
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(schema),
errors=errors
)
async def async_step_setup(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Verify user can recevie one-time password."""
errors = {} # type: Dict[str, str]
hass = self._auth_module.hass
if user_input:
verified = await hass.async_add_executor_job(
_verify_otp, self._secret, user_input['code'], self._count)
if verified:
await self._auth_module.async_setup_user(
self._user_id, {
'notify_service': self._notify_service,
'target': self._target,
})
return self.async_create_entry(
title=self._auth_module.name,
data={}
)
errors['base'] = 'invalid_code'
# generate code every time, no retry logic
assert self._secret and self._count
code = await hass.async_add_executor_job(
_generate_otp, self._secret, self._count)
assert self._notify_service
await self._auth_module.async_notify(
code, self._notify_service, self._target)
return self.async_show_form(
step_id='setup',
data_schema=self._setup_schema,
description_placeholders={'notify_service': self._notify_service},
errors=errors,
)

View File

@@ -0,0 +1,214 @@
"""Time-based One Time Password auth module."""
import logging
from io import BytesIO
from typing import Any, Dict, Optional, Tuple # noqa: F401
import voluptuous as vol
from homeassistant.auth.models import User
from homeassistant.core import HomeAssistant
from . import MultiFactorAuthModule, MULTI_FACTOR_AUTH_MODULES, \
MULTI_FACTOR_AUTH_MODULE_SCHEMA, SetupFlow
REQUIREMENTS = ['pyotp==2.2.6', 'PyQRCode==1.2.1']
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
STORAGE_VERSION = 1
STORAGE_KEY = 'auth_module.totp'
STORAGE_USERS = 'users'
STORAGE_USER_ID = 'user_id'
STORAGE_OTA_SECRET = 'ota_secret'
INPUT_FIELD_CODE = 'code'
DUMMY_SECRET = 'FPPTH34D4E3MI2HG'
_LOGGER = logging.getLogger(__name__)
def _generate_qr_code(data: str) -> str:
"""Generate a base64 PNG string represent QR Code image of data."""
import pyqrcode
qr_code = pyqrcode.create(data)
with BytesIO() as buffer:
qr_code.svg(file=buffer, scale=4)
return '{}'.format(
buffer.getvalue().decode("ascii").replace('\n', '')
.replace('<?xml version="1.0" encoding="UTF-8"?>'
'<svg xmlns="http://www.w3.org/2000/svg"', '<svg')
)
def _generate_secret_and_qr_code(username: str) -> Tuple[str, str, str]:
"""Generate a secret, url, and QR code."""
import pyotp
ota_secret = pyotp.random_base32()
url = pyotp.totp.TOTP(ota_secret).provisioning_uri(
username, issuer_name="Home Assistant")
image = _generate_qr_code(url)
return ota_secret, url, image
@MULTI_FACTOR_AUTH_MODULES.register('totp')
class TotpAuthModule(MultiFactorAuthModule):
"""Auth module validate time-based one time password."""
DEFAULT_TITLE = 'Time-based One Time Password'
MAX_RETRY_TIME = 5
def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None:
"""Initialize the user data store."""
super().__init__(hass, config)
self._users = None # type: Optional[Dict[str, str]]
self._user_store = hass.helpers.storage.Store(
STORAGE_VERSION, STORAGE_KEY, private=True)
@property
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({INPUT_FIELD_CODE: str})
async def _async_load(self) -> None:
"""Load stored data."""
data = await self._user_store.async_load()
if data is None:
data = {STORAGE_USERS: {}}
self._users = data.get(STORAGE_USERS, {})
async def _async_save(self) -> None:
"""Save data."""
await self._user_store.async_save({STORAGE_USERS: self._users})
def _add_ota_secret(self, user_id: str,
secret: Optional[str] = None) -> str:
"""Create a ota_secret for user."""
import pyotp
ota_secret = secret or pyotp.random_base32() # type: str
self._users[user_id] = ota_secret # type: ignore
return ota_secret
async def async_setup_flow(self, user_id: str) -> SetupFlow:
"""Return a data entry flow handler for setup module.
Mfa module should extend SetupFlow
"""
user = await self.hass.auth.async_get_user(user_id) # type: ignore
return TotpSetupFlow(self, self.input_schema, user)
async def async_setup_user(self, user_id: str, setup_data: Any) -> str:
"""Set up auth module for user."""
if self._users is None:
await self._async_load()
result = await self.hass.async_add_executor_job(
self._add_ota_secret, user_id, setup_data.get('secret'))
await self._async_save()
return result
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._users is None:
await self._async_load()
if self._users.pop(user_id, None): # type: ignore
await self._async_save()
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._users is None:
await self._async_load()
return user_id in self._users # type: ignore
async def async_validate(
self, user_id: str, user_input: Dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._users is None:
await self._async_load()
# user_input has been validate in caller
# set INPUT_FIELD_CODE as vol.Required is not user friendly
return await self.hass.async_add_executor_job(
self._validate_2fa, user_id, user_input.get(INPUT_FIELD_CODE, ''))
def _validate_2fa(self, user_id: str, code: str) -> bool:
"""Validate two factor authentication code."""
import pyotp
ota_secret = self._users.get(user_id) # type: ignore
if ota_secret is None:
# even we cannot find user, we still do verify
# to make timing the same as if user was found.
pyotp.TOTP(DUMMY_SECRET).verify(code, valid_window=1)
return False
return bool(pyotp.TOTP(ota_secret).verify(code, valid_window=1))
class TotpSetupFlow(SetupFlow):
"""Handler for the setup flow."""
def __init__(self, auth_module: TotpAuthModule,
setup_schema: vol.Schema,
user: User) -> None:
"""Initialize the setup flow."""
super().__init__(auth_module, setup_schema, user.id)
# to fix typing complaint
self._auth_module = auth_module # type: TotpAuthModule
self._user = user
self._ota_secret = None # type: Optional[str]
self._url = None # type Optional[str]
self._image = None # type Optional[str]
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the first step of setup flow.
Return self.async_show_form(step_id='init') if user_input == None.
Return self.async_create_entry(data={'result': result}) if finish.
"""
import pyotp
errors = {} # type: Dict[str, str]
if user_input:
verified = await self.hass.async_add_executor_job( # type: ignore
pyotp.TOTP(self._ota_secret).verify, user_input['code'])
if verified:
result = await self._auth_module.async_setup_user(
self._user_id, {'secret': self._ota_secret})
return self.async_create_entry(
title=self._auth_module.name,
data={'result': result}
)
errors['base'] = 'invalid_code'
else:
hass = self._auth_module.hass
self._ota_secret, self._url, self._image = \
await hass.async_add_executor_job( # type: ignore
_generate_secret_and_qr_code, str(self._user.name))
return self.async_show_form(
step_id='init',
data_schema=self._setup_schema,
description_placeholders={
'code': self._ota_secret,
'url': self._url,
'qr_code': self._image
},
errors=errors
)

View File

@@ -1,30 +1,72 @@
"""Auth models."""
from datetime import datetime, timedelta
from typing import Dict, List, NamedTuple, Optional # noqa: F401
import uuid
import attr
from homeassistant.util import dt as dt_util
from .const import ACCESS_TOKEN_EXPIRATION
from . import permissions as perm_mdl
from .util import generate_secret
TOKEN_TYPE_NORMAL = 'normal'
TOKEN_TYPE_SYSTEM = 'system'
TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN = 'long_lived_access_token'
@attr.s(slots=True)
class Group:
"""A group."""
name = attr.ib(type=str) # type: Optional[str]
policy = attr.ib(type=perm_mdl.PolicyType)
id = attr.ib(type=str, factory=lambda: uuid.uuid4().hex)
@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))
name = attr.ib(type=str) # type: Optional[str]
id = attr.ib(type=str, 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)
groups = attr.ib(type=List, factory=list, cmp=False) # type: List[Group]
# List of credentials of a user.
credentials = attr.ib(type=list, default=attr.Factory(list), cmp=False)
credentials = attr.ib(
type=list, factory=list, cmp=False
) # type: List[Credentials]
# Tokens associated with a user.
refresh_tokens = attr.ib(type=dict, default=attr.Factory(dict), cmp=False)
refresh_tokens = attr.ib(
type=dict, factory=dict, cmp=False
) # type: Dict[str, RefreshToken]
_permissions = attr.ib(
type=perm_mdl.PolicyPermissions,
init=False,
cmp=False,
default=None,
)
@property
def permissions(self) -> perm_mdl.AbstractPermissions:
"""Return permissions object for user."""
if self.is_owner:
return perm_mdl.OwnerPermissions
if self._permissions is not None:
return self._permissions
self._permissions = perm_mdl.PolicyPermissions(
perm_mdl.merge_policies([
group.policy for group in self.groups]))
return self._permissions
@attr.s(slots=True)
@@ -32,33 +74,21 @@ 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)
client_id = attr.ib(type=Optional[str])
access_token_expiration = attr.ib(type=timedelta)
client_name = attr.ib(type=Optional[str], default=None)
client_icon = attr.ib(type=Optional[str], default=None)
token_type = attr.ib(type=str, default=TOKEN_TYPE_NORMAL,
validator=attr.validators.in_((
TOKEN_TYPE_NORMAL, TOKEN_TYPE_SYSTEM,
TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN)))
id = attr.ib(type=str, factory=lambda: uuid.uuid4().hex)
created_at = attr.ib(type=datetime, factory=dt_util.utcnow)
token = attr.ib(type=str, factory=lambda: generate_secret(64))
jwt_key = attr.ib(type=str, factory=lambda: generate_secret(64))
@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
last_used_at = attr.ib(type=Optional[datetime], default=None)
last_used_ip = attr.ib(type=Optional[str], default=None)
@attr.s(slots=True)
@@ -66,10 +96,14 @@ class Credentials:
"""Credentials for a user on an auth provider."""
auth_provider_type = attr.ib(type=str)
auth_provider_id = attr.ib(type=str)
auth_provider_id = attr.ib(type=Optional[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))
id = attr.ib(type=str, factory=lambda: uuid.uuid4().hex)
is_new = attr.ib(type=bool, default=True)
UserMeta = NamedTuple("UserMeta",
[('name', Optional[str]), ('is_active', bool)])

View File

@@ -0,0 +1,252 @@
"""Permissions for Home Assistant."""
from typing import ( # noqa: F401
cast, Any, Callable, Dict, List, Mapping, Set, Tuple, Union)
import voluptuous as vol
from homeassistant.core import State
CategoryType = Union[Mapping[str, 'CategoryType'], bool, None]
PolicyType = Mapping[str, CategoryType]
# Default policy if group has no policy applied.
DEFAULT_POLICY = {
"entities": True
} # type: PolicyType
CAT_ENTITIES = 'entities'
ENTITY_DOMAINS = 'domains'
ENTITY_ENTITY_IDS = 'entity_ids'
VALUES_SCHEMA = vol.Any(True, vol.Schema({
str: True
}))
ENTITY_POLICY_SCHEMA = vol.Any(True, vol.Schema({
vol.Optional(ENTITY_DOMAINS): VALUES_SCHEMA,
vol.Optional(ENTITY_ENTITY_IDS): VALUES_SCHEMA,
}))
POLICY_SCHEMA = vol.Schema({
vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA
})
class AbstractPermissions:
"""Default permissions class."""
def check_entity(self, entity_id: str, *keys: str) -> bool:
"""Test if we can access entity."""
raise NotImplementedError
def filter_states(self, states: List[State]) -> List[State]:
"""Filter a list of states for what the user is allowed to see."""
raise NotImplementedError
class PolicyPermissions(AbstractPermissions):
"""Handle permissions."""
def __init__(self, policy: PolicyType) -> None:
"""Initialize the permission class."""
self._policy = policy
self._compiled = {} # type: Dict[str, Callable[..., bool]]
def check_entity(self, entity_id: str, *keys: str) -> bool:
"""Test if we can access entity."""
func = self._policy_func(CAT_ENTITIES, _compile_entities)
return func(entity_id, keys)
def filter_states(self, states: List[State]) -> List[State]:
"""Filter a list of states for what the user is allowed to see."""
func = self._policy_func(CAT_ENTITIES, _compile_entities)
keys = ('read',)
return [entity for entity in states if func(entity.entity_id, keys)]
def _policy_func(self, category: str,
compile_func: Callable[[CategoryType], Callable]) \
-> Callable[..., bool]:
"""Get a policy function."""
func = self._compiled.get(category)
if func:
return func
func = self._compiled[category] = compile_func(
self._policy.get(category))
return func
def __eq__(self, other: Any) -> bool:
"""Equals check."""
# pylint: disable=protected-access
return (isinstance(other, PolicyPermissions) and
other._policy == self._policy)
class _OwnerPermissions(AbstractPermissions):
"""Owner permissions."""
# pylint: disable=no-self-use
def check_entity(self, entity_id: str, *keys: str) -> bool:
"""Test if we can access entity."""
return True
def filter_states(self, states: List[State]) -> List[State]:
"""Filter a list of states for what the user is allowed to see."""
return states
OwnerPermissions = _OwnerPermissions() # pylint: disable=invalid-name
def _compile_entities(policy: CategoryType) \
-> Callable[[str, Tuple[str]], bool]:
"""Compile policy into a function that tests policy."""
# None, Empty Dict, False
if not policy:
def apply_policy_deny_all(entity_id: str, keys: Tuple[str]) -> bool:
"""Decline all."""
return False
return apply_policy_deny_all
if policy is True:
def apply_policy_allow_all(entity_id: str, keys: Tuple[str]) -> bool:
"""Approve all."""
return True
return apply_policy_allow_all
assert isinstance(policy, dict)
domains = policy.get(ENTITY_DOMAINS)
entity_ids = policy.get(ENTITY_ENTITY_IDS)
funcs = [] # type: List[Callable[[str, Tuple[str]], Union[None, bool]]]
# The order of these functions matter. The more precise are at the top.
# If a function returns None, they cannot handle it.
# If a function returns a boolean, that's the result to return.
# Setting entity_ids to a boolean is final decision for permissions
# So return right away.
if isinstance(entity_ids, bool):
def apply_entity_id_policy(entity_id: str, keys: Tuple[str]) -> bool:
"""Test if allowed entity_id."""
return entity_ids # type: ignore
return apply_entity_id_policy
if entity_ids is not None:
def allowed_entity_id(entity_id: str, keys: Tuple[str]) \
-> Union[None, bool]:
"""Test if allowed entity_id."""
return entity_ids.get(entity_id) # type: ignore
funcs.append(allowed_entity_id)
if isinstance(domains, bool):
def allowed_domain(entity_id: str, keys: Tuple[str]) \
-> Union[None, bool]:
"""Test if allowed domain."""
return domains
funcs.append(allowed_domain)
elif domains is not None:
def allowed_domain(entity_id: str, keys: Tuple[str]) \
-> Union[None, bool]:
"""Test if allowed domain."""
domain = entity_id.split(".", 1)[0]
return domains.get(domain) # type: ignore
funcs.append(allowed_domain)
# Can happen if no valid subcategories specified
if not funcs:
def apply_policy_deny_all_2(entity_id: str, keys: Tuple[str]) -> bool:
"""Decline all."""
return False
return apply_policy_deny_all_2
if len(funcs) == 1:
func = funcs[0]
def apply_policy_func(entity_id: str, keys: Tuple[str]) -> bool:
"""Apply a single policy function."""
return func(entity_id, keys) is True
return apply_policy_func
def apply_policy_funcs(entity_id: str, keys: Tuple[str]) -> bool:
"""Apply several policy functions."""
for func in funcs:
result = func(entity_id, keys)
if result is not None:
return result
return False
return apply_policy_funcs
def merge_policies(policies: List[PolicyType]) -> PolicyType:
"""Merge policies."""
new_policy = {} # type: Dict[str, CategoryType]
seen = set() # type: Set[str]
for policy in policies:
for category in policy:
if category in seen:
continue
seen.add(category)
new_policy[category] = _merge_policies([
policy.get(category) for policy in policies])
cast(PolicyType, new_policy)
return new_policy
def _merge_policies(sources: List[CategoryType]) -> CategoryType:
"""Merge a policy."""
# When merging policies, the most permissive wins.
# This means we order it like this:
# True > Dict > None
#
# True: allow everything
# Dict: specify more granular permissions
# None: no opinion
#
# If there are multiple sources with a dict as policy, we recursively
# merge each key in the source.
policy = None # type: CategoryType
seen = set() # type: Set[str]
for source in sources:
if source is None:
continue
# A source that's True will always win. Shortcut return.
if source is True:
return True
assert isinstance(source, dict)
if policy is None:
policy = {}
assert isinstance(policy, dict)
for key in source:
if key in seen:
continue
seen.add(key)
key_sources = []
for src in sources:
if isinstance(src, dict):
key_sources.append(src.get(key))
policy[key] = _merge_policies(key_sources)
return policy

View File

@@ -1,16 +1,22 @@
"""Auth providers for Home Assistant."""
import importlib
import logging
import types
from typing import Any, Dict, List, Optional
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 import data_entry_flow, requirements
from homeassistant.core import callback, HomeAssistant
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import dt as dt_util
from homeassistant.util.decorator import Registry
from homeassistant.auth.models import Credentials
from ..auth_store import AuthStore
from ..const import MFA_SESSION_EXPIRATION
from ..models import Credentials, User, UserMeta # noqa: F401
_LOGGER = logging.getLogger(__name__)
DATA_REQS = 'auth_prov_reqs_processed'
@@ -25,66 +31,20 @@ AUTH_PROVIDER_SCHEMA = vol.Schema({
}, 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):
def __init__(self, hass: HomeAssistant, store: AuthStore,
config: Dict[str, Any]) -> None:
"""Initialize an auth provider."""
self.hass = hass
self.store = store
self.config = config
@property
def id(self): # pylint: disable=invalid-name
def id(self) -> Optional[str]: # pylint: disable=invalid-name
"""Return id of the auth provider.
Optional, can be None.
@@ -92,16 +52,21 @@ class AuthProvider:
return self.config.get(CONF_ID)
@property
def type(self):
def type(self) -> str:
"""Return type of the provider."""
return self.config[CONF_TYPE]
return self.config[CONF_TYPE] # type: ignore
@property
def name(self):
def name(self) -> str:
"""Return the name of the auth provider."""
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
async def async_credentials(self):
@property
def support_mfa(self) -> bool:
"""Return whether multi-factor auth supported by the auth provider."""
return True
async def async_credentials(self) -> List[Credentials]:
"""Return all credentials of this provider."""
users = await self.store.async_get_users()
return [
@@ -113,7 +78,7 @@ class AuthProvider:
]
@callback
def async_create_credentials(self, data):
def async_create_credentials(self, data: Dict[str, str]) -> Credentials:
"""Create credentials."""
return Credentials(
auth_provider_type=self.type,
@@ -123,21 +88,181 @@ class AuthProvider:
# Implement by extending class
async def async_credential_flow(self):
"""Return the data flow for logging in with auth provider."""
async def async_login_flow(self, context: Optional[Dict]) -> 'LoginFlow':
"""Return the data flow for logging in with auth provider.
Auth provider should extend LoginFlow and return an instance.
"""
raise NotImplementedError
async def async_get_or_create_credentials(self, flow_result):
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Get credentials based on the flow result."""
raise NotImplementedError
async def async_user_meta_for_credentials(self, credentials):
async def async_user_meta_for_credentials(
self, credentials: Credentials) -> UserMeta:
"""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 {}
raise NotImplementedError
async def auth_provider_from_config(
hass: HomeAssistant, store: AuthStore,
config: Dict[str, Any]) -> AuthProvider:
"""Initialize an auth provider from a config."""
provider_name = config[CONF_TYPE]
module = await load_auth_provider_module(hass, provider_name)
try:
config = module.CONFIG_SCHEMA(config) # type: ignore
except vol.Invalid as err:
_LOGGER.error('Invalid configuration for auth provider %s: %s',
provider_name, humanize_error(config, err))
raise
return AUTH_PROVIDERS[provider_name](hass, store, config) # type: ignore
async def load_auth_provider_module(
hass: HomeAssistant, provider: str) -> types.ModuleType:
"""Load an auth provider."""
try:
module = importlib.import_module(
'homeassistant.auth.providers.{}'.format(provider))
except ImportError as err:
_LOGGER.error('Unable to load auth provider %s: %s', provider, err)
raise HomeAssistantError('Unable to load auth provider {}: {}'.format(
provider, err))
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
# https://github.com/python/mypy/issues/1424
reqs = module.REQUIREMENTS # type: ignore
req_success = await requirements.async_process_requirements(
hass, 'auth provider {}'.format(provider), reqs)
if not req_success:
raise HomeAssistantError(
'Unable to process requirements of auth provider {}'.format(
provider))
processed.add(provider)
return module
class LoginFlow(data_entry_flow.FlowHandler):
"""Handler for the login flow."""
def __init__(self, auth_provider: AuthProvider) -> None:
"""Initialize the login flow."""
self._auth_provider = auth_provider
self._auth_module_id = None # type: Optional[str]
self._auth_manager = auth_provider.hass.auth # type: ignore
self.available_mfa_modules = {} # type: Dict[str, str]
self.created_at = dt_util.utcnow()
self.invalid_mfa_times = 0
self.user = None # type: Optional[User]
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the first step of login flow.
Return self.async_show_form(step_id='init') if user_input == None.
Return await self.async_finish(flow_result) if login init step pass.
"""
raise NotImplementedError
async def async_step_select_mfa_module(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of select mfa module."""
errors = {}
if user_input is not None:
auth_module = user_input.get('multi_factor_auth_module')
if auth_module in self.available_mfa_modules:
self._auth_module_id = auth_module
return await self.async_step_mfa()
errors['base'] = 'invalid_auth_module'
if len(self.available_mfa_modules) == 1:
self._auth_module_id = list(self.available_mfa_modules.keys())[0]
return await self.async_step_mfa()
return self.async_show_form(
step_id='select_mfa_module',
data_schema=vol.Schema({
'multi_factor_auth_module': vol.In(self.available_mfa_modules)
}),
errors=errors,
)
async def async_step_mfa(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of mfa validation."""
assert self.user
errors = {}
auth_module = self._auth_manager.get_auth_mfa_module(
self._auth_module_id)
if auth_module is None:
# Given an invalid input to async_step_select_mfa_module
# will show invalid_auth_module error
return await self.async_step_select_mfa_module(user_input={})
if user_input is None and hasattr(auth_module,
'async_initialize_login_mfa_step'):
await auth_module.async_initialize_login_mfa_step(self.user.id)
if user_input is not None:
expires = self.created_at + MFA_SESSION_EXPIRATION
if dt_util.utcnow() > expires:
return self.async_abort(
reason='login_expired'
)
result = await auth_module.async_validate(
self.user.id, user_input)
if not result:
errors['base'] = 'invalid_code'
self.invalid_mfa_times += 1
if self.invalid_mfa_times >= auth_module.MAX_RETRY_TIME > 0:
return self.async_abort(
reason='too_many_retry'
)
if not errors:
return await self.async_finish(self.user)
description_placeholders = {
'mfa_module_name': auth_module.name,
'mfa_module_id': auth_module.id,
} # type: Dict[str, Optional[str]]
return self.async_show_form(
step_id='mfa',
data_schema=auth_module.input_schema,
description_placeholders=description_placeholders,
errors=errors,
)
async def async_finish(self, flow_result: Any) -> Dict:
"""Handle the pass of login flow."""
return self.async_create_entry(
title=self._auth_provider.name,
data=flow_result
)

View File

@@ -3,24 +3,27 @@ import base64
from collections import OrderedDict
import hashlib
import hmac
from typing import Dict # noqa: F401 pylint: disable=unused-import
from typing import Any, Dict, List, Optional, cast
import bcrypt
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.const import CONF_ID
from homeassistant.core import callback
from homeassistant.core import callback, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.auth.util import generate_secret
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
from ..models import Credentials, UserMeta
from ..util import generate_secret
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
STORAGE_VERSION = 1
STORAGE_KEY = 'auth_provider.homeassistant'
def _disallow_id(conf):
def _disallow_id(conf: Dict[str, Any]) -> Dict[str, Any]:
"""Disallow ID in config."""
if CONF_ID in conf:
raise vol.Invalid(
@@ -46,13 +49,14 @@ class InvalidUser(HomeAssistantError):
class Data:
"""Hold the user data."""
def __init__(self, hass):
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the user data store."""
self.hass = hass
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
self._data = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY,
private=True)
self._data = None # type: Optional[Dict[str, Any]]
async def async_load(self):
async def async_load(self) -> None:
"""Load stored data."""
data = await self._store.async_load()
@@ -65,37 +69,69 @@ class Data:
self._data = data
@property
def users(self):
def users(self) -> List[Dict[str, str]]:
"""Return users."""
return self._data['users']
return self._data['users'] # type: ignore
def validate_login(self, username: str, password: str) -> None:
"""Validate a username and password.
Raises InvalidAuth if auth invalid.
"""
hashed = self.hash_password(password)
dummy = b'$2b$12$CiuFGszHx9eNHxPuQcwBWez4CwDTOcLTX5CbOpV6gef2nYuXkY7BO'
found = None
# Compare all users to avoid timing attacks.
for user in self._data['users']:
for user in self.users:
if username == user['username']:
found = user
if found is None:
# Do one more compare to make timing the same as if user was found.
hmac.compare_digest(hashed, hashed)
# check a hash to make timing the same as if user was found
bcrypt.checkpw(b'foo',
dummy)
raise InvalidAuth
if not hmac.compare_digest(hashed,
base64.b64decode(found['password'])):
user_hash = base64.b64decode(found['password'])
# if the hash is not a bcrypt hash...
# provide a transparant upgrade for old pbkdf2 hash format
if not (user_hash.startswith(b'$2a$')
or user_hash.startswith(b'$2b$')
or user_hash.startswith(b'$2x$')
or user_hash.startswith(b'$2y$')):
# IMPORTANT! validate the login, bail if invalid
hashed = self.legacy_hash_password(password)
if not hmac.compare_digest(hashed, user_hash):
raise InvalidAuth
# then re-hash the valid password with bcrypt
self.change_password(found['username'], password)
run_coroutine_threadsafe(
self.async_save(), self.hass.loop
).result()
user_hash = base64.b64decode(found['password'])
# bcrypt.checkpw is timing-safe
if not bcrypt.checkpw(password.encode(),
user_hash):
raise InvalidAuth
def legacy_hash_password(self, password: str,
for_storage: bool = False) -> bytes:
"""LEGACY password encoding."""
# We're no longer storing salts in data, but if one exists we
# should be able to retrieve it.
salt = self._data['salt'].encode() # type: ignore
hashed = hashlib.pbkdf2_hmac('sha512', password.encode(), salt, 100000)
if for_storage:
hashed = base64.b64encode(hashed)
return hashed
# pylint: disable=no-self-use
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)
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)) \
# type: bytes
if for_storage:
hashed = base64.b64encode(hashed)
return hashed
@@ -137,7 +173,7 @@ class Data:
else:
raise InvalidUser
async def async_save(self):
async def async_save(self) -> None:
"""Save data."""
await self._store.async_save(self._data)
@@ -150,7 +186,7 @@ class HassAuthProvider(AuthProvider):
data = None
async def async_initialize(self):
async def async_initialize(self) -> None:
"""Initialize the auth provider."""
if self.data is not None:
return
@@ -158,19 +194,22 @@ class HassAuthProvider(AuthProvider):
self.data = Data(self.hass)
await self.data.async_load()
async def async_credential_flow(self):
async def async_login_flow(
self, context: Optional[Dict]) -> LoginFlow:
"""Return a flow to login."""
return LoginFlow(self)
return HassLoginFlow(self)
async def async_validate_login(self, username: str, password: str):
"""Helper to validate a username and password."""
async def async_validate_login(self, username: str, password: str) -> None:
"""Validate a username and password."""
if self.data is None:
await self.async_initialize()
assert self.data is not None
await self.hass.async_add_executor_job(
self.data.validate_login, username, password)
async def async_get_or_create_credentials(self, flow_result):
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Get credentials based on the flow result."""
username = flow_result['username']
@@ -183,17 +222,17 @@ class HassAuthProvider(AuthProvider):
'username': username
})
async def async_user_meta_for_credentials(self, credentials):
async def async_user_meta_for_credentials(
self, credentials: Credentials) -> UserMeta:
"""Get extra info for this credential."""
return {
'name': credentials.data['username'],
'is_active': True,
}
return UserMeta(name=credentials.data['username'], is_active=True)
async def async_will_remove_credentials(self, credentials):
async def async_will_remove_credentials(
self, credentials: Credentials) -> None:
"""When credentials get removed, also remove the auth."""
if self.data is None:
await self.async_initialize()
assert self.data is not None
try:
self.data.async_remove_auth(credentials.data['username'])
@@ -203,29 +242,26 @@ class HassAuthProvider(AuthProvider):
pass
class LoginFlow(data_entry_flow.FlowHandler):
class HassLoginFlow(LoginFlow):
"""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):
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of the form."""
errors = {}
if user_input is not None:
try:
await self._auth_provider.async_validate_login(
user_input['username'], user_input['password'])
await cast(HassAuthProvider, self._auth_provider)\
.async_validate_login(user_input['username'],
user_input['password'])
except InvalidAuth:
errors['base'] = 'invalid_auth'
if not errors:
return self.async_create_entry(
title=self._auth_provider.name,
data=user_input
)
user_input.pop('password')
return await self.async_finish(user_input)
schema = OrderedDict() # type: Dict[str, type]
schema['username'] = str

View File

@@ -1,14 +1,15 @@
"""Example auth provider."""
from collections import OrderedDict
import hmac
from typing import Any, Dict, Optional, cast
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
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
from ..models import Credentials, UserMeta
USER_SCHEMA = vol.Schema({
@@ -31,13 +32,13 @@ class InvalidAuthError(HomeAssistantError):
class ExampleAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
async def async_credential_flow(self):
async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow:
"""Return a flow to login."""
return LoginFlow(self)
return ExampleLoginFlow(self)
@callback
def async_validate_login(self, username, password):
"""Helper to validate a username and password."""
def async_validate_login(self, username: str, password: str) -> None:
"""Validate a username and password."""
user = None
# Compare all users to avoid timing attacks.
@@ -56,7 +57,8 @@ class ExampleAuthProvider(AuthProvider):
password.encode('utf-8')):
raise InvalidAuthError
async def async_get_or_create_credentials(self, flow_result):
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Get credentials based on the flow result."""
username = flow_result['username']
@@ -69,49 +71,45 @@ class ExampleAuthProvider(AuthProvider):
'username': username
})
async def async_user_meta_for_credentials(self, credentials):
async def async_user_meta_for_credentials(
self, credentials: Credentials) -> UserMeta:
"""Return extra user metadata for credentials.
Will be used to populate info when creating a new user.
"""
username = credentials.data['username']
info = {
'is_active': True,
}
name = None
for user in self.config['users']:
if user['username'] == username:
info['name'] = user.get('name')
name = user.get('name')
break
return info
return UserMeta(name=name, is_active=True)
class LoginFlow(data_entry_flow.FlowHandler):
class ExampleLoginFlow(LoginFlow):
"""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):
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of the form."""
errors = {}
if user_input is not None:
try:
self._auth_provider.async_validate_login(
user_input['username'], user_input['password'])
cast(ExampleAuthProvider, self._auth_provider)\
.async_validate_login(user_input['username'],
user_input['password'])
except InvalidAuthError:
errors['base'] = 'invalid_auth'
if not errors:
return self.async_create_entry(
title=self._auth_provider.name,
data=user_input
)
user_input.pop('password')
return await self.async_finish(user_input)
schema = OrderedDict()
schema = OrderedDict() # type: Dict[str, type]
schema['username'] = str
schema['password'] = str

View File

@@ -3,16 +3,17 @@ Support Legacy API password auth provider.
It will be removed when auth system production ready
"""
from collections import OrderedDict
import hmac
from typing import Any, Dict, Optional, cast
import voluptuous as vol
from homeassistant.exceptions import HomeAssistantError
from homeassistant import data_entry_flow
from homeassistant.components.http import HomeAssistantHTTP # noqa: F401
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
from ..models import Credentials, UserMeta
USER_SCHEMA = vol.Schema({
@@ -23,7 +24,7 @@ USER_SCHEMA = vol.Schema({
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
LEGACY_USER = 'homeassistant'
LEGACY_USER_NAME = 'Legacy API password user'
class InvalidAuthError(HomeAssistantError):
@@ -36,75 +37,65 @@ class LegacyApiPasswordAuthProvider(AuthProvider):
DEFAULT_TITLE = 'Legacy API Password'
async def async_credential_flow(self):
async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow:
"""Return a flow to login."""
return LoginFlow(self)
return LegacyLoginFlow(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')
def async_validate_login(self, password: str) -> None:
"""Validate a username and password."""
hass_http = getattr(self.hass, 'http', None) # type: HomeAssistantHTTP
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'),
if not hmac.compare_digest(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
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Return credentials for this login."""
credentials = await self.async_credentials()
if credentials:
return credentials[0]
return self.async_create_credentials({
'username': LEGACY_USER
})
return self.async_create_credentials({})
async def async_user_meta_for_credentials(self, credentials):
async def async_user_meta_for_credentials(
self, credentials: Credentials) -> UserMeta:
"""
Set name as LEGACY_USER always.
Return info for the user.
Will be used to populate info when creating a new user.
"""
return {
'name': LEGACY_USER,
'is_active': True,
}
return UserMeta(name=LEGACY_USER_NAME, is_active=True)
class LoginFlow(data_entry_flow.FlowHandler):
class LegacyLoginFlow(LoginFlow):
"""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):
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of the form."""
errors = {}
hass_http = getattr(self.hass, 'http', None)
if hass_http is None or not hass_http.api_password:
return self.async_abort(
reason='no_api_password_set'
)
if user_input is not None:
try:
self._auth_provider.async_validate_login(
user_input['password'])
cast(LegacyApiPasswordAuthProvider, 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 await self.async_finish({})
return self.async_show_form(
step_id='init',
data_schema=vol.Schema(schema),
data_schema=vol.Schema({'password': str}),
errors=errors,
)

View File

@@ -0,0 +1,129 @@
"""Trusted Networks auth provider.
It shows list of users if access from trusted network.
Abort login flow if not access from trusted network.
"""
from typing import Any, Dict, Optional, cast
import voluptuous as vol
from homeassistant.components.http import HomeAssistantHTTP # noqa: F401
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
from ..models import Credentials, UserMeta
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend({
}, extra=vol.PREVENT_EXTRA)
class InvalidAuthError(HomeAssistantError):
"""Raised when try to access from untrusted networks."""
class InvalidUserError(HomeAssistantError):
"""Raised when try to login as invalid user."""
@AUTH_PROVIDERS.register('trusted_networks')
class TrustedNetworksAuthProvider(AuthProvider):
"""Trusted Networks auth provider.
Allow passwordless access from trusted network.
"""
DEFAULT_TITLE = 'Trusted Networks'
@property
def support_mfa(self) -> bool:
"""Trusted Networks auth provider does not support MFA."""
return False
async def async_login_flow(self, context: Optional[Dict]) -> LoginFlow:
"""Return a flow to login."""
assert context is not None
users = await self.store.async_get_users()
available_users = {user.id: user.name
for user in users
if not user.system_generated and user.is_active}
return TrustedNetworksLoginFlow(
self, cast(str, context.get('ip_address')), available_users)
async def async_get_or_create_credentials(
self, flow_result: Dict[str, str]) -> Credentials:
"""Get credentials based on the flow result."""
user_id = flow_result['user']
users = await self.store.async_get_users()
for user in users:
if (not user.system_generated and
user.is_active and
user.id == user_id):
for credential in await self.async_credentials():
if credential.data['user_id'] == user_id:
return credential
cred = self.async_create_credentials({'user_id': user_id})
await self.store.async_link_user(user, cred)
return cred
# We only allow login as exist user
raise InvalidUserError
async def async_user_meta_for_credentials(
self, credentials: Credentials) -> UserMeta:
"""Return extra user metadata for credentials.
Trusted network auth provider should never create new user.
"""
raise NotImplementedError
@callback
def async_validate_access(self, ip_address: str) -> None:
"""Make sure the access from trusted networks.
Raise InvalidAuthError if not.
Raise InvalidAuthError if trusted_networks is not configured.
"""
hass_http = getattr(self.hass, 'http', None) # type: HomeAssistantHTTP
if not hass_http or not hass_http.trusted_networks:
raise InvalidAuthError('trusted_networks is not configured')
if not any(ip_address in trusted_network for trusted_network
in hass_http.trusted_networks):
raise InvalidAuthError('Not in trusted_networks')
class TrustedNetworksLoginFlow(LoginFlow):
"""Handler for the login flow."""
def __init__(self, auth_provider: TrustedNetworksAuthProvider,
ip_address: str, available_users: Dict[str, Optional[str]]) \
-> None:
"""Initialize the login flow."""
super().__init__(auth_provider)
self._available_users = available_users
self._ip_address = ip_address
async def async_step_init(
self, user_input: Optional[Dict[str, str]] = None) \
-> Dict[str, Any]:
"""Handle the step of the form."""
try:
cast(TrustedNetworksAuthProvider, self._auth_provider)\
.async_validate_access(self._ip_address)
except InvalidAuthError:
return self.async_abort(
reason='not_whitelisted'
)
if user_input is not None:
return await self.async_finish(user_input)
return self.async_show_form(
step_id='init',
data_schema=vol.Schema({'user': vol.In(self._available_users)}),
)

View File

@@ -5,7 +5,6 @@ import os
import sys
from time import time
from collections import OrderedDict
from typing import Any, Optional, Dict
import voluptuous as vol
@@ -19,7 +18,6 @@ from homeassistant.util.logging import AsyncHandler
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
_LOGGER = logging.getLogger(__name__)
@@ -61,7 +59,6 @@ def from_config_dict(config: Dict[str, Any],
config, hass, config_dir, enable_log, verbose, skip_pip,
log_rotate_days, log_file, log_no_color)
)
return hass
@@ -87,11 +84,20 @@ async def async_from_config_dict(config: Dict[str, Any],
log_no_color)
core_config = config.get(core.DOMAIN, {})
has_api_password = bool((config.get('http') or {}).get('api_password'))
has_trusted_networks = bool((config.get('http') or {})
.get('trusted_networks'))
try:
await conf_util.async_process_ha_core_config(hass, core_config)
except vol.Invalid as ex:
conf_util.async_log_exception(ex, 'homeassistant', core_config, hass)
await conf_util.async_process_ha_core_config(
hass, core_config, has_api_password, has_trusted_networks)
except vol.Invalid as config_err:
conf_util.async_log_exception(
config_err, 'homeassistant', core_config, hass)
return None
except HomeAssistantError:
_LOGGER.error("Home Assistant core failed to initialize. "
"Further initialization aborted")
return None
await hass.async_add_executor_job(
@@ -126,7 +132,7 @@ async def async_from_config_dict(config: Dict[str, Any],
res = await core_components.async_setup(hass, config)
if not res:
_LOGGER.error("Home Assistant core failed to initialize. "
"further initialization aborted")
"Further initialization aborted")
return hass
await persistent_notification.async_setup(hass, config)
@@ -152,7 +158,6 @@ async def async_from_config_dict(config: Dict[str, Any],
stop = time()
_LOGGER.info("Home Assistant initialized in %.2fs", stop-start)
async_register_signal_handling(hass)
return hass
@@ -307,7 +312,7 @@ def async_enable_logging(hass: core.HomeAssistant,
hass.data[DATA_LOGGING] = err_log_path
else:
_LOGGER.error(
"Unable to setup error log %s (access denied)", err_log_path)
"Unable to set up error log %s (access denied)", err_log_path)
async def async_mount_local_lib_path(config_dir: str) -> str:

View File

@@ -10,6 +10,9 @@ Component design guidelines:
import asyncio
import itertools as it
import logging
from typing import Awaitable
import voluptuous as vol
import homeassistant.core as ha
import homeassistant.config as conf_util
@@ -20,11 +23,16 @@ from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
RESTART_EXIT_CODE)
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config'
SERVICE_CHECK_CONFIG = 'check_config'
SERVICE_UPDATE_ENTITY = 'update_entity'
SCHEMA_UPDATE_ENTITY = vol.Schema({
ATTR_ENTITY_ID: cv.entity_id
})
def is_on(hass, entity_id=None):
@@ -58,61 +66,9 @@ def is_on(hass, entity_id=None):
return False
def turn_on(hass, entity_id=None, **service_data):
"""Turn specified entity on if possible."""
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(ha.DOMAIN, SERVICE_TURN_ON, service_data)
def turn_off(hass, entity_id=None, **service_data):
"""Turn specified entity off."""
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
def toggle(hass, entity_id=None, **service_data):
"""Toggle specified entity."""
if entity_id is not None:
service_data[ATTR_ENTITY_ID] = entity_id
hass.services.call(ha.DOMAIN, SERVICE_TOGGLE, service_data)
def stop(hass):
"""Stop Home Assistant."""
hass.services.call(ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP)
def restart(hass):
"""Stop Home Assistant."""
hass.services.call(ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART)
def check_config(hass):
"""Check the config files."""
hass.services.call(ha.DOMAIN, SERVICE_CHECK_CONFIG)
def reload_core_config(hass):
"""Reload the core config."""
hass.services.call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG)
@asyncio.coroutine
def async_reload_core_config(hass):
"""Reload the core config."""
yield from hass.services.async_call(ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]:
"""Set up general services related to Home Assistant."""
@asyncio.coroutine
def async_handle_turn_service(service):
async def async_handle_turn_service(service):
"""Handle calls to homeassistant.turn_on/off."""
entity_ids = extract_entity_ids(hass, service)
@@ -147,7 +103,7 @@ def async_setup(hass, config):
tasks.append(hass.services.async_call(
domain, service.service, data, blocking))
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
@@ -163,15 +119,14 @@ def async_setup(hass, config):
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
intent.INTENT_TOGGLE, ha.DOMAIN, SERVICE_TOGGLE, "Toggled {}"))
@asyncio.coroutine
def async_handle_core_service(call):
async def async_handle_core_service(call):
"""Service handler for handling core services."""
if call.service == SERVICE_HOMEASSISTANT_STOP:
hass.async_create_task(hass.async_stop())
return
try:
errors = yield from conf_util.async_check_ha_config_file(hass)
errors = await conf_util.async_check_ha_config_file(hass)
except HomeAssistantError:
return
@@ -185,23 +140,30 @@ def async_setup(hass, config):
if call.service == SERVICE_HOMEASSISTANT_RESTART:
hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE))
async def async_handle_update_service(call):
"""Service handler for updating an entity."""
await hass.helpers.entity_component.async_update_entity(
call.data[ATTR_ENTITY_ID])
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_UPDATE_ENTITY, async_handle_update_service,
schema=SCHEMA_UPDATE_ENTITY)
@asyncio.coroutine
def async_handle_reload_config(call):
async def async_handle_reload_config(call):
"""Service handler for reloading core config."""
try:
conf = yield from conf_util.async_hass_config_yaml(hass)
conf = await conf_util.async_hass_config_yaml(hass)
except HomeAssistantError as err:
_LOGGER.error(err)
return
yield from conf_util.async_process_ha_core_config(
await conf_util.async_process_ha_core_config(
hass, conf.get(ha.DOMAIN) or {})
hass.services.async_register(

View File

@@ -4,7 +4,6 @@ This component provides basic support for Abode Home Security system.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/abode/
"""
import asyncio
import logging
from functools import partial
from requests.exceptions import HTTPError, ConnectTimeout
@@ -19,7 +18,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.entity import Entity
REQUIREMENTS = ['abodepy==0.13.1']
REQUIREMENTS = ['abodepy==0.14.0']
_LOGGER = logging.getLogger(__name__)
@@ -261,8 +260,7 @@ class AbodeDevice(Entity):
self._data = data
self._device = device
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Subscribe Abode events."""
self.hass.async_add_job(
self._data.abode.events.add_device_callback,
@@ -308,8 +306,7 @@ class AbodeAutomation(Entity):
self._automation = automation
self._event = event
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Subscribe Abode events."""
if self._event:
self.hass.async_add_job(

View File

@@ -4,7 +4,6 @@ Component to interface with an alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel/
"""
import asyncio
from datetime import timedelta
import logging
@@ -14,7 +13,6 @@ from homeassistant.const import (
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_CUSTOM_BYPASS)
from homeassistant.loader import bind_hass
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
@@ -26,136 +24,49 @@ ATTR_CHANGED_BY = 'changed_by'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
SERVICE_TO_METHOD = {
SERVICE_ALARM_DISARM: 'alarm_disarm',
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
SERVICE_ALARM_ARM_NIGHT: 'alarm_arm_night',
SERVICE_ALARM_ARM_CUSTOM_BYPASS: 'alarm_arm_custom_bypass',
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
}
ATTR_TO_PROPERTY = [
ATTR_CODE,
ATTR_CODE_FORMAT
]
ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_CODE): cv.string,
})
@bind_hass
def alarm_disarm(hass, code=None, entity_id=None):
"""Send the alarm the command for disarm."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
@bind_hass
def alarm_arm_home(hass, code=None, entity_id=None):
"""Send the alarm the command for arm home."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
@bind_hass
def alarm_arm_away(hass, code=None, entity_id=None):
"""Send the alarm the command for arm away."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
@bind_hass
def alarm_arm_night(hass, code=None, entity_id=None):
"""Send the alarm the command for arm night."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_NIGHT, data)
@bind_hass
def alarm_trigger(hass, code=None, entity_id=None):
"""Send the alarm the command for trigger."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
@bind_hass
def alarm_arm_custom_bypass(hass, code=None, entity_id=None):
"""Send the alarm the command for arm custom bypass."""
data = {}
if code:
data[ATTR_CODE] = code
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_CUSTOM_BYPASS, data)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Track states and offer events for sensors."""
component = hass.data[DOMAIN] = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
yield from component.async_setup(config)
await component.async_setup(config)
@asyncio.coroutine
def async_alarm_service_handler(service):
"""Map services to methods on Alarm."""
target_alarms = component.async_extract_from_service(service)
code = service.data.get(ATTR_CODE)
method = "async_{}".format(SERVICE_TO_METHOD[service.service])
update_tasks = []
for alarm in target_alarms:
yield from getattr(alarm, method)(code)
if not alarm.should_poll:
continue
update_tasks.append(alarm.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
for service in SERVICE_TO_METHOD:
hass.services.async_register(
DOMAIN, service, async_alarm_service_handler,
schema=ALARM_SERVICE_SCHEMA)
component.async_register_entity_service(
SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA,
'async_alarm_disarm'
)
component.async_register_entity_service(
SERVICE_ALARM_ARM_HOME, ALARM_SERVICE_SCHEMA,
'async_alarm_arm_home'
)
component.async_register_entity_service(
SERVICE_ALARM_ARM_AWAY, ALARM_SERVICE_SCHEMA,
'async_alarm_arm_away'
)
component.async_register_entity_service(
SERVICE_ALARM_ARM_NIGHT, ALARM_SERVICE_SCHEMA,
'async_alarm_arm_night'
)
component.async_register_entity_service(
SERVICE_ALARM_ARM_CUSTOM_BYPASS, ALARM_SERVICE_SCHEMA,
'async_alarm_arm_custom_bypass'
)
component.async_register_entity_service(
SERVICE_ALARM_TRIGGER, ALARM_SERVICE_SCHEMA,
'async_alarm_trigger'
)
return True
async def async_setup_entry(hass, entry):
"""Setup a config entry."""
"""Set up a config entry."""
return await hass.data[DOMAIN].async_setup_entry(entry)

View File

@@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
ICON = 'mdi:security'
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up an alarm control panel for an Abode device."""
data = hass.data[ABODE_DOMAIN]
@@ -28,7 +28,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
data.devices.extend(alarm_devices)
add_devices(alarm_devices)
add_entities(alarm_devices)
class AbodeAlarm(AbodeDevice, AlarmControlPanel):

View File

@@ -4,7 +4,6 @@ Support for AlarmDecoder-based alarm control panels (Honeywell/DSC).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.alarmdecoder/
"""
import asyncio
import logging
import voluptuous as vol
@@ -26,10 +25,10 @@ ALARM_TOGGLE_CHIME_SCHEMA = vol.Schema({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up for AlarmDecoder alarm panels."""
device = AlarmDecoderAlarmPanel()
add_devices([device])
add_entities([device])
def alarm_toggle_chime_handler(service):
"""Register toggle chime handler."""
@@ -59,8 +58,7 @@ class AlarmDecoderAlarmPanel(alarm.AlarmControlPanel):
self._ready = None
self._zone_bypassed = None
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Register callbacks."""
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_PANEL_MESSAGE, self._message_callback)

View File

@@ -4,7 +4,6 @@ Interfaces with Alarm.com alarm control panels.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.alarmdotcom/
"""
import asyncio
import logging
import re
@@ -32,8 +31,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up a Alarm.com control panel."""
name = config.get(CONF_NAME)
code = config.get(CONF_CODE)
@@ -41,8 +40,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
password = config.get(CONF_PASSWORD)
alarmdotcom = AlarmDotCom(hass, name, code, username, password)
yield from alarmdotcom.async_login()
async_add_devices([alarmdotcom])
await alarmdotcom.async_login()
async_add_entities([alarmdotcom])
class AlarmDotCom(alarm.AlarmControlPanel):
@@ -62,15 +61,13 @@ class AlarmDotCom(alarm.AlarmControlPanel):
self._alarm = Alarmdotcom(
username, password, self._websession, hass.loop)
@asyncio.coroutine
def async_login(self):
async def async_login(self):
"""Login to Alarm.com."""
yield from self._alarm.async_login()
await self._alarm.async_login()
@asyncio.coroutine
def async_update(self):
async def async_update(self):
"""Fetch the latest state."""
yield from self._alarm.async_update()
await self._alarm.async_update()
return self._alarm.state
@property
@@ -105,23 +102,20 @@ class AlarmDotCom(alarm.AlarmControlPanel):
'sensor_status': self._alarm.sensor_status
}
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if self._validate_code(code):
yield from self._alarm.async_alarm_disarm()
await self._alarm.async_alarm_disarm()
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code=None):
"""Send arm hom command."""
if self._validate_code(code):
yield from self._alarm.async_alarm_arm_home()
await self._alarm.async_alarm_arm_home()
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if self._validate_code(code):
yield from self._alarm.async_alarm_arm_away()
await self._alarm.async_alarm_arm_away()
def _validate_code(self, code):
"""Validate given code."""

View File

@@ -38,7 +38,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Arlo Alarm Control Panels."""
arlo = hass.data[DATA_ARLO]
@@ -51,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for base_station in arlo.base_stations:
base_stations.append(ArloBaseStation(base_station, home_mode_name,
away_mode_name))
add_devices(base_stations, True)
add_entities(base_stations, True)
class ArloBaseStation(AlarmControlPanel):

View File

@@ -0,0 +1,92 @@
"""
Support for Blink Alarm Control Panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.blink/
"""
import logging
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.blink import (
BLINK_DATA, DEFAULT_ATTRIBUTION)
from homeassistant.const import (
ATTR_ATTRIBUTION, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['blink']
ICON = 'mdi:security'
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Arlo Alarm Control Panels."""
if discovery_info is None:
return
data = hass.data[BLINK_DATA]
# Current version of blinkpy API only supports one sync module. When
# support for additional models is added, the sync module name should
# come from the API.
sync_modules = []
sync_modules.append(BlinkSyncModule(data, 'sync'))
add_entities(sync_modules, True)
class BlinkSyncModule(AlarmControlPanel):
"""Representation of a Blink Alarm Control Panel."""
def __init__(self, data, name):
"""Initialize the alarm control panel."""
self.data = data
self.sync = data.sync
self._name = name
self._state = None
@property
def unique_id(self):
"""Return the unique id for the sync module."""
return self.sync.serial
@property
def icon(self):
"""Return icon."""
return ICON
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def name(self):
"""Return the name of the panel."""
return "{} {}".format(BLINK_DATA, self._name)
@property
def device_state_attributes(self):
"""Return the state attributes."""
attr = self.sync.attributes
attr['network_info'] = self.data.networks
attr[ATTR_ATTRIBUTION] = DEFAULT_ATTRIBUTION
return attr
def update(self):
"""Update the state of the device."""
_LOGGER.debug("Updating Blink Alarm Control Panel %s", self._name)
self.data.refresh()
mode = self.sync.arm
if mode:
self._state = STATE_ALARM_ARMED_AWAY
else:
self._state = STATE_ALARM_DISARMED
def alarm_disarm(self, code=None):
"""Send disarm command."""
self.sync.arm = False
self.sync.refresh()
def alarm_arm_away(self, code=None):
"""Send arm command."""
self.sync.arm = True
self.sync.refresh()

View File

@@ -16,7 +16,7 @@ DEPENDENCIES = ['canary']
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Canary alarms."""
data = hass.data[DATA_CANARY]
devices = []
@@ -24,7 +24,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
for location in data.locations:
devices.append(CanaryAlarm(data, location.location_id))
add_devices(devices, True)
add_entities(devices, True)
class CanaryAlarm(AlarmControlPanel):

View File

@@ -35,7 +35,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Concord232 alarm control panel platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
@@ -44,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
url = 'http://{}:{}'.format(host, port)
try:
add_devices([Concord232Alarm(hass, url, name)])
add_entities([Concord232Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error("Unable to connect to Concord232: %s", str(ex))
return

View File

@@ -13,9 +13,9 @@ from homeassistant.const import (
CONF_PENDING_TIME, CONF_TRIGGER_TIME)
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Demo alarm control panel platform."""
add_devices([
add_entities([
manual.ManualAlarm(hass, 'Alarm', '1234', None, False, {
STATE_ALARM_ARMED_AWAY: {
CONF_DELAY_TIME: datetime.timedelta(seconds=0),

View File

@@ -4,7 +4,6 @@ Interfaces with Egardia/Woonveilig alarm control panel.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.egardia/
"""
import asyncio
import logging
import requests
@@ -34,7 +33,7 @@ STATES = {
}
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Egardia platform."""
if discovery_info is None:
return
@@ -45,7 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
discovery_info.get(CONF_REPORT_SERVER_CODES),
discovery_info[CONF_REPORT_SERVER_PORT])
# add egardia alarm device
add_devices([device], True)
add_entities([device], True)
class EgardiaAlarm(alarm.AlarmControlPanel):
@@ -61,8 +60,7 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
self._rs_codes = rs_codes
self._rs_port = rs_port
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Add Egardiaserver callback if enabled."""
if self._rs_enabled:
_LOGGER.debug("Registering callback to Egardiaserver")

View File

@@ -0,0 +1,204 @@
"""
Each ElkM1 area will be created as a separate alarm_control_panel in HASS.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.elkm1/
"""
import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.const import (
ATTR_CODE, ATTR_ENTITY_ID, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMING, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
from homeassistant.components.elkm1 import (
DOMAIN as ELK_DOMAIN, create_elk_entities, ElkEntity)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_send)
DEPENDENCIES = [ELK_DOMAIN]
SIGNAL_ARM_ENTITY = 'elkm1_arm'
SIGNAL_DISPLAY_MESSAGE = 'elkm1_display_message'
ELK_ALARM_SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID, default=[]): cv.entity_ids,
vol.Required(ATTR_CODE): vol.All(vol.Coerce(int), vol.Range(0, 999999)),
})
DISPLAY_MESSAGE_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID, default=[]): cv.entity_ids,
vol.Optional('clear', default=2): vol.In([0, 1, 2]),
vol.Optional('beep', default=False): cv.boolean,
vol.Optional('timeout', default=0): vol.Range(min=0, max=65535),
vol.Optional('line1', default=''): cv.string,
vol.Optional('line2', default=''): cv.string,
})
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up the ElkM1 alarm platform."""
if discovery_info is None:
return
elk = hass.data[ELK_DOMAIN]['elk']
entities = create_elk_entities(hass, elk.areas, 'area', ElkArea, [])
async_add_entities(entities, True)
def _dispatch(signal, entity_ids, *args):
for entity_id in entity_ids:
async_dispatcher_send(
hass, '{}_{}'.format(signal, entity_id), *args)
def _arm_service(service):
entity_ids = service.data.get(ATTR_ENTITY_ID, [])
arm_level = _arm_services().get(service.service)
args = (arm_level, service.data.get(ATTR_CODE))
_dispatch(SIGNAL_ARM_ENTITY, entity_ids, *args)
for service in _arm_services():
hass.services.async_register(
alarm.DOMAIN, service, _arm_service, ELK_ALARM_SERVICE_SCHEMA)
def _display_message_service(service):
entity_ids = service.data.get(ATTR_ENTITY_ID, [])
data = service.data
args = (data['clear'], data['beep'], data['timeout'],
data['line1'], data['line2'])
_dispatch(SIGNAL_DISPLAY_MESSAGE, entity_ids, *args)
hass.services.async_register(
alarm.DOMAIN, 'elkm1_alarm_display_message',
_display_message_service, DISPLAY_MESSAGE_SERVICE_SCHEMA)
def _arm_services():
from elkm1_lib.const import ArmLevel
return {
'elkm1_alarm_arm_vacation': ArmLevel.ARMED_VACATION.value,
'elkm1_alarm_arm_home_instant': ArmLevel.ARMED_STAY_INSTANT.value,
'elkm1_alarm_arm_night_instant': ArmLevel.ARMED_NIGHT_INSTANT.value,
}
class ElkArea(ElkEntity, alarm.AlarmControlPanel):
"""Representation of an Area / Partition within the ElkM1 alarm panel."""
def __init__(self, element, elk, elk_data):
"""Initialize Area as Alarm Control Panel."""
super().__init__(element, elk, elk_data)
self._changed_by_entity_id = ''
self._state = None
async def async_added_to_hass(self):
"""Register callback for ElkM1 changes."""
await super().async_added_to_hass()
for keypad in self._elk.keypads:
keypad.add_callback(self._watch_keypad)
async_dispatcher_connect(
self.hass, '{}_{}'.format(SIGNAL_ARM_ENTITY, self.entity_id),
self._arm_service)
async_dispatcher_connect(
self.hass, '{}_{}'.format(SIGNAL_DISPLAY_MESSAGE, self.entity_id),
self._display_message)
def _watch_keypad(self, keypad, changeset):
if keypad.area != self._element.index:
return
if changeset.get('last_user') is not None:
self._changed_by_entity_id = self.hass.data[
ELK_DOMAIN]['keypads'].get(keypad.index, '')
self.async_schedule_update_ha_state(True)
@property
def code_format(self):
"""Return the alarm code format."""
return '^[0-9]{4}([0-9]{2})?$'
@property
def state(self):
"""Return the state of the element."""
return self._state
@property
def device_state_attributes(self):
"""Attributes of the area."""
from elkm1_lib.const import AlarmState, ArmedStatus, ArmUpState
attrs = self.initial_attrs()
elmt = self._element
attrs['is_exit'] = elmt.is_exit
attrs['timer1'] = elmt.timer1
attrs['timer2'] = elmt.timer2
if elmt.armed_status is not None:
attrs['armed_status'] = \
ArmedStatus(elmt.armed_status).name.lower()
if elmt.arm_up_state is not None:
attrs['arm_up_state'] = ArmUpState(elmt.arm_up_state).name.lower()
if elmt.alarm_state is not None:
attrs['alarm_state'] = AlarmState(elmt.alarm_state).name.lower()
attrs['changed_by_entity_id'] = self._changed_by_entity_id
return attrs
def _element_changed(self, element, changeset):
from elkm1_lib.const import ArmedStatus
elk_state_to_hass_state = {
ArmedStatus.DISARMED.value: STATE_ALARM_DISARMED,
ArmedStatus.ARMED_AWAY.value: STATE_ALARM_ARMED_AWAY,
ArmedStatus.ARMED_STAY.value: STATE_ALARM_ARMED_HOME,
ArmedStatus.ARMED_STAY_INSTANT.value: STATE_ALARM_ARMED_HOME,
ArmedStatus.ARMED_TO_NIGHT.value: STATE_ALARM_ARMED_NIGHT,
ArmedStatus.ARMED_TO_NIGHT_INSTANT.value: STATE_ALARM_ARMED_NIGHT,
ArmedStatus.ARMED_TO_VACATION.value: STATE_ALARM_ARMED_AWAY,
}
if self._element.alarm_state is None:
self._state = None
elif self._area_is_in_alarm_state():
self._state = STATE_ALARM_TRIGGERED
elif self._entry_exit_timer_is_running():
self._state = STATE_ALARM_ARMING \
if self._element.is_exit else STATE_ALARM_PENDING
else:
self._state = elk_state_to_hass_state[self._element.armed_status]
def _entry_exit_timer_is_running(self):
return self._element.timer1 > 0 or self._element.timer2 > 0
def _area_is_in_alarm_state(self):
from elkm1_lib.const import AlarmState
return self._element.alarm_state >= AlarmState.FIRE_ALARM.value
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
self._element.disarm(int(code))
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
from elkm1_lib.const import ArmLevel
self._element.arm(ArmLevel.ARMED_STAY.value, int(code))
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
from elkm1_lib.const import ArmLevel
self._element.arm(ArmLevel.ARMED_AWAY.value, int(code))
async def async_alarm_arm_night(self, code=None):
"""Send arm night command."""
from elkm1_lib.const import ArmLevel
self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code))
async def _arm_service(self, arm_level, code):
self._element.arm(arm_level, code)
async def _display_message(self, clear, beep, timeout, line1, line2):
"""Display a message on all keypads for the area."""
self._element.display_message(clear, beep, timeout, line1, line2)

View File

@@ -4,7 +4,6 @@ Support for Envisalink-based alarm control panels (Honeywell/DSC).
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.envisalink/
"""
import asyncio
import logging
import voluptuous as vol
@@ -32,8 +31,8 @@ ALARM_KEYPRESS_SCHEMA = vol.Schema({
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Perform the setup for Envisalink alarm panels."""
configured_partitions = discovery_info['partitions']
code = discovery_info[CONF_CODE]
@@ -53,7 +52,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
)
devices.append(device)
async_add_devices(devices)
async_add_entities(devices)
@callback
def alarm_keypress_handler(service):
@@ -87,8 +86,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
_LOGGER.debug("Setting up alarm: %s", alarm_name)
super().__init__(alarm_name, info, controller)
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_KEYPAD_UPDATE, self._update_callback)
@@ -127,8 +125,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
state = STATE_ALARM_DISARMED
return state
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if code:
self.hass.data[DATA_EVL].disarm_partition(
@@ -137,8 +134,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
self.hass.data[DATA_EVL].disarm_partition(
str(self._code), self._partition_number)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if code:
self.hass.data[DATA_EVL].arm_stay_partition(
@@ -147,8 +143,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
self.hass.data[DATA_EVL].arm_stay_partition(
str(self._code), self._partition_number)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if code:
self.hass.data[DATA_EVL].arm_away_partition(
@@ -157,8 +152,7 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel):
self.hass.data[DATA_EVL].arm_away_partition(
str(self._code), self._partition_number)
@asyncio.coroutine
def async_alarm_trigger(self, code=None):
async def async_alarm_trigger(self, code=None):
"""Alarm trigger command. Will be used to trigger a panic alarm."""
self.hass.data[DATA_EVL].panic_alarm(self._panic_type)

View File

@@ -1,36 +1,35 @@
"""
Support for HomematicIP alarm control panel.
Support for HomematicIP Cloud alarm control panel.
For more details about this component, please refer to the documentation at
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.homematicip_cloud/
"""
import logging
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.homematicip_cloud import (
HMIPC_HAPID, HomematicipGenericDevice)
from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN
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__)
DEPENDENCIES = ['homematicip_cloud']
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."""
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up the HomematicIP Cloud alarm control devices."""
pass
async def async_setup_entry(hass, config_entry, async_add_devices):
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the HomematicIP alarm control panel from a config entry."""
from homematicip.aio.group import AsyncSecurityZoneGroup
@@ -41,11 +40,11 @@ async def async_setup_entry(hass, config_entry, async_add_devices):
devices.append(HomematicipSecurityZone(home, group))
if devices:
async_add_devices(devices)
async_add_entities(devices)
class HomematicipSecurityZone(HomematicipGenericDevice, AlarmControlPanel):
"""Representation of an HomematicIP security zone group."""
"""Representation of an HomematicIP Cloud security zone group."""
def __init__(self, home, device):
"""Initialize the security zone group."""

View File

@@ -40,7 +40,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up an iAlarm control panel."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
@@ -49,7 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
url = 'http://{}'.format(host)
ialarm = IAlarmPanel(name, username, password, url)
add_devices([ialarm], True)
add_entities([ialarm], True)
class IAlarmPanel(alarm.AlarmControlPanel):

View File

@@ -59,7 +59,7 @@ PUSH_ALARM_STATE_SERVICE_SCHEMA = vol.Schema({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up a control panel managed through IFTTT."""
if DATA_IFTTT_ALARM not in hass.data:
hass.data[DATA_IFTTT_ALARM] = []
@@ -75,7 +75,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
alarmpanel = IFTTTAlarmPanel(name, code, event_away, event_home,
event_night, event_disarm, optimistic)
hass.data[DATA_IFTTT_ALARM].append(alarmpanel)
add_devices([alarmpanel])
add_entities([alarmpanel])
async def push_state_update(service):
"""Set the service state as device state attribute."""

View File

@@ -103,9 +103,9 @@ PLATFORM_SCHEMA = vol.Schema(vol.All({
}, _state_validator))
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the manual alarm platform."""
add_devices([ManualAlarm(
add_entities([ManualAlarm(
hass,
config[CONF_NAME],
config.get(CONF_CODE),

View File

@@ -4,7 +4,6 @@ Support for manual alarms controllable via MQTT.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.manual_mqtt/
"""
import asyncio
import copy
import datetime
import logging
@@ -123,9 +122,9 @@ PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
}), _state_validator))
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the manual MQTT alarm platform."""
add_devices([ManualMQTTAlarm(
add_entities([ManualMQTTAlarm(
hass,
config[CONF_NAME],
config.get(CONF_CODE),
@@ -363,8 +362,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
return mqtt.async_subscribe(
self.hass, self._command_topic, message_received, self._qos)
@asyncio.coroutine
def _async_state_changed_listener(self, entity_id, old_state, new_state):
async def _async_state_changed_listener(self, entity_id, old_state,
new_state):
"""Publish state change to MQTT."""
mqtt.async_publish(
self.hass, self._state_topic, new_state.state, self._qos, True)

View File

@@ -4,7 +4,6 @@ This platform enables the possibility to control a MQTT alarm.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.mqtt/
"""
import asyncio
import logging
import re
@@ -18,10 +17,13 @@ from homeassistant.const import (
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN,
CONF_NAME, CONF_CODE)
from homeassistant.components.mqtt import (
CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_QOS,
CONF_RETAIN, MqttAvailability)
ATTR_DISCOVERY_HASH, CONF_AVAILABILITY_TOPIC, CONF_STATE_TOPIC,
CONF_COMMAND_TOPIC, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE,
CONF_QOS, CONF_RETAIN, MqttAvailability, MqttDiscoveryUpdate)
from homeassistant.components.mqtt.discovery import MQTT_DISCOVERY_NEW
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import HomeAssistantType, ConfigType
_LOGGER = logging.getLogger(__name__)
@@ -46,13 +48,29 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
}).extend(mqtt.MQTT_AVAILABILITY_SCHEMA.schema)
@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 def async_setup_platform(hass: HomeAssistantType, config: ConfigType,
async_add_entities, discovery_info=None):
"""Set up MQTT alarm control panel through configuration.yaml."""
await _async_setup_entity(hass, config, async_add_entities)
async_add_devices([MqttAlarm(
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up MQTT alarm control panel dynamically through MQTT discovery."""
async def async_discover(discovery_payload):
"""Discover and add an MQTT alarm control panel."""
config = PLATFORM_SCHEMA(discovery_payload)
await _async_setup_entity(hass, config, async_add_entities,
discovery_payload[ATTR_DISCOVERY_HASH])
async_dispatcher_connect(
hass, MQTT_DISCOVERY_NEW.format(alarm.DOMAIN, 'mqtt'),
async_discover)
async def _async_setup_entity(hass, config, async_add_entities,
discovery_hash=None):
"""Set up the MQTT Alarm Control Panel platform."""
async_add_entities([MqttAlarm(
config.get(CONF_NAME),
config.get(CONF_STATE_TOPIC),
config.get(CONF_COMMAND_TOPIC),
@@ -64,18 +82,22 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
config.get(CONF_CODE),
config.get(CONF_AVAILABILITY_TOPIC),
config.get(CONF_PAYLOAD_AVAILABLE),
config.get(CONF_PAYLOAD_NOT_AVAILABLE))])
config.get(CONF_PAYLOAD_NOT_AVAILABLE),
discovery_hash,)])
class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
class MqttAlarm(MqttAvailability, MqttDiscoveryUpdate,
alarm.AlarmControlPanel):
"""Representation of a MQTT alarm status."""
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):
availability_topic, payload_available, payload_not_available,
discovery_hash):
"""Init the MQTT Alarm Control Panel."""
super().__init__(availability_topic, qos, payload_available,
payload_not_available)
MqttAvailability.__init__(self, availability_topic, qos,
payload_available, payload_not_available)
MqttDiscoveryUpdate.__init__(self, discovery_hash)
self._state = STATE_UNKNOWN
self._name = name
self._state_topic = state_topic
@@ -86,11 +108,12 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away
self._code = code
self._discovery_hash = discovery_hash
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Subscribe mqtt events."""
yield from super().async_added_to_hass()
await MqttAvailability.async_added_to_hass(self)
await MqttDiscoveryUpdate.async_added_to_hass(self)
@callback
def message_received(topic, payload, qos):
@@ -103,7 +126,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
self._state = payload
self.async_schedule_update_ha_state()
yield from mqtt.async_subscribe(
await mqtt.async_subscribe(
self.hass, self._state_topic, message_received, self._qos)
@property
@@ -130,8 +153,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
return 'Number'
return 'Any'
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command.
This method is a coroutine.
@@ -142,8 +164,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
self.hass, self._command_topic, self._payload_disarm, self._qos,
self._retain)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code=None):
"""Send arm home command.
This method is a coroutine.
@@ -154,8 +175,7 @@ class MqttAlarm(MqttAvailability, alarm.AlarmControlPanel):
self.hass, self._command_topic, self._payload_arm_home, self._qos,
self._retain)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code=None):
"""Send arm away command.
This method is a coroutine.

View File

@@ -31,7 +31,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the NX584 platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
@@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
url = 'http://{}:{}'.format(host, port)
try:
add_devices([NX584Alarm(hass, url, name)])
add_entities([NX584Alarm(hass, url, name)])
except requests.exceptions.ConnectionError as ex:
_LOGGER.error("Unable to connect to NX584: %s", str(ex))
return False

View File

@@ -4,7 +4,6 @@ Support for Satel Integra alarm, using ETHM module: https://www.satel.pl/en/ .
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.satel_integra/
"""
import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
@@ -18,15 +17,15 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['satel_integra']
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up for Satel Integra alarm panels."""
if not discovery_info:
return
device = SatelIntegraAlarmPanel(
"Alarm Panel", discovery_info.get(CONF_ARM_HOME_MODE))
async_add_devices([device])
async_add_entities([device])
class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
@@ -38,8 +37,7 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
self._state = None
self._arm_home_mode = arm_home_mode
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback)
@@ -73,21 +71,18 @@ class SatelIntegraAlarmPanel(alarm.AlarmControlPanel):
"""Return the state of the device."""
return self._state
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if code:
yield from self.hass.data[DATA_SATEL].disarm(code)
await self.hass.data[DATA_SATEL].disarm(code)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if code:
yield from self.hass.data[DATA_SATEL].arm(code)
await self.hass.data[DATA_SATEL].arm(code)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if code:
yield from self.hass.data[DATA_SATEL].arm(
await self.hass.data[DATA_SATEL].arm(
code, self._arm_home_mode)

View File

@@ -79,3 +79,55 @@ ifttt_push_alarm_state:
state:
description: The state to which the alarm control panel has to be set.
example: 'armed_night'
elkm1_alarm_arm_vacation:
description: Arm the ElkM1 in vacation mode.
fields:
entity_id:
description: Name of alarm control panel to arm.
example: 'alarm_control_panel.main'
code:
description: An code to arm the alarm control panel.
example: 1234
elkm1_alarm_arm_home_instant:
description: Arm the ElkM1 in home instant mode.
fields:
entity_id:
description: Name of alarm control panel to arm.
example: 'alarm_control_panel.main'
code:
description: An code to arm the alarm control panel.
example: 1234
elkm1_alarm_arm_night_instant:
description: Arm the ElkM1 in night instant mode.
fields:
entity_id:
description: Name of alarm control panel to arm.
example: 'alarm_control_panel.main'
code:
description: An code to arm the alarm control panel.
example: 1234
elkm1_alarm_display_message:
description: Display a message on all of the ElkM1 keypads for an area.
fields:
entity_id:
description: Name of alarm control panel to display messages on.
example: 'alarm_control_panel.main'
clear:
description: 0=clear message, 1=clear message with * key, 2=Display until timeout; default 2
example: 1
beep:
description: 0=no beep, 1=beep; default 0
example: 1
timeout:
description: Time to display message, 0=forever, max 65535, default 0
example: 4242
line1:
description: Up to 16 characters of text (truncated if too long). Default blank.
example: The answer to life,
line2:
description: Up to 16 characters of text (truncated if too long). Default blank.
example: the universe, and everything.

View File

@@ -1,5 +1,5 @@
"""
Interfaces with SimpliSafe alarm control panel.
This platform provides alarm control functionality for SimpliSafe.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.simplisafe/
@@ -7,80 +7,61 @@ https://home-assistant.io/components/alarm_control_panel.simplisafe/
import logging
import re
import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
PLATFORM_SCHEMA, AlarmControlPanel)
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.simplisafe.const import (
DATA_CLIENT, DOMAIN, TOPIC_UPDATE)
from homeassistant.const import (
CONF_CODE, CONF_NAME, CONF_PASSWORD, CONF_USERNAME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['simplisafe-python==2.0.2']
CONF_CODE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'SimpliSafe'
ATTR_ALARM_ACTIVE = "alarm_active"
ATTR_TEMPERATURE = "temperature"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_CODE): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
ATTR_ALARM_ACTIVE = 'alarm_active'
ATTR_TEMPERATURE = 'temperature'
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the SimpliSafe platform."""
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)
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up a SimpliSafe alarm control panel based on existing config."""
pass
try:
simplisafe = SimpliSafeApiInterface(username, password)
except SimpliSafeAPIException:
_LOGGER.error("Failed to setup SimpliSafe")
return
systems = []
for system in simplisafe.get_systems():
systems.append(SimpliSafeAlarm(system, name, code))
add_devices(systems)
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up a SimpliSafe alarm control panel based on a config entry."""
systems = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
async_add_entities([
SimpliSafeAlarm(system, entry.data.get(CONF_CODE))
for system in systems
], True)
class SimpliSafeAlarm(AlarmControlPanel):
"""Representation of a SimpliSafe alarm."""
def __init__(self, simplisafe, name, code):
def __init__(self, system, code):
"""Initialize the SimpliSafe alarm."""
self.simplisafe = simplisafe
self._name = name
self._code = str(code) if code else None
self._async_unsub_dispatcher_connect = None
self._attrs = {}
self._code = code
self._system = system
self._state = None
@property
def unique_id(self):
"""Return the unique ID."""
return self.simplisafe.location_id
return self._system.system_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 self._system.address
@property
def code_format(self):
"""Return one or more digits/characters."""
if self._code is None:
if not self._code:
return None
if isinstance(self._code, str) and re.search('^\\d+$', self._code):
return 'Number'
@@ -89,53 +70,12 @@ class SimpliSafeAlarm(AlarmControlPanel):
@property
def state(self):
"""Return the state of the device."""
status = self.simplisafe.state
if status.lower() == 'off':
state = STATE_ALARM_DISARMED
elif status.lower() == 'home' or status.lower() == 'home_count':
state = STATE_ALARM_ARMED_HOME
elif (status.lower() == 'away' or status.lower() == 'exitDelay' or
status.lower() == 'away_count'):
state = STATE_ALARM_ARMED_AWAY
else:
state = STATE_UNKNOWN
return state
return self._state
@property
def device_state_attributes(self):
"""Return the state attributes."""
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."""
self.simplisafe.update()
def alarm_disarm(self, code=None):
"""Send disarm command."""
if not self._validate_code(code, 'disarming'):
return
self.simplisafe.set_state('off')
_LOGGER.info("SimpliSafe alarm disarming")
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, 'arming home'):
return
self.simplisafe.set_state('home')
_LOGGER.info("SimpliSafe alarm arming home")
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, 'arming away'):
return
self.simplisafe.set_state('away')
_LOGGER.info("SimpliSafe alarm arming away")
return self._attrs
def _validate_code(self, code, state):
"""Validate given code."""
@@ -143,3 +83,63 @@ class SimpliSafeAlarm(AlarmControlPanel):
if not check:
_LOGGER.warning("Wrong code entered for %s", state)
return check
async def async_added_to_hass(self):
"""Register callbacks."""
@callback
def update():
"""Update the state."""
self.async_schedule_update_ha_state(True)
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
self.hass, TOPIC_UPDATE, update)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect dispatcher listener when removed."""
if self._async_unsub_dispatcher_connect:
self._async_unsub_dispatcher_connect()
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
if not self._validate_code(code, 'disarming'):
return
await self._system.set_off()
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
if not self._validate_code(code, 'arming home'):
return
await self._system.set_home()
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
if not self._validate_code(code, 'arming away'):
return
await self._system.set_away()
async def async_update(self):
"""Update alarm status."""
from simplipy.system import SystemStates
await self._system.update()
self._attrs[ATTR_ALARM_ACTIVE] = self._system.alarm_going_off
if self._system.temperature:
self._attrs[ATTR_TEMPERATURE] = self._system.temperature
if self._system.state == SystemStates.error:
return
if self._system.state == SystemStates.off:
self._state = STATE_ALARM_DISARMED
elif self._system.state in (SystemStates.home,
SystemStates.home_count):
self._state = STATE_ALARM_ARMED_HOME
elif self._system.state in (SystemStates.away, SystemStates.away_count,
SystemStates.exit_delay):
self._state = STATE_ALARM_ARMED_AWAY
else:
self._state = None

View File

@@ -4,70 +4,63 @@ Support for Vanderbilt (formerly Siemens) SPC alarm systems.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.spc/
"""
import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.spc import (
ATTR_DISCOVER_AREAS, DATA_API, DATA_REGISTRY, SpcWebGateway)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.core import callback
from homeassistant.components.spc import (DATA_API, SIGNAL_UPDATE_ALARM)
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED)
_LOGGER = logging.getLogger(__name__)
SPC_AREA_MODE_TO_STATE = {
'0': STATE_ALARM_DISARMED,
'1': STATE_ALARM_ARMED_HOME,
'3': STATE_ALARM_ARMED_AWAY,
}
def _get_alarm_state(spc_mode):
def _get_alarm_state(area):
"""Get the alarm state."""
return SPC_AREA_MODE_TO_STATE.get(spc_mode, STATE_UNKNOWN)
from pyspcwebgw.const import AreaMode
if area.verified_alarm:
return STATE_ALARM_TRIGGERED
mode_to_state = {
AreaMode.UNSET: STATE_ALARM_DISARMED,
AreaMode.PART_SET_A: STATE_ALARM_ARMED_HOME,
AreaMode.PART_SET_B: STATE_ALARM_ARMED_NIGHT,
AreaMode.FULL_SET: STATE_ALARM_ARMED_AWAY,
}
return mode_to_state.get(area.mode)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up the SPC alarm control panel platform."""
if (discovery_info is None or
discovery_info[ATTR_DISCOVER_AREAS] is None):
if discovery_info is None:
return
api = hass.data[DATA_API]
devices = [SpcAlarm(api, area)
for area in discovery_info[ATTR_DISCOVER_AREAS]]
async_add_devices(devices)
async_add_entities([SpcAlarm(area=area, api=api)
for area in api.areas.values()])
class SpcAlarm(alarm.AlarmControlPanel):
"""Representation of the SPC alarm panel."""
def __init__(self, api, area):
def __init__(self, area, api):
"""Initialize the SPC alarm panel."""
self._area_id = area['id']
self._name = area['name']
self._state = _get_alarm_state(area['mode'])
if self._state == STATE_ALARM_DISARMED:
self._changed_by = area.get('last_unset_user_name', 'unknown')
else:
self._changed_by = area.get('last_set_user_name', 'unknown')
self._area = area
self._api = api
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Call for adding new entities."""
self.hass.data[DATA_REGISTRY].register_alarm_device(
self._area_id, self)
async_dispatcher_connect(self.hass,
SIGNAL_UPDATE_ALARM.format(self._area.id),
self._update_callback)
@asyncio.coroutine
def async_update_from_spc(self, state, extra):
"""Update the alarm panel with a new state."""
self._state = state
self._changed_by = extra.get('changed_by', 'unknown')
self.async_schedule_update_ha_state()
@callback
def _update_callback(self):
"""Call update method."""
self.async_schedule_update_ha_state(True)
@property
def should_poll(self):
@@ -77,32 +70,38 @@ class SpcAlarm(alarm.AlarmControlPanel):
@property
def name(self):
"""Return the name of the device."""
return self._name
return self._area.name
@property
def changed_by(self):
"""Return the user the last change was triggered by."""
return self._changed_by
return self._area.last_changed_by
@property
def state(self):
"""Return the state of the device."""
return self._state
return _get_alarm_state(self._area)
@asyncio.coroutine
def async_alarm_disarm(self, code=None):
async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
yield from self._api.send_area_command(
self._area_id, SpcWebGateway.AREA_COMMAND_UNSET)
from pyspcwebgw.const import AreaMode
await self._api.change_mode(area=self._area,
new_mode=AreaMode.UNSET)
@asyncio.coroutine
def async_alarm_arm_home(self, code=None):
async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
yield from self._api.send_area_command(
self._area_id, SpcWebGateway.AREA_COMMAND_PART_SET)
from pyspcwebgw.const import AreaMode
await self._api.change_mode(area=self._area,
new_mode=AreaMode.PART_SET_A)
@asyncio.coroutine
def async_alarm_arm_away(self, code=None):
async def async_alarm_arm_night(self, code=None):
"""Send arm home command."""
from pyspcwebgw.const import AreaMode
await self._api.change_mode(area=self._area,
new_mode=AreaMode.PART_SET_B)
async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
yield from self._api.send_area_command(
self._area_id, SpcWebGateway.AREA_COMMAND_SET)
from pyspcwebgw.const import AreaMode
await self._api.change_mode(area=self._area,
new_mode=AreaMode.FULL_SET)

View File

@@ -18,7 +18,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_CUSTOM_BYPASS)
REQUIREMENTS = ['total_connect_client==0.18']
REQUIREMENTS = ['total_connect_client==0.20']
_LOGGER = logging.getLogger(__name__)
@@ -31,14 +31,14 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up a TotalConnect control panel."""
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
total_connect = TotalConnect(name, username, password)
add_devices([total_connect], True)
add_entities([total_connect], True)
class TotalConnect(alarm.AlarmControlPanel):

View File

@@ -17,13 +17,13 @@ from homeassistant.const import (
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Verisure platform."""
alarms = []
if int(hub.config.get(CONF_ALARM, 1)):
hub.update_overview()
alarms.append(VerisureAlarm())
add_devices(alarms)
add_entities(alarms)
def set_arm_state(state, code=None):

View File

@@ -4,7 +4,6 @@ Interfaces with Wink Cameras.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.wink/
"""
import asyncio
import logging
import homeassistant.components.alarm_control_panel as alarm
@@ -20,7 +19,7 @@ DEPENDENCIES = ['wink']
STATE_ALARM_PRIVACY = 'Private'
def setup_platform(hass, config, add_devices, discovery_info=None):
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Wink platform."""
import pywink
@@ -32,14 +31,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
except AttributeError:
_id = camera.object_id() + camera.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkCameraDevice(camera, hass)])
add_entities([WinkCameraDevice(camera, hass)])
class WinkCameraDevice(WinkDevice, alarm.AlarmControlPanel):
"""Representation a Wink camera alarm."""
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]['entities']['alarm_control_panel'].append(self)

View File

@@ -0,0 +1,98 @@
"""
Yale Smart Alarm client for interacting with the Yale Smart Alarm System API.
For more details about this platform, please refer to the documentation at
https://www.home-assistant.io/components/alarm_control_panel.yale_smart_alarm
"""
import logging
import voluptuous as vol
from homeassistant.components.alarm_control_panel import (
AlarmControlPanel, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_PASSWORD, CONF_USERNAME, CONF_NAME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['yalesmartalarmclient==0.1.4']
CONF_AREA_ID = 'area_id'
DEFAULT_NAME = 'Yale Smart Alarm'
DEFAULT_AREA_ID = '1'
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_AREA_ID, default=DEFAULT_AREA_ID): cv.string,
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the alarm platform."""
name = config[CONF_NAME]
username = config[CONF_USERNAME]
password = config[CONF_PASSWORD]
area_id = config[CONF_AREA_ID]
from yalesmartalarmclient.client import (
YaleSmartAlarmClient, AuthenticationError)
try:
client = YaleSmartAlarmClient(username, password, area_id)
except AuthenticationError:
_LOGGER.error("Authentication failed. Check credentials")
return
add_entities([YaleAlarmDevice(name, client)], True)
class YaleAlarmDevice(AlarmControlPanel):
"""Represent a Yale Smart Alarm."""
def __init__(self, name, client):
"""Initialize the Yale Alarm Device."""
self._name = name
self._client = client
self._state = None
from yalesmartalarmclient.client import (YALE_STATE_DISARM,
YALE_STATE_ARM_PARTIAL,
YALE_STATE_ARM_FULL)
self._state_map = {
YALE_STATE_DISARM: STATE_ALARM_DISARMED,
YALE_STATE_ARM_PARTIAL: STATE_ALARM_ARMED_HOME,
YALE_STATE_ARM_FULL: STATE_ALARM_ARMED_AWAY
}
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
def update(self):
"""Return the state of the device."""
armed_status = self._client.get_armed_status()
self._state = self._state_map.get(armed_status)
def alarm_disarm(self, code=None):
"""Send disarm command."""
self._client.disarm()
def alarm_arm_home(self, code=None):
"""Send arm home command."""
self._client.arm_partial()
def alarm_arm_away(self, code=None):
"""Send arm away command."""
self._client.arm_full()

View File

@@ -10,7 +10,8 @@ import logging
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.components.notify import (
ATTR_MESSAGE, DOMAIN as DOMAIN_NOTIFY)
from homeassistant.const import (
CONF_ENTITY_ID, STATE_IDLE, CONF_NAME, CONF_STATE, STATE_ON, STATE_OFF,
SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE, ATTR_ENTITY_ID)
@@ -59,64 +60,24 @@ def is_on(hass, entity_id):
return hass.states.is_state(entity_id, STATE_ON)
def turn_on(hass, entity_id):
"""Reset the alert."""
hass.add_job(async_turn_on, hass, entity_id)
@callback
def async_turn_on(hass, entity_id):
"""Async reset the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TURN_ON, data))
def turn_off(hass, entity_id):
"""Acknowledge alert."""
hass.add_job(async_turn_off, hass, entity_id)
@callback
def async_turn_off(hass, entity_id):
"""Async acknowledge the alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, data))
def toggle(hass, entity_id):
"""Toggle acknowledgement of alert."""
hass.add_job(async_toggle, hass, entity_id)
@callback
def async_toggle(hass, entity_id):
"""Async toggle acknowledgement of alert."""
data = {ATTR_ENTITY_ID: entity_id}
hass.async_create_task(
hass.services.async_call(DOMAIN, SERVICE_TOGGLE, data))
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Set up the Alert component."""
alerts = config.get(DOMAIN)
all_alerts = {}
@asyncio.coroutine
def async_handle_alert_service(service_call):
async def async_handle_alert_service(service_call):
"""Handle calls to alert services."""
alert_ids = service.extract_entity_ids(hass, service_call)
for alert_id in alert_ids:
alert = all_alerts[alert_id]
alert.async_set_context(service_call.context)
if service_call.service == SERVICE_TURN_ON:
yield from alert.async_turn_on()
await alert.async_turn_on()
elif service_call.service == SERVICE_TOGGLE:
yield from alert.async_toggle()
await alert.async_toggle()
else:
yield from alert.async_turn_off()
await alert.async_turn_off()
# Setup alerts
for entity_id, alert in alerts.items():
@@ -140,7 +101,7 @@ def async_setup(hass, config):
tasks = [alert.async_update_ha_state() for alert in all_alerts.values()]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
return True
@@ -195,17 +156,15 @@ class Alert(ToggleEntity):
"""Hide the alert when it is not firing."""
return not self._can_ack or not self._firing
@asyncio.coroutine
def watched_entity_change(self, entity, from_state, to_state):
async def watched_entity_change(self, entity, from_state, to_state):
"""Determine if the alert should start or stop."""
_LOGGER.debug("Watched entity (%s) has changed", entity)
if to_state.state == self._alert_state and not self._firing:
yield from self.begin_alerting()
await self.begin_alerting()
if to_state.state != self._alert_state and self._firing:
yield from self.end_alerting()
await self.end_alerting()
@asyncio.coroutine
def begin_alerting(self):
async def begin_alerting(self):
"""Begin the alert procedures."""
_LOGGER.debug("Beginning Alert: %s", self._name)
self._ack = False
@@ -213,25 +172,23 @@ class Alert(ToggleEntity):
self._next_delay = 0
if not self._skip_first:
yield from self._notify()
await self._notify()
else:
yield from self._schedule_notify()
await self._schedule_notify()
self.async_schedule_update_ha_state()
@asyncio.coroutine
def end_alerting(self):
async def end_alerting(self):
"""End the alert procedures."""
_LOGGER.debug("Ending Alert: %s", self._name)
self._cancel()
self._ack = False
self._firing = False
if self._done_message and self._send_done_message:
yield from self._notify_done_message()
await self._notify_done_message()
self.async_schedule_update_ha_state()
@asyncio.coroutine
def _schedule_notify(self):
async def _schedule_notify(self):
"""Schedule a notification."""
delay = self._delay[self._next_delay]
next_msg = datetime.now() + delay
@@ -239,8 +196,7 @@ class Alert(ToggleEntity):
event.async_track_point_in_time(self.hass, self._notify, next_msg)
self._next_delay = min(self._next_delay + 1, len(self._delay) - 1)
@asyncio.coroutine
def _notify(self, *args):
async def _notify(self, *args):
"""Send the alert notification."""
if not self._firing:
return
@@ -249,36 +205,32 @@ class Alert(ToggleEntity):
_LOGGER.info("Alerting: %s", self._name)
self._send_done_message = True
for target in self._notifiers:
yield from self.hass.services.async_call(
'notify', target, {'message': self._name})
yield from self._schedule_notify()
await self.hass.services.async_call(
DOMAIN_NOTIFY, target, {ATTR_MESSAGE: self._name})
await self._schedule_notify()
@asyncio.coroutine
def _notify_done_message(self, *args):
async def _notify_done_message(self, *args):
"""Send notification of complete alert."""
_LOGGER.info("Alerting: %s", self._done_message)
self._send_done_message = False
for target in self._notifiers:
yield from self.hass.services.async_call(
'notify', target, {'message': self._done_message})
await self.hass.services.async_call(
DOMAIN_NOTIFY, target, {ATTR_MESSAGE: self._done_message})
@asyncio.coroutine
def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs):
"""Async Unacknowledge alert."""
_LOGGER.debug("Reset Alert: %s", self._name)
self._ack = False
yield from self.async_update_ha_state()
await self.async_update_ha_state()
@asyncio.coroutine
def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs):
"""Async Acknowledge alert."""
_LOGGER.debug("Acknowledged Alert: %s", self._name)
self._ack = True
yield from self.async_update_ha_state()
await self.async_update_ha_state()
@asyncio.coroutine
def async_toggle(self, **kwargs):
async def async_toggle(self, **kwargs):
"""Async toggle alert."""
if self._ack:
return self.async_turn_on()
return self.async_turn_off()
return await self.async_turn_on()
return await self.async_turn_off()

View File

@@ -4,7 +4,6 @@ Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import logging
import voluptuous as vol
@@ -53,8 +52,7 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Activate Alexa component."""
config = config.get(DOMAIN, {})
flash_briefings_config = config.get(CONF_FLASH_BRIEFINGS)

View File

@@ -4,7 +4,6 @@ Support for Alexa skill service end point.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import asyncio
import enum
import logging
@@ -59,16 +58,15 @@ class AlexaIntentsView(http.HomeAssistantView):
url = INTENTS_API_ENDPOINT
name = 'api:alexa'
@asyncio.coroutine
def post(self, request):
async def post(self, request):
"""Handle Alexa."""
hass = request.app['hass']
message = yield from request.json()
message = await request.json()
_LOGGER.debug("Received Alexa request: %s", message)
try:
response = yield from async_handle_message(hass, message)
response = await async_handle_message(hass, message)
return b'' if response is None else self.json(response)
except UnknownRequest as err:
_LOGGER.warning(str(err))
@@ -101,8 +99,7 @@ def intent_error_response(hass, message, error):
return alexa_response.as_dict()
@asyncio.coroutine
def async_handle_message(hass, message):
async def async_handle_message(hass, message):
"""Handle an Alexa intent.
Raises:
@@ -120,20 +117,18 @@ def async_handle_message(hass, message):
if not handler:
raise UnknownRequest('Received unknown request {}'.format(req_type))
return (yield from handler(hass, message))
return await handler(hass, message)
@HANDLERS.register('SessionEndedRequest')
@asyncio.coroutine
def async_handle_session_end(hass, message):
async def async_handle_session_end(hass, message):
"""Handle a session end request."""
return None
@HANDLERS.register('IntentRequest')
@HANDLERS.register('LaunchRequest')
@asyncio.coroutine
def async_handle_intent(hass, message):
async def async_handle_intent(hass, message):
"""Handle an intent request.
Raises:
@@ -153,7 +148,7 @@ def async_handle_intent(hass, message):
else:
intent_name = alexa_intent_info['name']
intent_response = yield from intent.async_handle(
intent_response = await intent.async_handle(
hass, DOMAIN, intent_name,
{key: {'value': value} for key, value
in alexa_response.variables.items()})

View File

@@ -1,5 +1,4 @@
"""Support for alexa Smart Home Skill API."""
import asyncio
import logging
import math
from datetime import datetime
@@ -13,12 +12,13 @@ import homeassistant.util.color as color_util
from homeassistant.util.temperature import convert as convert_temperature
from homeassistant.util.decorator import Registry
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, CONF_NAME,
SERVICE_LOCK, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE,
ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, SERVICE_LOCK,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
SERVICE_UNLOCK, SERVICE_VOLUME_SET, TEMP_FAHRENHEIT, TEMP_CELSIUS,
CONF_UNIT_OF_MEASUREMENT, STATE_LOCKED, STATE_UNLOCKED, STATE_ON)
STATE_LOCKED, STATE_UNLOCKED, STATE_ON)
from .const import CONF_FILTER, CONF_ENTITY_CONFIG
@@ -53,6 +53,7 @@ CONF_DISPLAY_CATEGORIES = 'display_categories'
HANDLERS = Registry()
ENTITY_ADAPTERS = Registry()
EVENT_ALEXA_SMART_HOME = 'alexa_smart_home'
class _DisplayCategory:
@@ -159,7 +160,8 @@ class _AlexaEntity:
The API handlers should manipulate entities only through this interface.
"""
def __init__(self, config, entity):
def __init__(self, hass, config, entity):
self.hass = hass
self.config = config
self.entity = entity
self.entity_conf = config.entity_config.get(entity.entity_id, {})
@@ -383,6 +385,10 @@ class _AlexaInputController(_AlexaInterface):
class _AlexaTemperatureSensor(_AlexaInterface):
def __init__(self, hass, entity):
_AlexaInterface.__init__(self, entity)
self.hass = hass
def name(self):
return 'Alexa.TemperatureSensor'
@@ -396,9 +402,10 @@ class _AlexaTemperatureSensor(_AlexaInterface):
if name != 'temperature':
raise _UnsupportedProperty(name)
unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT]
unit = self.entity.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
temp = self.entity.state
if self.entity.domain == climate.DOMAIN:
unit = self.hass.config.units.temperature_unit
temp = self.entity.attributes.get(
climate.ATTR_CURRENT_TEMPERATURE)
return {
@@ -408,6 +415,10 @@ class _AlexaTemperatureSensor(_AlexaInterface):
class _AlexaThermostatController(_AlexaInterface):
def __init__(self, hass, entity):
_AlexaInterface.__init__(self, entity)
self.hass = hass
def name(self):
return 'Alexa.ThermostatController'
@@ -438,8 +449,7 @@ class _AlexaThermostatController(_AlexaInterface):
raise _UnsupportedProperty(name)
return mode
unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT]
temp = None
unit = self.hass.config.units.temperature_unit
if name == 'targetSetpoint':
temp = self.entity.attributes.get(climate.ATTR_TEMPERATURE)
elif name == 'lowerSetpoint':
@@ -490,8 +500,8 @@ class _ClimateCapabilities(_AlexaEntity):
return [_DisplayCategory.THERMOSTAT]
def interfaces(self):
yield _AlexaThermostatController(self.entity)
yield _AlexaTemperatureSensor(self.entity)
yield _AlexaThermostatController(self.hass, self.entity)
yield _AlexaTemperatureSensor(self.hass, self.entity)
@ENTITY_ADAPTERS.register(cover.DOMAIN)
@@ -608,11 +618,11 @@ class _SensorCapabilities(_AlexaEntity):
def interfaces(self):
attrs = self.entity.attributes
if attrs.get(CONF_UNIT_OF_MEASUREMENT) in (
if attrs.get(ATTR_UNIT_OF_MEASUREMENT) in (
TEMP_FAHRENHEIT,
TEMP_CELSIUS,
):
yield _AlexaTemperatureSensor(self.entity)
yield _AlexaTemperatureSensor(self.hass, self.entity)
class _Cause:
@@ -684,8 +694,7 @@ class SmartHomeView(http.HomeAssistantView):
"""Initialize."""
self.smart_home_config = smart_home_config
@asyncio.coroutine
def post(self, request):
async def post(self, request):
"""Handle Alexa Smart Home requests.
The Smart Home API requires the endpoint to be implemented in AWS
@@ -693,34 +702,57 @@ class SmartHomeView(http.HomeAssistantView):
the response.
"""
hass = request.app['hass']
message = yield from request.json()
message = await request.json()
_LOGGER.debug("Received Alexa Smart Home request: %s", message)
response = yield from async_handle_message(
response = await async_handle_message(
hass, self.smart_home_config, message)
_LOGGER.debug("Sending Alexa Smart Home response: %s", response)
return b'' if response is None else self.json(response)
@asyncio.coroutine
def async_handle_message(hass, config, message):
async def async_handle_message(hass, config, request, context=None):
"""Handle incoming API messages."""
assert message[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3'
assert request[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3'
if context is None:
context = ha.Context()
# Read head data
message = message[API_DIRECTIVE]
namespace = message[API_HEADER]['namespace']
name = message[API_HEADER]['name']
request = request[API_DIRECTIVE]
namespace = request[API_HEADER]['namespace']
name = request[API_HEADER]['name']
# Do we support this API request?
funct_ref = HANDLERS.get((namespace, name))
if not funct_ref:
if funct_ref:
response = await funct_ref(hass, config, request, context)
else:
_LOGGER.warning(
"Unsupported API request %s/%s", namespace, name)
return api_error(message)
response = api_error(request)
return (yield from funct_ref(hass, config, message))
request_info = {
'namespace': namespace,
'name': name,
}
if API_ENDPOINT in request and 'endpointId' in request[API_ENDPOINT]:
request_info['entity_id'] = \
request[API_ENDPOINT]['endpointId'].replace('#', '.')
response_header = response[API_EVENT][API_HEADER]
hass.bus.async_fire(EVENT_ALEXA_SMART_HOME, {
'request': request_info,
'response': {
'namespace': response_header['namespace'],
'name': response_header['name'],
}
}, context=context)
return response
def api_message(request,
@@ -784,8 +816,7 @@ def api_error(request,
@HANDLERS.register(('Alexa.Discovery', 'Discover'))
@asyncio.coroutine
def async_api_discovery(hass, config, request):
async def async_api_discovery(hass, config, request, context):
"""Create a API formatted discovery response.
Async friendly.
@@ -800,7 +831,7 @@ def async_api_discovery(hass, config, request):
if entity.domain not in ENTITY_ADAPTERS:
continue
alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity)
alexa_entity = ENTITY_ADAPTERS[entity.domain](hass, config, entity)
endpoint = {
'displayCategories': alexa_entity.display_categories(),
@@ -827,8 +858,7 @@ def async_api_discovery(hass, config, request):
def extract_entity(funct):
"""Decorate for extract entity object from request."""
@asyncio.coroutine
def async_api_entity_wrapper(hass, config, request):
async def async_api_entity_wrapper(hass, config, request, context):
"""Process a turn on request."""
entity_id = request[API_ENDPOINT]['endpointId'].replace('#', '.')
@@ -839,15 +869,14 @@ def extract_entity(funct):
request[API_HEADER]['name'], entity_id)
return api_error(request, error_type='NO_SUCH_ENDPOINT')
return (yield from funct(hass, config, request, entity))
return await funct(hass, config, request, context, entity)
return async_api_entity_wrapper
@HANDLERS.register(('Alexa.PowerController', 'TurnOn'))
@extract_entity
@asyncio.coroutine
def async_api_turn_on(hass, config, request, entity):
async def async_api_turn_on(hass, config, request, context, entity):
"""Process a turn on request."""
domain = entity.domain
if entity.domain == group.DOMAIN:
@@ -857,17 +886,16 @@ def async_api_turn_on(hass, config, request, entity):
if entity.domain == cover.DOMAIN:
service = cover.SERVICE_OPEN_COVER
yield from hass.services.async_call(domain, service, {
await hass.services.async_call(domain, service, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PowerController', 'TurnOff'))
@extract_entity
@asyncio.coroutine
def async_api_turn_off(hass, config, request, entity):
async def async_api_turn_off(hass, config, request, context, entity):
"""Process a turn off request."""
domain = entity.domain
if entity.domain == group.DOMAIN:
@@ -877,32 +905,30 @@ def async_api_turn_off(hass, config, request, entity):
if entity.domain == cover.DOMAIN:
service = cover.SERVICE_CLOSE_COVER
yield from hass.services.async_call(domain, service, {
await hass.services.async_call(domain, service, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.BrightnessController', 'SetBrightness'))
@extract_entity
@asyncio.coroutine
def async_api_set_brightness(hass, config, request, entity):
async def async_api_set_brightness(hass, config, request, context, entity):
"""Process a set brightness request."""
brightness = int(request[API_PAYLOAD]['brightness'])
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_BRIGHTNESS_PCT: brightness,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.BrightnessController', 'AdjustBrightness'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_brightness(hass, config, request, entity):
async def async_api_adjust_brightness(hass, config, request, context, entity):
"""Process an adjust brightness request."""
brightness_delta = int(request[API_PAYLOAD]['brightnessDelta'])
@@ -915,18 +941,17 @@ def async_api_adjust_brightness(hass, config, request, entity):
# set brightness
brightness = max(0, brightness_delta + current)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_BRIGHTNESS_PCT: brightness,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.ColorController', 'SetColor'))
@extract_entity
@asyncio.coroutine
def async_api_set_color(hass, config, request, entity):
async def async_api_set_color(hass, config, request, context, entity):
"""Process a set color request."""
rgb = color_util.color_hsb_to_RGB(
float(request[API_PAYLOAD]['color']['hue']),
@@ -934,25 +959,25 @@ def async_api_set_color(hass, config, request, entity):
float(request[API_PAYLOAD]['color']['brightness'])
)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_RGB_COLOR: rgb,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.ColorTemperatureController', 'SetColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_set_color_temperature(hass, config, request, entity):
async def async_api_set_color_temperature(hass, config, request, context,
entity):
"""Process a set color temperature request."""
kelvin = int(request[API_PAYLOAD]['colorTemperatureInKelvin'])
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_KELVIN: kelvin,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@@ -960,17 +985,17 @@ def async_api_set_color_temperature(hass, config, request, entity):
@HANDLERS.register(
('Alexa.ColorTemperatureController', 'DecreaseColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_decrease_color_temp(hass, config, request, entity):
async def async_api_decrease_color_temp(hass, config, request, context,
entity):
"""Process a decrease color temperature request."""
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS))
value = min(max_mireds, current + 50)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_COLOR_TEMP: value,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@@ -978,31 +1003,30 @@ def async_api_decrease_color_temp(hass, config, request, entity):
@HANDLERS.register(
('Alexa.ColorTemperatureController', 'IncreaseColorTemperature'))
@extract_entity
@asyncio.coroutine
def async_api_increase_color_temp(hass, config, request, entity):
async def async_api_increase_color_temp(hass, config, request, context,
entity):
"""Process an increase color temperature request."""
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
value = max(min_mireds, current - 50)
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id,
light.ATTR_COLOR_TEMP: value,
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.SceneController', 'Activate'))
@extract_entity
@asyncio.coroutine
def async_api_activate(hass, config, request, entity):
async def async_api_activate(hass, config, request, context, entity):
"""Process an activate request."""
domain = entity.domain
yield from hass.services.async_call(domain, SERVICE_TURN_ON, {
await hass.services.async_call(domain, SERVICE_TURN_ON, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
payload = {
'cause': {'type': _Cause.VOICE_INTERACTION},
@@ -1019,14 +1043,13 @@ def async_api_activate(hass, config, request, entity):
@HANDLERS.register(('Alexa.SceneController', 'Deactivate'))
@extract_entity
@asyncio.coroutine
def async_api_deactivate(hass, config, request, entity):
async def async_api_deactivate(hass, config, request, context, entity):
"""Process a deactivate request."""
domain = entity.domain
yield from hass.services.async_call(domain, SERVICE_TURN_OFF, {
await hass.services.async_call(domain, SERVICE_TURN_OFF, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
payload = {
'cause': {'type': _Cause.VOICE_INTERACTION},
@@ -1043,8 +1066,7 @@ def async_api_deactivate(hass, config, request, entity):
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
@extract_entity
@asyncio.coroutine
def async_api_set_percentage(hass, config, request, entity):
async def async_api_set_percentage(hass, config, request, context, entity):
"""Process a set percentage request."""
percentage = int(request[API_PAYLOAD]['percentage'])
service = None
@@ -1066,16 +1088,15 @@ def async_api_set_percentage(hass, config, request, entity):
service = SERVICE_SET_COVER_POSITION
data[cover.ATTR_POSITION] = percentage
yield from hass.services.async_call(
entity.domain, service, data, blocking=False)
await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_percentage(hass, config, request, entity):
async def async_api_adjust_percentage(hass, config, request, context, entity):
"""Process an adjust percentage request."""
percentage_delta = int(request[API_PAYLOAD]['percentageDelta'])
service = None
@@ -1114,20 +1135,19 @@ def async_api_adjust_percentage(hass, config, request, entity):
data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
yield from hass.services.async_call(
entity.domain, service, data, blocking=False)
await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.LockController', 'Lock'))
@extract_entity
@asyncio.coroutine
def async_api_lock(hass, config, request, entity):
async def async_api_lock(hass, config, request, context, entity):
"""Process a lock request."""
yield from hass.services.async_call(entity.domain, SERVICE_LOCK, {
await hass.services.async_call(entity.domain, SERVICE_LOCK, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
# Alexa expects a lockState in the response, we don't know the actual
# lockState at this point but assume it is locked. It is reported
@@ -1144,20 +1164,18 @@ def async_api_lock(hass, config, request, entity):
# Not supported by Alexa yet
@HANDLERS.register(('Alexa.LockController', 'Unlock'))
@extract_entity
@asyncio.coroutine
def async_api_unlock(hass, config, request, entity):
async def async_api_unlock(hass, config, request, context, entity):
"""Process an unlock request."""
yield from hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
await hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
ATTR_ENTITY_ID: entity.entity_id
}, blocking=False)
}, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'SetVolume'))
@extract_entity
@asyncio.coroutine
def async_api_set_volume(hass, config, request, entity):
async def async_api_set_volume(hass, config, request, context, entity):
"""Process a set volume request."""
volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2)
@@ -1166,17 +1184,16 @@ def async_api_set_volume(hass, config, request, entity):
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_VOLUME_SET,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.InputController', 'SelectInput'))
@extract_entity
@asyncio.coroutine
def async_api_select_input(hass, config, request, entity):
async def async_api_select_input(hass, config, request, context, entity):
"""Process a set input request."""
media_input = request[API_PAYLOAD]['input']
@@ -1200,17 +1217,16 @@ def async_api_select_input(hass, config, request, entity):
media_player.ATTR_INPUT_SOURCE: media_input,
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, media_player.SERVICE_SELECT_SOURCE,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_volume(hass, config, request, entity):
async def async_api_adjust_volume(hass, config, request, context, entity):
"""Process an adjust volume request."""
volume_delta = int(request[API_PAYLOAD]['volume'])
@@ -1229,17 +1245,16 @@ def async_api_adjust_volume(hass, config, request, entity):
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_SET,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.StepSpeaker', 'AdjustVolume'))
@extract_entity
@asyncio.coroutine
def async_api_adjust_volume_step(hass, config, request, entity):
async def async_api_adjust_volume_step(hass, config, request, context, entity):
"""Process an adjust volume step request."""
# media_player volume up/down service does not support specifying steps
# each component handles it differently e.g. via config.
@@ -1252,13 +1267,13 @@ def async_api_adjust_volume_step(hass, config, request, entity):
}
if volume_step > 0:
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_UP,
data, blocking=False)
data, blocking=False, context=context)
elif volume_step < 0:
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_DOWN,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@@ -1266,8 +1281,7 @@ def async_api_adjust_volume_step(hass, config, request, entity):
@HANDLERS.register(('Alexa.StepSpeaker', 'SetMute'))
@HANDLERS.register(('Alexa.Speaker', 'SetMute'))
@extract_entity
@asyncio.coroutine
def async_api_set_mute(hass, config, request, entity):
async def async_api_set_mute(hass, config, request, context, entity):
"""Process a set mute request."""
mute = bool(request[API_PAYLOAD]['mute'])
@@ -1276,98 +1290,94 @@ def async_api_set_mute(hass, config, request, entity):
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, media_player.SERVICE_VOLUME_MUTE,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Play'))
@extract_entity
@asyncio.coroutine
def async_api_play(hass, config, request, entity):
async def async_api_play(hass, config, request, context, entity):
"""Process a play request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_PLAY,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Pause'))
@extract_entity
@asyncio.coroutine
def async_api_pause(hass, config, request, entity):
async def async_api_pause(hass, config, request, context, entity):
"""Process a pause request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_PAUSE,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Stop'))
@extract_entity
@asyncio.coroutine
def async_api_stop(hass, config, request, entity):
async def async_api_stop(hass, config, request, context, entity):
"""Process a stop request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_STOP,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Next'))
@extract_entity
@asyncio.coroutine
def async_api_next(hass, config, request, entity):
async def async_api_next(hass, config, request, context, entity):
"""Process a next request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_NEXT_TRACK,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa.PlaybackController', 'Previous'))
@extract_entity
@asyncio.coroutine
def async_api_previous(hass, config, request, entity):
async def async_api_previous(hass, config, request, context, entity):
"""Process a previous request."""
data = {
ATTR_ENTITY_ID: entity.entity_id
}
yield from hass.services.async_call(
await hass.services.async_call(
entity.domain, SERVICE_MEDIA_PREVIOUS_TRACK,
data, blocking=False)
data, blocking=False, context=context)
return api_message(request)
def api_error_temp_range(request, temp, min_temp, max_temp, unit):
def api_error_temp_range(hass, request, temp, min_temp, max_temp):
"""Create temperature value out of range API error response.
Async friendly.
"""
unit = hass.config.units.temperature_unit
temp_range = {
'minimumValue': {
'value': min_temp,
@@ -1388,8 +1398,9 @@ def api_error_temp_range(request, temp, min_temp, max_temp, unit):
)
def temperature_from_object(temp_obj, to_unit, interval=False):
def temperature_from_object(hass, temp_obj, interval=False):
"""Get temperature from Temperature object in requested unit."""
to_unit = hass.config.units.temperature_unit
from_unit = TEMP_CELSIUS
temp = float(temp_obj['value'])
@@ -1405,9 +1416,8 @@ def temperature_from_object(temp_obj, to_unit, interval=False):
@HANDLERS.register(('Alexa.ThermostatController', 'SetTargetTemperature'))
@extract_entity
async def async_api_set_target_temp(hass, config, request, entity):
async def async_api_set_target_temp(hass, config, request, context, entity):
"""Process a set target temperature request."""
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP)
@@ -1417,48 +1427,45 @@ async def async_api_set_target_temp(hass, config, request, entity):
payload = request[API_PAYLOAD]
if 'targetSetpoint' in payload:
temp = temperature_from_object(
payload['targetSetpoint'], unit)
temp = temperature_from_object(hass, payload['targetSetpoint'])
if temp < min_temp or temp > max_temp:
return api_error_temp_range(
request, temp, min_temp, max_temp, unit)
hass, request, temp, min_temp, max_temp)
data[ATTR_TEMPERATURE] = temp
if 'lowerSetpoint' in payload:
temp_low = temperature_from_object(
payload['lowerSetpoint'], unit)
temp_low = temperature_from_object(hass, payload['lowerSetpoint'])
if temp_low < min_temp or temp_low > max_temp:
return api_error_temp_range(
request, temp_low, min_temp, max_temp, unit)
hass, request, temp_low, min_temp, max_temp)
data[climate.ATTR_TARGET_TEMP_LOW] = temp_low
if 'upperSetpoint' in payload:
temp_high = temperature_from_object(
payload['upperSetpoint'], unit)
temp_high = temperature_from_object(hass, payload['upperSetpoint'])
if temp_high < min_temp or temp_high > max_temp:
return api_error_temp_range(
request, temp_high, min_temp, max_temp, unit)
hass, request, temp_high, min_temp, max_temp)
data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high
await hass.services.async_call(
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False)
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False,
context=context)
return api_message(request)
@HANDLERS.register(('Alexa.ThermostatController', 'AdjustTargetTemperature'))
@extract_entity
async def async_api_adjust_target_temp(hass, config, request, entity):
async def async_api_adjust_target_temp(hass, config, request, context, entity):
"""Process an adjust target temperature request."""
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
max_temp = entity.attributes.get(climate.ATTR_MAX_TEMP)
temp_delta = temperature_from_object(
request[API_PAYLOAD]['targetSetpointDelta'], unit, interval=True)
hass, request[API_PAYLOAD]['targetSetpointDelta'], interval=True)
target_temp = float(entity.attributes.get(ATTR_TEMPERATURE)) + temp_delta
if target_temp < min_temp or target_temp > max_temp:
return api_error_temp_range(
request, target_temp, min_temp, max_temp, unit)
hass, request, target_temp, min_temp, max_temp)
data = {
ATTR_ENTITY_ID: entity.entity_id,
@@ -1466,14 +1473,16 @@ async def async_api_adjust_target_temp(hass, config, request, entity):
}
await hass.services.async_call(
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False)
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False,
context=context)
return api_message(request)
@HANDLERS.register(('Alexa.ThermostatController', 'SetThermostatMode'))
@extract_entity
async def async_api_set_thermostat_mode(hass, config, request, entity):
async def async_api_set_thermostat_mode(hass, config, request, context,
entity):
"""Process a set thermostat mode request."""
mode = request[API_PAYLOAD]['thermostatMode']
mode = mode if isinstance(mode, str) else mode['value']
@@ -1499,17 +1508,16 @@ async def async_api_set_thermostat_mode(hass, config, request, entity):
await hass.services.async_call(
entity.domain, climate.SERVICE_SET_OPERATION_MODE, data,
blocking=False)
blocking=False, context=context)
return api_message(request)
@HANDLERS.register(('Alexa', 'ReportState'))
@extract_entity
@asyncio.coroutine
def async_api_reportstate(hass, config, request, entity):
async def async_api_reportstate(hass, config, request, context, entity):
"""Process a ReportState request."""
alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity)
alexa_entity = ENTITY_ADAPTERS[entity.domain](hass, config, entity)
properties = []
for interface in alexa_entity.interfaces():
properties.extend(interface.serialize_properties())
@@ -1519,3 +1527,8 @@ def async_api_reportstate(hass, config, request, entity):
name='StateReport',
context={'properties': properties}
)
def turned_off_response(message):
"""Return a device turned off response."""
return api_error(message[API_DIRECTIVE], error_type='BRIDGE_UNREACHABLE')

View File

@@ -149,16 +149,14 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Set up the IP Webcam component."""
from pydroid_ipcam import PyDroidIPCam
webcams = hass.data[DATA_IP_WEBCAM] = {}
websession = async_get_clientsession(hass)
@asyncio.coroutine
def async_setup_ipcamera(cam_config):
async def async_setup_ipcamera(cam_config):
"""Set up an IP camera."""
host = cam_config[CONF_HOST]
username = cam_config.get(CONF_USERNAME)
@@ -188,16 +186,15 @@ def async_setup(hass, config):
if motion is None:
motion = 'motion_active' in cam.enabled_sensors
@asyncio.coroutine
def async_update_data(now):
async def async_update_data(now):
"""Update data from IP camera in SCAN_INTERVAL."""
yield from cam.update()
await cam.update()
async_dispatcher_send(hass, SIGNAL_UPDATE_DATA, host)
async_track_point_in_utc_time(
hass, async_update_data, utcnow() + interval)
yield from async_update_data(None)
await async_update_data(None)
# Load platforms
webcams[host] = cam
@@ -242,7 +239,7 @@ def async_setup(hass, config):
tasks = [async_setup_ipcamera(conf) for conf in config[DOMAIN]]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
return True
@@ -255,8 +252,7 @@ class AndroidIPCamEntity(Entity):
self._host = host
self._ipcam = ipcam
@asyncio.coroutine
def async_added_to_hass(self):
async def async_added_to_hass(self):
"""Register update dispatcher."""
@callback
def async_ipcam_update(host):

View File

@@ -49,10 +49,9 @@ def setup(hass, config):
# It doesn't really matter why we're not able to get the status, just that
# we can't.
# pylint: disable=broad-except
try:
DATA.update(no_throttle=True)
except Exception:
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Failure while testing APCUPSd status retrieval.")
return False
return True

View File

@@ -24,7 +24,7 @@ from homeassistant.exceptions import TemplateError
from homeassistant.helpers import template
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.helpers.state import AsyncTrackStates
import homeassistant.remote as rem
from homeassistant.helpers.json import JSONEncoder
_LOGGER = logging.getLogger(__name__)
@@ -102,7 +102,7 @@ class APIEventStream(HomeAssistantView):
if event.event_type == EVENT_HOMEASSISTANT_STOP:
data = stop_obj
else:
data = json.dumps(event, cls=rem.JSONEncoder)
data = json.dumps(event, cls=JSONEncoder)
await to_write.put(data)
@@ -141,6 +141,8 @@ class APIEventStream(HomeAssistantView):
_LOGGER.debug("STREAM %s RESPONSE CLOSED", id(stop_obj))
unsub_stream()
return response
class APIConfigView(HomeAssistantView):
"""View to handle Configuration requests."""

View File

@@ -6,7 +6,6 @@ https://home-assistant.io/components/apple_tv/
"""
import asyncio
import logging
from typing import Sequence, TypeVar, Union
import voluptuous as vol
@@ -78,14 +77,13 @@ def request_configuration(hass, config, atv, credentials):
"""Request configuration steps from the user."""
configurator = hass.components.configurator
@asyncio.coroutine
def configuration_callback(callback_data):
async def configuration_callback(callback_data):
"""Handle the submitted configuration."""
from pyatv import exceptions
pin = callback_data.get('pin')
try:
yield from atv.airplay.finish_authentication(pin)
await atv.airplay.finish_authentication(pin)
hass.components.persistent_notification.async_create(
'Authentication succeeded!<br /><br />Add the following '
'to credentials: in your apple_tv configuration:<br /><br />'
@@ -109,11 +107,10 @@ def request_configuration(hass, config, atv, credentials):
)
@asyncio.coroutine
def scan_for_apple_tvs(hass):
async def scan_for_apple_tvs(hass):
"""Scan for devices and present a notification of the ones found."""
import pyatv
atvs = yield from pyatv.scan_for_apple_tvs(hass.loop, timeout=3)
atvs = await pyatv.scan_for_apple_tvs(hass.loop, timeout=3)
devices = []
for atv in atvs:
@@ -133,14 +130,12 @@ def scan_for_apple_tvs(hass):
notification_id=NOTIFICATION_SCAN_ID)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Set up the Apple TV component."""
if DATA_APPLE_TV not in hass.data:
hass.data[DATA_APPLE_TV] = {}
@asyncio.coroutine
def async_service_handler(service):
async def async_service_handler(service):
"""Handle service calls."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
@@ -159,17 +154,16 @@ def async_setup(hass, config):
continue
atv = device.atv
credentials = yield from atv.airplay.generate_credentials()
yield from atv.airplay.load_credentials(credentials)
credentials = await atv.airplay.generate_credentials()
await atv.airplay.load_credentials(credentials)
_LOGGER.debug('Generated new credentials: %s', credentials)
yield from atv.airplay.start_authentication()
await atv.airplay.start_authentication()
hass.async_add_job(request_configuration,
hass, config, atv, credentials)
@asyncio.coroutine
def atv_discovered(service, info):
async def atv_discovered(service, info):
"""Set up an Apple TV that was auto discovered."""
yield from _setup_atv(hass, {
await _setup_atv(hass, {
CONF_NAME: info['name'],
CONF_HOST: info['host'],
CONF_LOGIN_ID: info['properties']['hG'],
@@ -180,7 +174,7 @@ def async_setup(hass, config):
tasks = [_setup_atv(hass, conf) for conf in config.get(DOMAIN, [])]
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SCAN, async_service_handler,
@@ -193,8 +187,7 @@ def async_setup(hass, config):
return True
@asyncio.coroutine
def _setup_atv(hass, atv_config):
async def _setup_atv(hass, atv_config):
"""Set up an Apple TV."""
import pyatv
name = atv_config.get(CONF_NAME)
@@ -210,7 +203,7 @@ def _setup_atv(hass, atv_config):
session = async_get_clientsession(hass)
atv = pyatv.connect_to_apple_tv(details, hass.loop, session=session)
if credentials:
yield from atv.airplay.load_credentials(credentials)
await atv.airplay.load_credentials(credentials)
power = AppleTVPowerManager(hass, atv, start_off)
hass.data[DATA_APPLE_TV][host] = {
@@ -259,4 +252,4 @@ class AppleTVPowerManager:
self.atv.push_updater.start()
for listener in self.listeners:
self.hass.async_add_job(listener.async_update_ha_state())
self.hass.async_create_task(listener.async_update_ha_state())

View File

@@ -0,0 +1,95 @@
"""
Support for AquaLogic component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/aqualogic/
"""
from datetime import timedelta
import logging
import time
import threading
import voluptuous as vol
from homeassistant.const import (CONF_HOST, CONF_PORT,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import config_validation as cv
REQUIREMENTS = ["aqualogic==1.0"]
_LOGGER = logging.getLogger(__name__)
DOMAIN = "aqualogic"
UPDATE_TOPIC = DOMAIN + "_update"
CONF_UNIT = "unit"
RECONNECT_INTERVAL = timedelta(seconds=10)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Set up AquaLogic platform."""
host = config[DOMAIN][CONF_HOST]
port = config[DOMAIN][CONF_PORT]
processor = AquaLogicProcessor(hass, host, port)
hass.data[DOMAIN] = processor
hass.bus.listen_once(EVENT_HOMEASSISTANT_START,
processor.start_listen)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
processor.shutdown)
_LOGGER.debug("AquaLogicProcessor %s:%i initialized", host, port)
return True
class AquaLogicProcessor(threading.Thread):
"""AquaLogic event processor thread."""
def __init__(self, hass, host, port):
"""Initialize the data object."""
super().__init__(daemon=True)
self._hass = hass
self._host = host
self._port = port
self._shutdown = False
self._panel = None
def start_listen(self, event):
"""Start event-processing thread."""
_LOGGER.debug("Event processing thread started")
self.start()
def shutdown(self, event):
"""Signal shutdown of processing event."""
_LOGGER.debug("Event processing signaled exit")
self._shutdown = True
def data_changed(self, panel):
"""Aqualogic data changed callback."""
self._hass.helpers.dispatcher.dispatcher_send(UPDATE_TOPIC)
def run(self):
"""Event thread."""
from aqualogic.core import AquaLogic
while True:
self._panel = AquaLogic()
self._panel.connect(self._host, self._port)
self._panel.process(self.data_changed)
if self._shutdown:
return
_LOGGER.error("Connection to %s:%d lost",
self._host, self._port)
time.sleep(RECONNECT_INTERVAL.seconds)
@property
def panel(self):
"""Retrieve the AquaLogic object."""
return self._panel

View File

@@ -16,7 +16,7 @@ from homeassistant.const import (
from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.dispatcher import dispatcher_send
REQUIREMENTS = ['pyarlo==0.2.0']
REQUIREMENTS = ['pyarlo==0.2.2']
_LOGGER = logging.getLogger(__name__)
@@ -61,10 +61,12 @@ def setup(hass, config):
arlo_base_station = next((
station for station in arlo.base_stations), None)
if arlo_base_station is None:
if arlo_base_station is not None:
arlo_base_station.refresh_rate = scan_interval.total_seconds()
elif not arlo.cameras:
_LOGGER.error("No Arlo camera or base station available.")
return False
arlo_base_station.refresh_rate = scan_interval.total_seconds()
hass.data[DATA_ARLO] = arlo
except (ConnectTimeout, HTTPError) as ex:
@@ -79,7 +81,7 @@ def setup(hass, config):
def hub_refresh(event_time):
"""Call ArloHub to refresh information."""
_LOGGER.info("Updating Arlo Hub component")
_LOGGER.debug("Updating Arlo Hub component")
hass.data[DATA_ARLO].update(update_cameras=True,
update_base_station=True)
dispatcher_send(hass, SIGNAL_UPDATE_ARLO)

View File

@@ -13,22 +13,25 @@ from homeassistant.core import callback
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_send)
async_dispatcher_send, dispatcher_connect)
REQUIREMENTS = ['asterisk_mbox==0.4.0']
REQUIREMENTS = ['asterisk_mbox==0.5.0']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'asterisk_mbox'
SIGNAL_DISCOVER_PLATFORM = "asterisk_mbox.discover_platform"
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
SIGNAL_CDR_UPDATE = 'asterisk_mbox.message_updated'
SIGNAL_CDR_REQUEST = 'asterisk_mbox.message_request'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_PORT): int,
vol.Required(CONF_PORT): cv.port,
}),
}, extra=vol.ALLOW_EXTRA)
@@ -41,9 +44,7 @@ def setup(hass, config):
port = conf.get(CONF_PORT)
password = conf.get(CONF_PASSWORD)
hass.data[DOMAIN] = AsteriskData(hass, host, port, password)
discovery.load_platform(hass, 'mailbox', DOMAIN, {}, config)
hass.data[DOMAIN] = AsteriskData(hass, host, port, password, config)
return True
@@ -51,31 +52,71 @@ def setup(hass, config):
class AsteriskData:
"""Store Asterisk mailbox data."""
def __init__(self, hass, host, port, password):
def __init__(self, hass, host, port, password, config):
"""Init the Asterisk data object."""
from asterisk_mbox import Client as asteriskClient
self.hass = hass
self.client = asteriskClient(host, port, password, self.handle_data)
self.messages = []
self.config = config
self.messages = None
self.cdr = None
async_dispatcher_connect(
dispatcher_connect(
self.hass, SIGNAL_MESSAGE_REQUEST, self._request_messages)
dispatcher_connect(
self.hass, SIGNAL_CDR_REQUEST, self._request_cdr)
dispatcher_connect(
self.hass, SIGNAL_DISCOVER_PLATFORM, self._discover_platform)
# Only connect after signal connection to ensure we don't miss any
self.client = asteriskClient(host, port, password, self.handle_data)
@callback
def _discover_platform(self, component):
_LOGGER.debug("Adding mailbox %s", component)
self.hass.async_create_task(discovery.async_load_platform(
self.hass, "mailbox", component, {}, self.config))
@callback
def handle_data(self, command, msg):
"""Handle changes to the mailbox."""
from asterisk_mbox.commands import CMD_MESSAGE_LIST
from asterisk_mbox.commands import (CMD_MESSAGE_LIST,
CMD_MESSAGE_CDR_AVAILABLE,
CMD_MESSAGE_CDR)
if command == CMD_MESSAGE_LIST:
_LOGGER.debug("AsteriskVM sent updated message list")
_LOGGER.debug("AsteriskVM sent updated message list: Len %d",
len(msg))
old_messages = self.messages
self.messages = sorted(
msg, key=lambda item: item['info']['origtime'], reverse=True)
async_dispatcher_send(
self.hass, SIGNAL_MESSAGE_UPDATE, self.messages)
if not isinstance(old_messages, list):
async_dispatcher_send(self.hass, SIGNAL_DISCOVER_PLATFORM,
DOMAIN)
async_dispatcher_send(self.hass, SIGNAL_MESSAGE_UPDATE,
self.messages)
elif command == CMD_MESSAGE_CDR:
_LOGGER.debug("AsteriskVM sent updated CDR list: Len %d",
len(msg.get('entries', [])))
self.cdr = msg['entries']
async_dispatcher_send(self.hass, SIGNAL_CDR_UPDATE, self.cdr)
elif command == CMD_MESSAGE_CDR_AVAILABLE:
if not isinstance(self.cdr, list):
_LOGGER.debug("AsteriskVM adding CDR platform")
self.cdr = []
async_dispatcher_send(self.hass, SIGNAL_DISCOVER_PLATFORM,
"asterisk_cdr")
async_dispatcher_send(self.hass, SIGNAL_CDR_REQUEST)
else:
_LOGGER.debug("AsteriskVM sent unknown message '%d' len: %d",
command, len(msg))
@callback
def _request_messages(self):
"""Handle changes to the mailbox."""
_LOGGER.debug("Requesting message list")
self.client.messages()
@callback
def _request_cdr(self):
"""Handle changes to the CDR."""
_LOGGER.debug("Requesting CDR list")
self.client.get_cdr()

View File

@@ -4,7 +4,6 @@ Support for August devices.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/august/
"""
import logging
from datetime import timedelta
@@ -21,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
_CONFIGURING = {}
REQUIREMENTS = ['py-august==0.4.0']
REQUIREMENTS = ['py-august==0.6.0']
DEFAULT_TIMEOUT = 10
ACTIVITY_FETCH_LIMIT = 10
@@ -124,6 +123,7 @@ def setup_august(hass, config, api, authenticator):
return True
if state == AuthenticationState.BAD_PASSWORD:
_LOGGER.error("Invalid password provided")
return False
if state == AuthenticationState.REQUIRES_VALIDATION:
request_configuration(hass, config, api, authenticator)
@@ -165,6 +165,7 @@ class AugustData:
self._doorbell_detail_by_id = {}
self._lock_status_by_id = {}
self._lock_detail_by_id = {}
self._door_state_by_id = {}
self._activities_by_id = {}
@property
@@ -184,6 +185,7 @@ class AugustData:
def get_device_activities(self, device_id, *activity_types):
"""Return a list of activities."""
_LOGGER.debug("Getting device activities")
self._update_device_activities()
activities = self._activities_by_id.get(device_id, [])
@@ -199,6 +201,7 @@ class AugustData:
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def _update_device_activities(self, limit=ACTIVITY_FETCH_LIMIT):
"""Update data object with latest from August API."""
_LOGGER.debug("Updating device activities")
for house_id in self.house_ids:
activities = self._api.get_house_activities(self._access_token,
house_id,
@@ -218,14 +221,21 @@ class AugustData:
def _update_doorbells(self):
detail_by_id = {}
_LOGGER.debug("Start retrieving doorbell details")
for doorbell in self._doorbells:
_LOGGER.debug("Updating status for %s",
doorbell.device_name)
detail_by_id[doorbell.device_id] = self._api.get_doorbell_detail(
self._access_token, doorbell.device_id)
_LOGGER.debug("Completed retrieving doorbell details")
self._doorbell_detail_by_id = detail_by_id
def get_lock_status(self, lock_id):
"""Return lock status."""
"""Return status if the door is locked or unlocked.
This is status for the lock itself.
"""
self._update_locks()
return self._lock_status_by_id.get(lock_id)
@@ -234,17 +244,43 @@ class AugustData:
self._update_locks()
return self._lock_detail_by_id.get(lock_id)
def get_door_state(self, lock_id):
"""Return status if the door is open or closed.
This is the status from the door sensor.
"""
self._update_doors()
return self._door_state_by_id.get(lock_id)
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def _update_doors(self):
state_by_id = {}
_LOGGER.debug("Start retrieving door status")
for lock in self._locks:
_LOGGER.debug("Updating status for %s",
lock.device_name)
state_by_id[lock.device_id] = self._api.get_lock_door_status(
self._access_token, lock.device_id)
_LOGGER.debug("Completed retrieving door status")
self._door_state_by_id = state_by_id
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def _update_locks(self):
status_by_id = {}
detail_by_id = {}
_LOGGER.debug("Start retrieving locks status")
for lock in self._locks:
_LOGGER.debug("Updating status for %s",
lock.device_name)
status_by_id[lock.device_id] = self._api.get_lock_status(
self._access_token, lock.device_id)
detail_by_id[lock.device_id] = self._api.get_lock_detail(
self._access_token, lock.device_id)
_LOGGER.debug("Completed retrieving locks status")
self._lock_status_by_id = status_by_id
self._lock_detail_by_id = detail_by_id

View File

@@ -0,0 +1,7 @@
{
"mfa_setup": {
"totp": {
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "No hi ha serveis de notificaci\u00f3 disponibles."
},
"error": {
"invalid_code": "Codi inv\u00e0lid, si us plau torni a provar-ho."
},
"step": {
"init": {
"description": "Seleccioneu un dels serveis de notificaci\u00f3:",
"title": "Configureu una contrasenya d'un sol \u00fas a trav\u00e9s del component de notificacions"
},
"setup": {
"description": "**notify.{notify_service}** ha enviat una contrasenya d'un sol \u00fas. Introdu\u00efu-la a continuaci\u00f3:",
"title": "Verifiqueu la configuraci\u00f3"
}
},
"title": "Contrasenya d'un sol \u00fas del servei de notificacions"
},
"totp": {
"error": {
"invalid_code": "Codi inv\u00e0lid, si us plau torni a provar-ho. Si obteniu aquest error repetidament, assegureu-vos que la data i hora de Home Assistant sigui correcta i precisa."
},
"step": {
"init": {
"description": "Per activar la verificaci\u00f3 en dos passos mitjan\u00e7ant contrasenyes d'un sol \u00fas basades en temps, escanegeu el codi QR amb la vostre aplicaci\u00f3 de verificaci\u00f3. Si no en teniu cap, us recomanem [Google Authenticator](https://support.google.com/accounts/answer/1066447) o b\u00e9 [Authy](https://authy.com/). \n\n {qr_code} \n \nDespr\u00e9s d'escanejar el codi QR, introdu\u00efu el codi de sis d\u00edgits proporcionat per l'aplicaci\u00f3. Si teniu problemes per escanejar el codi QR, feu una configuraci\u00f3 manual amb el codi **`{code}`**.",
"title": "Configureu la verificaci\u00f3 en dos passos utilitzant TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Keine Benachrichtigungsdienste verf\u00fcgbar."
},
"error": {
"invalid_code": "Ung\u00fcltiger Code, bitte versuche es erneut."
},
"step": {
"init": {
"description": "Bitte w\u00e4hle einen der Benachrichtigungsdienste:",
"title": "Einmal Passwort f\u00fcr Notify einrichten"
},
"setup": {
"description": "Ein Einmal-Passwort wurde per ** notify gesendet. {notify_service} **. Bitte gebe es unten ein:",
"title": "\u00dcberpr\u00fcfe das Setup"
}
},
"title": "Benachrichtig f\u00fcr One-Time Password"
},
"totp": {
"error": {
"invalid_code": "Ung\u00fcltiger Code, bitte versuche es erneut. Wenn du diesen Fehler regelm\u00e4\u00dfig erhalten, stelle sicher, dass die Uhr deines Home Assistant-Systems korrekt ist."
},
"step": {
"init": {
"description": "Um die Zwei-Faktor-Authentifizierung mit zeitbasierten Einmalpassw\u00f6rtern zu aktivieren, scanne den QR-Code mit Ihrer Authentifizierungs-App. Wenn du keine hast, empfehlen wir entweder [Google Authenticator] (https://support.google.com/accounts/answer/1066447) oder [Authy] (https://authy.com/). \n\n {qr_code} \n \nNachdem du den Code gescannt hast, gebe den sechsstelligen Code aus der App ein, um das Setup zu \u00fcberpr\u00fcfen. Wenn es Probleme beim Scannen des QR-Codes gibt, f\u00fchre ein manuelles Setup mit dem Code ** ` {code} ` ** durch.",
"title": "Richte die Zwei-Faktor-Authentifizierung mit TOTP ein"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "No notification services available."
},
"error": {
"invalid_code": "Invalid code, please try again."
},
"step": {
"init": {
"description": "Please select one of the notification services:",
"title": "Set up one-time password delivered by notify component"
},
"setup": {
"description": "A one-time password has been sent via **notify.{notify_service}**. Please enter it below:",
"title": "Verify setup"
}
},
"title": "Notify One-Time Password"
},
"totp": {
"error": {
"invalid_code": "Invalid code, please try again. If you get this error consistently, please make sure the clock of your Home Assistant system is accurate."
},
"step": {
"init": {
"description": "To activate two factor authentication using time-based one-time passwords, scan the QR code with your authentication app. If you don't have one, we recommend either [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/).\n\n{qr_code}\n\nAfter scanning the code, enter the six digit code from your app to verify the setup. If you have problems scanning the QR code, do a manual setup with code **`{code}`**.",
"title": "Set up two-factor authentication using TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,12 @@
{
"mfa_setup": {
"totp": {
"step": {
"init": {
"title": "Configurar la autenticaci\u00f3n de dos factores mediante TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Aucun service de notification disponible."
},
"error": {
"invalid_code": "Code invalide. Veuillez essayer \u00e0 nouveau."
},
"step": {
"init": {
"description": "Veuillez s\u00e9lectionner l'un des services de notification:",
"title": "Configurer un mot de passe \u00e0 usage unique d\u00e9livr\u00e9 par le composant notify"
},
"setup": {
"description": "Un mot de passe unique a \u00e9t\u00e9 envoy\u00e9 par **notify.{notify_service}**. Veuillez le saisir ci-dessous :",
"title": "V\u00e9rifier la configuration"
}
},
"title": "Notifier un mot de passe unique"
},
"totp": {
"error": {
"invalid_code": "Code invalide. Veuillez essayez \u00e0 nouveau. Si cette erreur persiste, assurez-vous que l'horloge de votre syst\u00e8me Home Assistant est correcte."
},
"step": {
"init": {
"description": "Pour activer l'authentification \u00e0 deux facteurs \u00e0 l'aide de mots de passe \u00e0 utilisation unique bas\u00e9s sur l'heure, num\u00e9risez le code QR avec votre application d'authentification. Si vous n'en avez pas, nous vous recommandons d'utiliser [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ou [Authy] (https://authy.com/). \n\n {qr_code} \n \n Apr\u00e8s avoir num\u00e9ris\u00e9 le code, entrez le code \u00e0 six chiffres de votre application pour v\u00e9rifier la configuration. Si vous rencontrez des probl\u00e8mes lors de l\u2019analyse du code QR, effectuez une configuration manuelle avec le code ** ` {code} ` **.",
"title": "Configurer une authentification \u00e0 deux facteurs \u00e0 l'aide de TOTP"
}
},
"title": "TOTP (Mot de passe \u00e0 utilisation unique bas\u00e9 sur le temps)"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\u05d0\u05d9\u05df \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 notify \u05d6\u05de\u05d9\u05e0\u05d9\u05dd."
},
"error": {
"invalid_code": "\u05e7\u05d5\u05d3 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d0\u05e0\u05d0 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1."
},
"step": {
"init": {
"description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d0\u05d7\u05d3 \u05de\u05e9\u05e8\u05d5\u05ea\u05d9 notify",
"title": "\u05d4\u05d2\u05d3\u05e8 \u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05d4\u05e0\u05e9\u05dc\u05d7\u05ea \u05e2\u05dc \u05d9\u05d3\u05d9 \u05e8\u05db\u05d9\u05d1 notify"
},
"setup": {
"description": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05e0\u05e9\u05dc\u05d7\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 **{notify_service}**. \u05d4\u05d6\u05df \u05d0\u05d5\u05ea\u05d4 \u05dc\u05de\u05d8\u05d4:",
"title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4"
}
},
"title": "\u05dc\u05d4\u05d5\u05d3\u05d9\u05e2 \u200b\u200b\u05e2\u05dc \u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea"
},
"totp": {
"error": {
"invalid_code": "\u05e7\u05d5\u05d3 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d0\u05e0\u05d0 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1. \u05d0\u05dd \u05d0\u05ea\u05d4 \u05de\u05e7\u05d1\u05dc \u05d0\u05ea \u05d4\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d4\u05d6\u05d5 \u05d1\u05d0\u05d5\u05e4\u05df \u05e2\u05e7\u05d1\u05d9, \u05d5\u05d3\u05d0 \u05e9\u05d4\u05e9\u05e2\u05d5\u05df \u05e9\u05dc \u05de\u05e2\u05e8\u05db\u05ea \u05d4 - Home Assistant \u05e9\u05dc\u05da \u05de\u05d3\u05d5\u05d9\u05e7."
},
"step": {
"init": {
"description": "\u05db\u05d3\u05d9 \u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05e1\u05d9\u05e1\u05de\u05d0\u05d5\u05ea \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05d5\u05ea \u05de\u05d1\u05d5\u05e1\u05e1\u05d5\u05ea \u05d6\u05de\u05df, \u05e1\u05e8\u05d5\u05e7 \u05d0\u05ea \u05e7\u05d5\u05d3 QR \u05e2\u05dd \u05d9\u05d9\u05e9\u05d5\u05dd \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05dc\u05da. \u05d0\u05dd \u05d0\u05d9\u05df \u05dc\u05da \u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d6\u05d4, \u05d0\u05e0\u05d5 \u05de\u05de\u05dc\u05d9\u05e6\u05d9\u05dd \u05e2\u05dc [Google Authenticator] (https://support.google.com/accounts/answer/1066447) \u05d0\u05d5 [Authy] (https://authy.com/). \n\n {qr_code} \n \n \u05dc\u05d0\u05d7\u05e8 \u05e1\u05e8\u05d9\u05e7\u05ea \u05d4\u05e7\u05d5\u05d3, \u05d4\u05d6\u05df \u05d0\u05ea \u05d4\u05e7\u05d5\u05d3 \u05d1\u05df \u05e9\u05e9 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05de\u05d4\u05d0\u05e4\u05dc\u05d9\u05e7\u05e6\u05d9\u05d4 \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05d0\u05de\u05ea \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d4. \u05d0\u05dd \u05d0\u05ea\u05d4 \u05e0\u05ea\u05e7\u05dc \u05d1\u05d1\u05e2\u05d9\u05d5\u05ea \u05d1\u05e1\u05e8\u05d9\u05e7\u05ea \u05e7\u05d5\u05d3 QR, \u05d1\u05e6\u05e2 \u05d4\u05d2\u05d3\u05e8\u05d4 \u05d9\u05d3\u05e0\u05d9\u05ea \u05e2\u05dd \u05e7\u05d5\u05d3 **`{code}`**.",
"title": "\u05d4\u05d2\u05d3\u05e8 \u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,32 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Nincs el\u00e9rhet\u0151 \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1s."
},
"error": {
"invalid_code": "\u00c9rv\u00e9nytelen k\u00f3d, pr\u00f3b\u00e1ld \u00fajra."
},
"step": {
"init": {
"description": "V\u00e1lassz \u00e9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1st:"
},
"setup": {
"title": "Be\u00e1ll\u00edt\u00e1s ellen\u0151rz\u00e9se"
}
}
},
"totp": {
"error": {
"invalid_code": "\u00c9rv\u00e9nytelen k\u00f3d, pr\u00f3b\u00e1ld \u00fajra. Ha ez a hiba folyamatosan el\u0151fordul, akkor gy\u0151z\u0151dj meg r\u00f3la, hogy a Home Assistant rendszered \u00f3r\u00e1ja pontosan j\u00e1r."
},
"step": {
"init": {
"description": "Ahhoz, hogy haszn\u00e1lhasd a k\u00e9tfaktoros hiteles\u00edt\u00e9st id\u0151alap\u00fa egyszeri jelszavakkal, szkenneld be a QR k\u00f3dot a hiteles\u00edt\u00e9si applik\u00e1ci\u00f3ddal. Ha m\u00e9g nincsen, akkor a [Google Hiteles\u00edt\u0151](https://support.google.com/accounts/answer/1066447)t vagy az [Authy](https://authy.com/)-t aj\u00e1nljuk.\n\n{qr_code}\n\nA k\u00f3d beolvas\u00e1sa ut\u00e1n add meg a hat sz\u00e1mjegy\u0171 k\u00f3dot az applik\u00e1ci\u00f3b\u00f3l a telep\u00edt\u00e9s ellen\u0151rz\u00e9s\u00e9hez. Ha probl\u00e9m\u00e1ba \u00fctk\u00f6z\u00f6l a QR k\u00f3d beolvas\u00e1s\u00e1n\u00e1l, akkor ind\u00edts egy k\u00e9zi be\u00e1ll\u00edt\u00e1st a **`{code}`** k\u00f3ddal.",
"title": "K\u00e9tfaktoros hiteles\u00edt\u00e9s be\u00e1ll\u00edt\u00e1sa TOTP haszn\u00e1lat\u00e1val"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"mfa_setup": {
"totp": {
"error": {
"invalid_code": "Kode salah, coba lagi. Jika Anda mendapatkan kesalahan ini secara konsisten, pastikan jam pada sistem Home Assistant anda akurat."
},
"step": {
"init": {
"description": "Untuk mengaktifkan otentikasi dua faktor menggunakan password satu kali berbasis waktu, pindai kode QR dengan aplikasi otentikasi Anda. Jika Anda tidak memilikinya, kami menyarankan [Google Authenticator] (https://support.google.com/accounts/answer/1066447) atau [Authy] (https://authy.com/). \n\n {qr_code} \n \n Setelah memindai kode, masukkan kode enam digit dari aplikasi Anda untuk memverifikasi pengaturan. Jika Anda mengalami masalah saat memindai kode QR, lakukan pengaturan manual dengan kode ** ` {code} ` **.",
"title": "Siapkan otentikasi dua faktor menggunakan TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,13 @@
{
"mfa_setup": {
"totp": {
"step": {
"init": {
"description": "Per attivare l'autenticazione a due fattori utilizzando password monouso basate sul tempo, eseguire la scansione del codice QR con l'app di autenticazione. Se non ne hai uno, ti consigliamo [Google Authenticator] (https://support.google.com/accounts/answer/1066447) o [Authy] (https://authy.com/). \n\n {qr_code} \n \n Dopo aver scansionato il codice, inserisci il codice a sei cifre dalla tua app per verificare la configurazione. Se riscontri problemi con la scansione del codice QR, esegui una configurazione manuale con codice ** ` {code} ` **.",
"title": "Imposta l'autenticazione a due fattori usando TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc54c\ub9bc \uc11c\ube44\uc2a4\uac00 \uc5c6\uc2b5\ub2c8\ub2e4."
},
"error": {
"invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc\uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694."
},
"step": {
"init": {
"description": "\uc54c\ub9bc \uc11c\ube44\uc2a4 \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694:",
"title": "\uc54c\ub9bc \uad6c\uc131\uc694\uc18c\uac00 \uc81c\uacf5\ud558\ub294 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638 \uc124\uc815"
},
"setup": {
"description": "**notify.{notify_service}** \uc5d0\uc11c \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \ubcf4\ub0c8\uc2b5\ub2c8\ub2e4. \uc544\ub798\uc758 \uacf5\ub780\uc5d0 \uc785\ub825\ud574 \uc8fc\uc138\uc694:",
"title": "\uc124\uc815 \ud655\uc778"
}
},
"title": "\uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638 \uc54c\ub9bc"
},
"totp": {
"error": {
"invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uac04\uc124\uc815\uc774 \uc62c\ubc14\ub978\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694."
},
"step": {
"init": {
"description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google OTP](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.",
"title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2\ub2e8\uacc4 \uc778\uc99d \uad6c\uc131"
}
},
"title": "TOTP (\uc2dc\uac04 \uae30\ubc18 OTP)"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Keen Notifikatioun's D\u00e9ngscht disponibel."
},
"error": {
"invalid_code": "Ong\u00ebltege Code, prob\u00e9iert w.e.g. nach emol."
},
"step": {
"init": {
"description": "Wielt w.e.g. een Notifikatioun's D\u00e9ngscht aus:",
"title": "Eemolegt Passwuert ariichte wat vun engem Notifikatioun's Komponente versch\u00e9ckt g\u00ebtt"
},
"setup": {
"description": "Een eemolegt Passwuert ass vun **notify.{notify_service}** gesch\u00e9ckt ginn. Gitt et w.e.g hei \u00ebnnen dr\u00ebnner an:",
"title": "Astellungen iwwerpr\u00e9iwen"
}
},
"title": "Eemolegt Passwuert Notifikatioun"
},
"totp": {
"error": {
"invalid_code": "Ong\u00ebltege Login, prob\u00e9iert w.e.g. nach emol. Falls d\u00ebse Feeler Message \u00ebmmer er\u00ebm optr\u00ebtt dann iwwerpr\u00e9ift op d'Z\u00e4it vum Home Assistant System richteg ass."
},
"step": {
"init": {
"description": "Fir d'Zwee-Faktor-Authentifikatioun m\u00ebttels engem Z\u00e4it bas\u00e9ierten eemolege Passwuert z'aktiv\u00e9ieren, scannt de QR Code mat enger Authentifikatioun's App.\nFalls dir keng hutt, recommand\u00e9iere mir entweder [Google Authenticator](https://support.google.com/accounts/answer/1066447) oder [Authy](https://authy.com/).\n\n{qr_code}\n\nNodeems de Code gescannt ass, gitt de sechs stellege Code vun der App a fir d'Konfiguratioun z'iwwerpr\u00e9iwen. Am Fall vu Problemer fir de QR Code ze scannen, gitt de folgende Code **`{code}`** a fir ee manuelle Setup.",
"title": "Zwee Faktor Authentifikatioun mat TOTP konfigur\u00e9ieren"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Geen meldingsservices beschikbaar."
},
"error": {
"invalid_code": "Ongeldige code, probeer opnieuw."
},
"step": {
"init": {
"description": "Selecteer een van de meldingsdiensten:",
"title": "Stel een \u00e9\u00e9nmalig wachtwoord in dat wordt afgegeven door een meldingscomponent"
},
"setup": {
"description": "Een \u00e9\u00e9nmalig wachtwoord is verzonden via **notify. {notify_service}**. Voer het hieronder in:",
"title": "Controleer de instellingen"
}
},
"title": "Eenmalig wachtwoord melden"
},
"totp": {
"error": {
"invalid_code": "Ongeldige code, probeer het opnieuw. Als u deze fout blijft krijgen, controleer dan of de klok van uw Home Assistant systeem correct is ingesteld."
},
"step": {
"init": {
"description": "Voor het activeren van twee-factor-authenticatie via tijdgebonden eenmalige wachtwoorden: scan de QR code met uw authenticatie-app. Als u nog geen app heeft, adviseren we [Google Authenticator (https://support.google.com/accounts/answer/1066447) of [Authy](https://authy.com/).\n\n{qr_code}\n\nNa het scannen van de code voert u de zescijferige code uit uw app in om de instelling te controleren. Als u problemen heeft met het scannen van de QR-code, voert u een handmatige configuratie uit met code **`{code}`**.",
"title": "Configureer twee-factor-authenticatie via TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,16 @@
{
"mfa_setup": {
"totp": {
"error": {
"invalid_code": "Ugyldig kode, pr\u00f8v igjen. Dersom du heile tida f\u00e5r denne feilen, m\u00e5 du s\u00f8rge for at klokka p\u00e5 Home Assistant-systemet ditt er n\u00f8yaktig."
},
"step": {
"init": {
"description": "For \u00e5 aktivere tofaktorautentisering ved hjelp av tidsbaserte eingangspassord, skann QR-koden med autentiseringsappen din. Dersom du ikkje har ein, vil vi r\u00e5de deg til \u00e5 bruke anten [Google Authenticator] (https://support.google.com/accounts/answer/1066447) eller [Authy] (https://authy.com/). \n\n {qr_code} \n \nN\u00e5r du har skanna koda, skriv du inn den sekssifra koda fr\u00e5 appen din for \u00e5 stadfeste oppsettet. Dersom du har problemer med \u00e5 skanne QR-koda, gjer du eit manuelt oppsett med kode ** ` {code} ` **.",
"title": "Konfigurer to-faktor-autentisering ved hjelp av TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Ingen varslingstjenester er tilgjengelig."
},
"error": {
"invalid_code": "Ugyldig kode, vennligst pr\u00f8v igjen."
},
"step": {
"init": {
"description": "Vennligst velg en av varslingstjenestene:",
"title": "Sett opp engangspassord levert av varsel komponent"
},
"setup": {
"description": "Et engangspassord har blitt sendt via **notify.{notify_service}**. Vennligst skriv det inn nedenfor:",
"title": "Bekreft oppsettet"
}
},
"title": "Varsle engangspassord"
},
"totp": {
"error": {
"invalid_code": "Ugyldig kode, pr\u00f8v igjen. Hvis du f\u00e5r denne feilen konsekvent, m\u00e5 du s\u00f8rge for at klokken p\u00e5 Home Assistant systemet er riktig."
},
"step": {
"init": {
"description": "For \u00e5 aktivere tofaktorautentisering ved hjelp av tidsbaserte engangspassord, skann QR-koden med autentiseringsappen din. Hvis du ikke har en, kan vi anbefale enten [Google Authenticator](https://support.google.com/accounts/answer/1066447) eller [Authy](https://authy.com/). \n\n {qr_code} \n \nEtter at du har skannet koden, skriver du inn den seks-sifrede koden fra appen din for \u00e5 kontrollere oppsettet. Dersom du har problemer med \u00e5 skanne QR-koden kan du taste inn f\u00f8lgende kode manuelt: **`{code}`**.",
"title": "Konfigurer tofaktorautentisering ved hjelp av TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Brak dost\u0119pnych us\u0142ug powiadamiania."
},
"error": {
"invalid_code": "Nieprawid\u0142owy kod, spr\u00f3buj ponownie."
},
"step": {
"init": {
"description": "Prosz\u0119 wybra\u0107 jedn\u0105 z us\u0142ugi powiadamiania:",
"title": "Skonfiguruj has\u0142o jednorazowe dostarczone przez komponent powiadomie\u0144"
},
"setup": {
"description": "Has\u0142o jednorazowe zosta\u0142o wys\u0142ane przez **notify.{notify_service}**. Wpisz je poni\u017cej:",
"title": "Sprawd\u017a konfiguracj\u0119"
}
},
"title": "Powiadomienie z has\u0142em jednorazowym"
},
"totp": {
"error": {
"invalid_code": "Nieprawid\u0142owy kod, spr\u00f3buj ponownie. Je\u015bli b\u0142\u0105d b\u0119dzie si\u0119 powtarza\u0142, upewnij si\u0119, \u017ce czas zegara systemu Home Assistant jest prawid\u0142owy."
},
"step": {
"init": {
"description": "Aby aktywowa\u0107 uwierzytelnianie dwusk\u0142adnikowe przy u\u017cyciu jednorazowych hase\u0142 opartych na czasie, zeskanuj kod QR za pomoc\u0105 aplikacji uwierzytelniaj\u0105cej. Je\u015bli jej nie masz, polecamy [Google Authenticator](https://support.google.com/accounts/answer/1066447) lub [Authy](https://authy.com/).\n\n{qr_code} \n \nPo zeskanowaniu kodu wprowad\u017a sze\u015bciocyfrowy kod z aplikacji, aby zweryfikowa\u0107 konfiguracj\u0119. Je\u015bli masz problemy z zeskanowaniem kodu QR, wykonaj r\u0119czn\u0105 konfiguracj\u0119 z kodem **`{code}`**.",
"title": "Skonfiguruj uwierzytelnianie dwusk\u0142adnikowe za pomoc\u0105 hase\u0142 jednorazowych opartych na czasie"
}
},
"title": "Has\u0142a jednorazowe oparte na czasie"
}
}
}

View File

@@ -0,0 +1,15 @@
{
"mfa_setup": {
"totp": {
"error": {
"invalid_code": "C\u00f3digo inv\u00e1lido, por favor tente novamente. Se voc\u00ea obtiver este erro de forma consistente, certifique-se de que o rel\u00f3gio do sistema Home Assistant esteja correto."
},
"step": {
"init": {
"title": "Configure a autentica\u00e7\u00e3o de dois fatores usando o TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Nenhum servi\u00e7o de notifica\u00e7\u00e3o dispon\u00edvel."
},
"error": {
"invalid_code": "C\u00f3digo inv\u00e1lido, por favor tente novamente."
},
"step": {
"init": {
"description": "Por favor, selecione um dos servi\u00e7os de notifica\u00e7\u00e3o:",
"title": "Configurar uma palavra passe entregue pela componente de notifica\u00e7\u00e3o"
},
"setup": {
"description": "Foi enviada uma palavra passe atrav\u00e9s de **notify.{notify_service}**. Por favor, insira-a:",
"title": "Verificar a configura\u00e7\u00e3o"
}
},
"title": "Notificar palavra passe de uso \u00fanico"
},
"totp": {
"error": {
"invalid_code": "C\u00f3digo inv\u00e1lido, por favor, tente novamente. Se receber este erro constantemente, por favor, certifique-se de que o rel\u00f3gio do sistema que hospeda o Home Assistent \u00e9 preciso."
},
"step": {
"init": {
"description": "Para ativar a autentica\u00e7\u00e3o com dois fatores utilizando passwords unicas temporais (OTP), ler o c\u00f3digo QR com a sua aplica\u00e7\u00e3o de autentica\u00e7\u00e3o. Se voc\u00ea n\u00e3o tiver uma, recomendamos [Google Authenticator](https://support.google.com/accounts/answer/1066447) ou [Authy](https://authy.com/).\n\n{qr_code}\n\nDepois de ler o c\u00f3digo, introduza o c\u00f3digo de seis d\u00edgitos fornecido pela sua aplica\u00e7\u00e3o para verificar a configura\u00e7\u00e3o. Se tiver problemas a ler o c\u00f3digo QR, fa\u00e7a uma configura\u00e7\u00e3o manual com o c\u00f3digo **`{c\u00f3digo}`**.",
"title": "Configurar autentica\u00e7\u00e3o com dois fatores usando TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,34 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Nu sunt disponibile servicii de notificare."
},
"error": {
"invalid_code": "Cod invalid, va rugam incercati din nou."
},
"step": {
"init": {
"description": "Selecta\u021bi unul dintre serviciile de notificare:",
"title": "Configura\u021bi o parol\u0103 unic\u0103 livrat\u0103 de o component\u0103 de notificare"
},
"setup": {
"description": "O parol\u0103 unic\u0103 a fost trimis\u0103 prin **notify.{notify_service}**. Introduce\u021bi parola mai jos:",
"title": "Verifica\u021bi configurarea"
}
},
"title": "Notifica\u021bi o parol\u0103 unic\u0103"
},
"totp": {
"error": {
"invalid_code": "Cod invalid, va rugam incercati din nou. Dac\u0103 primi\u021bi aceast\u0103 eroare \u00een mod consecvent, asigura\u021bi-v\u0103 c\u0103 ceasul sistemului dvs. Home Assistant este corect."
},
"step": {
"init": {
"title": "Configura\u021bi autentificarea cu doi factori utiliz\u00e2nd TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0441\u043b\u0443\u0436\u0431 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439."
},
"error": {
"invalid_code": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443."
},
"step": {
"init": {
"description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0434\u043d\u0443 \u0438\u0437 \u0441\u043b\u0443\u0436\u0431 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439:",
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439"
},
"setup": {
"description": "\u041e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u0447\u0435\u0440\u0435\u0437 **notify.{notify_service}**. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0438\u0436\u0435:",
"title": "\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443"
}
},
"title": "\u0414\u043e\u0441\u0442\u0430\u0432\u043a\u0430 \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439"
},
"totp": {
"error": {
"invalid_code": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u0434. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430. \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u043e\u0448\u0438\u0431\u043a\u0443, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0447\u0430\u0441\u044b \u0432 \u0432\u0430\u0448\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 Home Assistant \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f."
},
"step": {
"init": {
"description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator](https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy](https://authy.com/). \n\n {qr_code} \n \n\u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0434\u0430 **`{code}`**.",
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Ni na voljo storitev obve\u0161\u010danja."
},
"error": {
"invalid_code": "Neveljavna koda, poskusite znova."
},
"step": {
"init": {
"description": "Prosimo, izberite eno od storitev obve\u0161\u010danja:",
"title": "Nastavite enkratno geslo, ki ga dostavite z obvestilno komponento"
},
"setup": {
"description": "Enkratno geslo je poslal **notify.{notify_service} **. Vnesite ga spodaj:",
"title": "Preverite nastavitev"
}
},
"title": "Obvesti Enkratno Geslo"
},
"totp": {
"error": {
"invalid_code": "Neveljavna koda, prosimo, poskusite znova. \u010ce dobite to sporo\u010dilo ve\u010dkrat, prosimo poskrbite, da bo ura va\u0161ega Home Assistenta to\u010dna."
},
"step": {
"init": {
"description": "\u010ce \u017eelite aktivirati preverjanje pristnosti dveh faktorjev z enkratnimi gesli, ki temeljijo na \u010dasu, skenirajte kodo QR s svojo aplikacijo za preverjanje pristnosti. \u010ce je nimate, priporo\u010damo bodisi [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ali [Authy] (https://authy.com/). \n\n {qr_code} \n \n Po skeniranju kode vnesite \u0161estmestno kodo iz aplikacije, da preverite nastavitev. \u010ce imate te\u017eave pri skeniranju kode QR, naredite ro\u010dno nastavitev s kodo ** ` {code} ` **.",
"title": "Nastavite dvofaktorsko avtentifikacijo s pomo\u010djo TOTP-ja"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "Inga tillg\u00e4ngliga meddelande tj\u00e4nster."
},
"error": {
"invalid_code": "Ogiltig kod, var god f\u00f6rs\u00f6k igen."
},
"step": {
"init": {
"description": "Var god v\u00e4lj en av notifieringstj\u00e4nsterna:",
"title": "Konfigurera ett eng\u00e5ngsl\u00f6senord levererat genom notifieringskomponenten"
},
"setup": {
"description": "Ett eng\u00e5ngsl\u00f6senord har skickats av **notify.{notify_service}**. V\u00e4nligen ange det nedan:",
"title": "Verifiera inst\u00e4llningen"
}
},
"title": "Meddela eng\u00e5ngsl\u00f6senord"
},
"totp": {
"error": {
"invalid_code": "Ogiltig kod, f\u00f6rs\u00f6k igen. Om du flera g\u00e5nger i rad f\u00e5r detta fel, se till att klockan i din Home Assistant \u00e4r korrekt inst\u00e4lld."
},
"step": {
"init": {
"description": "F\u00f6r att aktivera tv\u00e5faktorsautentisering som anv\u00e4nder tidsbaserade eng\u00e5ngsl\u00f6senord, skanna QR-koden med din autentiseringsapp. Om du inte har en, rekommenderar vi antingen [Google Authenticator] (https://support.google.com/accounts/answer/1066447) eller [Authy] (https://authy.com/). \n\n{qr_code} \n\nN\u00e4r du har skannat koden anger du den sexsiffriga koden fr\u00e5n din app f\u00f6r att verifiera inst\u00e4llningen. Om du har problem med att skanna QR-koden, g\u00f6r en manuell inst\u00e4llning med kod ** ` {code} ` **.",
"title": "St\u00e4ll in tv\u00e5faktorsautentisering med TOTP"
}
},
"title": "TOTP"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"mfa_setup": {
"notify": {
"error": {
"invalid_code": "\u041d\u0435\u0432\u0456\u0440\u043d\u0438\u0439 \u043a\u043e\u0434, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0449\u0435 \u0440\u0430\u0437."
}
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\u6ca1\u6709\u53ef\u7528\u7684\u901a\u77e5\u670d\u52a1\u3002"
},
"error": {
"invalid_code": "\u4ee3\u7801\u65e0\u6548\uff0c\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002"
},
"step": {
"init": {
"description": "\u8bf7\u4ece\u4e0b\u9762\u9009\u62e9\u4e00\u4e2a\u901a\u77e5\u670d\u52a1\uff1a",
"title": "\u8bbe\u7f6e\u7531\u901a\u77e5\u7ec4\u4ef6\u4f20\u9012\u7684\u4e00\u6b21\u6027\u5bc6\u7801"
},
"setup": {
"description": "\u4e00\u6b21\u6027\u5bc6\u7801\u5df2\u7531 **notify.{notify_service}** \u53d1\u9001\u3002\u8bf7\u5728\u4e0b\u9762\u8f93\u5165\uff1a",
"title": "\u9a8c\u8bc1\u8bbe\u7f6e"
}
},
"title": "\u4e00\u6b21\u6027\u5bc6\u7801\u901a\u77e5"
},
"totp": {
"error": {
"invalid_code": "\u53e3\u4ee4\u65e0\u6548\uff0c\u8bf7\u91cd\u65b0\u8f93\u5165\u3002\u5982\u679c\u9519\u8bef\u53cd\u590d\u51fa\u73b0\uff0c\u8bf7\u786e\u4fdd Home Assistant \u7cfb\u7edf\u7684\u65f6\u95f4\u51c6\u786e\u65e0\u8bef\u3002"
},
"step": {
"init": {
"description": "\u8981\u6fc0\u6d3b\u57fa\u4e8e\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4\u7684\u53cc\u91cd\u8ba4\u8bc1\uff0c\u8bf7\u7528\u8eab\u4efd\u9a8c\u8bc1\u5e94\u7528\u626b\u63cf\u4ee5\u4e0b\u4e8c\u7ef4\u7801\u3002\u5982\u679c\u60a8\u8fd8\u6ca1\u6709\u8eab\u4efd\u9a8c\u8bc1\u5e94\u7528\uff0c\u63a8\u8350\u4f7f\u7528 [Google \u8eab\u4efd\u9a8c\u8bc1\u5668](https://support.google.com/accounts/answer/1066447) \u6216 [Authy](https://authy.com/)\u3002\n\n{qr_code}\n\n\u626b\u63cf\u4e8c\u7ef4\u7801\u4ee5\u540e\uff0c\u8f93\u5165\u5e94\u7528\u4e0a\u7684\u516d\u4f4d\u6570\u5b57\u53e3\u4ee4\u6765\u9a8c\u8bc1\u914d\u7f6e\u3002\u5982\u679c\u5728\u626b\u63cf\u4e8c\u7ef4\u7801\u65f6\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u4f7f\u7528\u4ee3\u7801 **`{code}`** \u624b\u52a8\u914d\u7f6e\u3002",
"title": "\u7528\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4\u8bbe\u7f6e\u53cc\u91cd\u8ba4\u8bc1"
}
},
"title": "\u65f6\u95f4\u52a8\u6001\u53e3\u4ee4"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\u6c92\u6709\u53ef\u7528\u7684\u901a\u77e5\u670d\u52d9\u3002"
},
"error": {
"invalid_code": "\u9a57\u8b49\u78bc\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002"
},
"step": {
"init": {
"description": "\u8acb\u9078\u64c7\u4e00\u9805\u901a\u77e5\u670d\u52d9\uff1a",
"title": "\u8a2d\u5b9a\u4e00\u6b21\u6027\u5bc6\u78bc\u50b3\u9001\u7d44\u4ef6"
},
"setup": {
"description": "\u4e00\u6b21\u6027\u5bc6\u78bc\u5df2\u900f\u904e **notify.{notify_service}** \u50b3\u9001\u3002\u8acb\u65bc\u4e0b\u65b9\u8f38\u5165\uff1a",
"title": "\u9a57\u8b49\u8a2d\u5b9a"
}
},
"title": "\u901a\u77e5\u4e00\u6b21\u6027\u5bc6\u78bc"
},
"totp": {
"error": {
"invalid_code": "\u9a57\u8b49\u78bc\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002\u5047\u5982\u932f\u8aa4\u6301\u7e8c\u767c\u751f\uff0c\u8acb\u5148\u78ba\u5b9a\u60a8\u7684 Home Assistant \u7cfb\u7d71\u4e0a\u7684\u6642\u9593\u8a2d\u5b9a\u6b63\u78ba\u5f8c\uff0c\u518d\u8a66\u4e00\u6b21\u3002"
},
"step": {
"init": {
"description": "\u6b32\u555f\u7528\u4e00\u6b21\u6027\u4e14\u5177\u6642\u6548\u6027\u7684\u5bc6\u78bc\u4e4b\u5169\u6b65\u9a5f\u9a57\u8b49\u529f\u80fd\uff0c\u8acb\u4f7f\u7528\u60a8\u7684\u9a57\u8b49 App \u6383\u7784\u4e0b\u65b9\u7684 QR code \u3002\u5018\u82e5\u60a8\u5c1a\u672a\u5b89\u88dd\u4efb\u4f55 App\uff0c\u63a8\u85a6\u60a8\u4f7f\u7528 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \u6216 [Authy](https://authy.com/)\u3002\n\n{qr_code}\n\n\u65bc\u641c\u5c0b\u4e4b\u5f8c\uff0c\u8f38\u5165 App \u4e2d\u7684\u516d\u4f4d\u6578\u5b57\u9032\u884c\u8a2d\u5b9a\u9a57\u8b49\u3002\u5047\u5982\u641c\u5c0b\u51fa\u73fe\u554f\u984c\uff0c\u8acb\u624b\u52d5\u8f38\u5165\u4ee5\u4e0b\u9a57\u8b49\u78bc **`{code}`**\u3002",
"title": "\u4f7f\u7528 TOTP \u8a2d\u5b9a\u5169\u6b65\u9a5f\u9a57\u8b49"
}
},
"title": "TOTP"
}
}
}

View File

@@ -12,6 +12,7 @@ be in JSON as it's more readable.
Exchange the authorization code retrieved from the login flow for tokens.
{
"client_id": "https://hassbian.local:8123/",
"grant_type": "authorization_code",
"code": "411ee2f916e648d691e937ae9344681e"
}
@@ -32,6 +33,7 @@ token.
Request a new access token using a refresh token.
{
"client_id": "https://hassbian.local:8123/",
"grant_type": "refresh_token",
"refresh_token": "IJKLMNOPQRST"
}
@@ -44,21 +46,98 @@ a limited expiration.
"expires_in": 1800,
"token_type": "Bearer"
}
## Revoking a refresh token
It is also possible to revoke a refresh token and all access tokens that have
ever been granted by that refresh token. Response code will ALWAYS be 200.
{
"token": "IJKLMNOPQRST",
"action": "revoke"
}
# Websocket API
## Get current user
Send websocket command `auth/current_user` will return current user of the
active websocket connection.
{
"id": 10,
"type": "auth/current_user",
}
The result payload likes
{
"id": 10,
"type": "result",
"success": true,
"result": {
"id": "USER_ID",
"name": "John Doe",
"is_owner': true,
"credentials": [
{
"auth_provider_type": "homeassistant",
"auth_provider_id": null
}
],
"mfa_modules": [
{
"id": "totp",
"name": "TOTP",
"enabled": true,
}
]
}
}
## Create a long-lived access token
Send websocket command `auth/long_lived_access_token` will create
a long-lived access token for current user. Access token will not be saved in
Home Assistant. User need to record the token in secure place.
{
"id": 11,
"type": "auth/long_lived_access_token",
"client_name": "GPS Logger",
"lifespan": 365
}
Result will be a long-lived access token:
{
"id": 11,
"type": "result",
"success": true,
"result": "ABCDEFGH"
}
"""
import logging
import uuid
from datetime import timedelta
from aiohttp import web
import voluptuous as vol
from homeassistant.auth.models import User, Credentials, \
TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN
from homeassistant.components import websocket_api
from homeassistant.components.http import KEY_REAL_IP
from homeassistant.components.http.ban import log_invalid_auth
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import callback
from homeassistant.core import callback, HomeAssistant
from homeassistant.util import dt as dt_util
from . import indieauth
from . import login_flow
from . import mfa_setup_flow
DOMAIN = 'auth'
DEPENDENCIES = ['http']
@@ -68,37 +147,78 @@ SCHEMA_WS_CURRENT_USER = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_CURRENT_USER,
})
WS_TYPE_LONG_LIVED_ACCESS_TOKEN = 'auth/long_lived_access_token'
SCHEMA_WS_LONG_LIVED_ACCESS_TOKEN = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_LONG_LIVED_ACCESS_TOKEN,
vol.Required('lifespan'): int, # days
vol.Required('client_name'): str,
vol.Optional('client_icon'): str,
})
WS_TYPE_REFRESH_TOKENS = 'auth/refresh_tokens'
SCHEMA_WS_REFRESH_TOKENS = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_REFRESH_TOKENS,
})
WS_TYPE_DELETE_REFRESH_TOKEN = 'auth/delete_refresh_token'
SCHEMA_WS_DELETE_REFRESH_TOKEN = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_DELETE_REFRESH_TOKEN,
vol.Required('refresh_token_id'): str,
})
RESULT_TYPE_CREDENTIALS = 'credentials'
RESULT_TYPE_USER = 'user'
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass, config):
"""Component to allow users to login."""
store_credentials, retrieve_credentials = _create_cred_store()
store_result, retrieve_result = _create_auth_code_store()
hass.http.register_view(GrantTokenView(retrieve_credentials))
hass.http.register_view(LinkUserView(retrieve_credentials))
hass.http.register_view(TokenView(retrieve_result))
hass.http.register_view(LinkUserView(retrieve_result))
hass.components.websocket_api.async_register_command(
WS_TYPE_CURRENT_USER, websocket_current_user,
SCHEMA_WS_CURRENT_USER
)
hass.components.websocket_api.async_register_command(
WS_TYPE_LONG_LIVED_ACCESS_TOKEN,
websocket_create_long_lived_access_token,
SCHEMA_WS_LONG_LIVED_ACCESS_TOKEN
)
hass.components.websocket_api.async_register_command(
WS_TYPE_REFRESH_TOKENS,
websocket_refresh_tokens,
SCHEMA_WS_REFRESH_TOKENS
)
hass.components.websocket_api.async_register_command(
WS_TYPE_DELETE_REFRESH_TOKEN,
websocket_delete_refresh_token,
SCHEMA_WS_DELETE_REFRESH_TOKEN
)
await login_flow.async_setup(hass, store_credentials)
await login_flow.async_setup(hass, store_result)
await mfa_setup_flow.async_setup(hass)
return True
class GrantTokenView(HomeAssistantView):
"""View to grant tokens."""
class TokenView(HomeAssistantView):
"""View to issue or revoke 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
def __init__(self, retrieve_user):
"""Initialize the token view."""
self._retrieve_user = retrieve_user
@log_invalid_auth
async def post(self, request):
@@ -108,17 +228,45 @@ class GrantTokenView(HomeAssistantView):
grant_type = data.get('grant_type')
# IndieAuth 6.3.5
# The revocation endpoint is the same as the token endpoint.
# The revocation request includes an additional parameter,
# action=revoke.
if data.get('action') == 'revoke':
return await self._async_handle_revoke_token(hass, data)
if grant_type == 'authorization_code':
return await self._async_handle_auth_code(hass, data)
return await self._async_handle_auth_code(
hass, data, str(request[KEY_REAL_IP]))
if grant_type == 'refresh_token':
return await self._async_handle_refresh_token(hass, data)
return await self._async_handle_refresh_token(
hass, data, str(request[KEY_REAL_IP]))
return self.json({
'error': 'unsupported_grant_type',
}, status_code=400)
async def _async_handle_auth_code(self, hass, data):
async def _async_handle_revoke_token(self, hass, data):
"""Handle revoke token request."""
# OAuth 2.0 Token Revocation [RFC7009]
# 2.2 The authorization server responds with HTTP status code 200
# if the token has been revoked successfully or if the client
# submitted an invalid token.
token = data.get('token')
if token is None:
return web.Response(status=200)
refresh_token = await hass.auth.async_get_refresh_token_by_token(token)
if refresh_token is None:
return web.Response(status=200)
await hass.auth.async_remove_refresh_token(refresh_token)
return web.Response(status=200)
async def _async_handle_auth_code(self, hass, data, remote_addr):
"""Handle authorization code request."""
client_id = data.get('client_id')
if client_id is None or not indieauth.verify_client_id(client_id):
@@ -132,17 +280,19 @@ class GrantTokenView(HomeAssistantView):
if code is None:
return self.json({
'error': 'invalid_request',
'error_description': 'Invalid code',
}, status_code=400)
credentials = self._retrieve_credentials(client_id, code)
user = self._retrieve_user(client_id, RESULT_TYPE_USER, code)
if credentials is None:
if user is None or not isinstance(user, User):
return self.json({
'error': 'invalid_request',
'error_description': 'Invalid code',
}, status_code=400)
user = await hass.auth.async_get_or_create_user(credentials)
# refresh user
user = await hass.auth.async_get_user(user.id)
if not user.is_active:
return self.json({
@@ -152,17 +302,18 @@ class GrantTokenView(HomeAssistantView):
refresh_token = await hass.auth.async_create_refresh_token(user,
client_id)
access_token = hass.auth.async_create_access_token(refresh_token)
access_token = hass.auth.async_create_access_token(
refresh_token, remote_addr)
return self.json({
'access_token': access_token.token,
'access_token': access_token,
'token_type': 'Bearer',
'refresh_token': refresh_token.token,
'expires_in':
int(refresh_token.access_token_expiration.total_seconds()),
})
async def _async_handle_refresh_token(self, hass, data):
async def _async_handle_refresh_token(self, hass, data, remote_addr):
"""Handle authorization code request."""
client_id = data.get('client_id')
if client_id is not None and not indieauth.verify_client_id(client_id):
@@ -178,7 +329,7 @@ class GrantTokenView(HomeAssistantView):
'error': 'invalid_request',
}, status_code=400)
refresh_token = await hass.auth.async_get_refresh_token(token)
refresh_token = await hass.auth.async_get_refresh_token_by_token(token)
if refresh_token is None:
return self.json({
@@ -190,10 +341,11 @@ class GrantTokenView(HomeAssistantView):
'error': 'invalid_request',
}, status_code=400)
access_token = hass.auth.async_create_access_token(refresh_token)
access_token = hass.auth.async_create_access_token(
refresh_token, remote_addr)
return self.json({
'access_token': access_token.token,
'access_token': access_token,
'token_type': 'Bearer',
'expires_in':
int(refresh_token.access_token_expiration.total_seconds()),
@@ -220,7 +372,7 @@ class LinkUserView(HomeAssistantView):
user = request['hass_user']
credentials = self._retrieve_credentials(
data['client_id'], data['code'])
data['client_id'], RESULT_TYPE_CREDENTIALS, data['code'])
if credentials is None:
return self.json_message('Invalid code', status_code=400)
@@ -230,54 +382,134 @@ class LinkUserView(HomeAssistantView):
@callback
def _create_cred_store():
"""Create a credential store."""
temp_credentials = {}
def _create_auth_code_store():
"""Create an in memory store."""
temp_results = {}
@callback
def store_credentials(client_id, credentials):
"""Store credentials and return a code to retrieve it."""
def store_result(client_id, result):
"""Store flow result and return a code to retrieve it."""
if isinstance(result, User):
result_type = RESULT_TYPE_USER
elif isinstance(result, Credentials):
result_type = RESULT_TYPE_CREDENTIALS
else:
raise ValueError('result has to be either User or Credentials')
code = uuid.uuid4().hex
temp_credentials[(client_id, code)] = (dt_util.utcnow(), credentials)
temp_results[(client_id, result_type, code)] = \
(dt_util.utcnow(), result_type, result)
return code
@callback
def retrieve_credentials(client_id, code):
"""Retrieve credentials."""
key = (client_id, code)
def retrieve_result(client_id, result_type, code):
"""Retrieve flow result."""
key = (client_id, result_type, code)
if key not in temp_credentials:
if key not in temp_results:
return None
created, credentials = temp_credentials.pop(key)
created, _, result = temp_results.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 result
return None
return store_credentials, retrieve_credentials
return store_result, retrieve_result
@websocket_api.ws_require_user()
@callback
def websocket_current_user(hass, connection, msg):
def websocket_current_user(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Return the current user."""
user = connection.request.get('hass_user')
async def async_get_current_user(user):
"""Get current user."""
enabled_modules = await hass.auth.async_get_enabled_mfa(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.send_message(
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],
'mfa_modules': [{
'id': module.id,
'name': module.name,
'enabled': module.id in enabled_modules,
} for module in hass.auth.auth_mfa_modules],
}))
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]
}))
hass.async_create_task(async_get_current_user(connection.user))
@websocket_api.ws_require_user()
@callback
def websocket_create_long_lived_access_token(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Create or a long-lived access token."""
async def async_create_long_lived_access_token(user):
"""Create or a long-lived access token."""
refresh_token = await hass.auth.async_create_refresh_token(
user,
client_name=msg['client_name'],
client_icon=msg.get('client_icon'),
token_type=TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN,
access_token_expiration=timedelta(days=msg['lifespan']))
access_token = hass.auth.async_create_access_token(
refresh_token)
connection.send_message(
websocket_api.result_message(msg['id'], access_token))
hass.async_create_task(
async_create_long_lived_access_token(connection.user))
@websocket_api.ws_require_user()
@callback
def websocket_refresh_tokens(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Return metadata of users refresh tokens."""
current_id = connection.refresh_token_id
connection.send_message(websocket_api.result_message(msg['id'], [{
'id': refresh.id,
'client_id': refresh.client_id,
'client_name': refresh.client_name,
'client_icon': refresh.client_icon,
'type': refresh.token_type,
'created_at': refresh.created_at,
'is_current': refresh.id == current_id,
'last_used_at': refresh.last_used_at,
'last_used_ip': refresh.last_used_ip,
} for refresh in connection.user.refresh_tokens.values()]))
@websocket_api.ws_require_user()
@callback
def websocket_delete_refresh_token(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Handle a delete refresh token request."""
async def async_delete_refresh_token(user, refresh_token_id):
"""Delete a refresh token."""
refresh_token = connection.user.refresh_tokens.get(refresh_token_id)
if refresh_token is None:
return websocket_api.error_message(
msg['id'], 'invalid_token_id', 'Received invalid token')
await hass.auth.async_remove_refresh_token(refresh_token)
connection.send_message(
websocket_api.result_message(msg['id'], {}))
hass.async_create_task(
async_delete_refresh_token(connection.user, msg['refresh_token_id']))

View File

@@ -1,22 +1,16 @@
"""Helpers to resolve client ID/secret."""
from ipaddress import ip_address, ip_network
from urllib.parse import urlparse
import asyncio
from ipaddress import ip_address
from html.parser import HTMLParser
from urllib.parse import urlparse, urljoin
# IP addresses of loopback interfaces
ALLOWED_IPS = (
ip_address('127.0.0.1'),
ip_address('::1'),
)
import aiohttp
from aiohttp.client_exceptions import ClientError
# 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'),
)
from homeassistant.util.network import is_local
def verify_redirect_uri(client_id, redirect_uri):
async def verify_redirect_uri(hass, client_id, redirect_uri):
"""Verify that the client and redirect uri match."""
try:
client_id_parts = _parse_client_id(client_id)
@@ -25,16 +19,74 @@ def verify_redirect_uri(client_id, redirect_uri):
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 (
is_valid = (
client_id_parts.scheme == redirect_parts.scheme and
client_id_parts.netloc == redirect_parts.netloc
)
if is_valid:
return True
# 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`.
redirect_uris = await fetch_redirect_uris(hass, client_id)
return redirect_uri in redirect_uris
class LinkTagParser(HTMLParser):
"""Parser to find link tags."""
def __init__(self, rel):
"""Initialize a link tag parser."""
super().__init__()
self.rel = rel
self.found = []
def handle_starttag(self, tag, attrs):
"""Handle finding a start tag."""
if tag != 'link':
return
attrs = dict(attrs)
if attrs.get('rel') == self.rel:
self.found.append(attrs.get('href'))
async def fetch_redirect_uris(hass, url):
"""Find link tag with redirect_uri values.
IndieAuth 4.2.2
The client SHOULD publish one or more <link> tags or Link HTTP headers with
a rel attribute of redirect_uri at the client_id URL.
We limit to the first 10kB of the page.
We do not implement extracting redirect uris from headers.
"""
parser = LinkTagParser('redirect_uri')
chunks = 0
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=5) as resp:
async for data in resp.content.iter_chunked(1024):
parser.feed(data.decode())
chunks += 1
if chunks == 10:
break
except (asyncio.TimeoutError, ClientError):
pass
# Authorization endpoints verifying that a redirect_uri is allowed for use
# by a client MUST look for an exact match of the given redirect_uri in the
# request against the list of redirect_uris discovered after resolving any
# relative URLs.
return [urljoin(url, found) for found in parser.found]
def verify_client_id(client_id):
"""Verify that the client id is valid."""
@@ -122,9 +174,7 @@ def _parse_client_id(client_id):
# Not an ip address
pass
if (address is None or
address in ALLOWED_IPS or
any(address in network for network in ALLOWED_NETWORKS)):
if address is None or is_local(address):
return parts
raise ValueError('Hostname should be a domain name or local IP address')

View File

@@ -22,10 +22,14 @@ 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.
And optional parameter 'type' has to set as 'link_user' if login flow used for
link credential to exist user. Default 'type' is 'authorize'.
{
"client_id": "https://hassbian.local:8123/",
"handler": ["local_provider", null],
"redirect_url": "https://hassbian.local:8123/"
"redirect_url": "https://hassbian.local:8123/",
"type': "authorize"
}
Return value will be a step in a data entry flow. See the docs for data entry
@@ -49,36 +53,37 @@ flow for details.
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.
The authorization code associated with an authorized user by default, it will
associate with an credential if "type" set to "link_user" in
"/auth/login_flow"
{
"flow_id": "8f7e42faab604bcab7ac43c44ca34d58",
"handler": ["insecure_example", null],
"result": "411ee2f916e648d691e937ae9344681e",
"source": "user",
"title": "Example",
"type": "create_entry",
"version": 1
}
"""
import aiohttp.web
from aiohttp import web
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.http import KEY_REAL_IP
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):
async def async_setup(hass, store_result):
"""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))
LoginFlowResourceView(hass.auth.login_flow, store_result))
class AuthProvidersView(HomeAssistantView):
@@ -90,53 +95,106 @@ class AuthProvidersView(HomeAssistantView):
async def get(self, request):
"""Get available auth providers."""
hass = request.app['hass']
if not hass.components.onboarding.async_is_onboarded():
return self.json_message(
message='Onboarding not finished',
status_code=400,
message_code='onboarding_required'
)
return self.json([{
'name': provider.name,
'id': provider.id,
'type': provider.type,
} for provider in request.app['hass'].auth.auth_providers])
} for provider in hass.auth.auth_providers])
class LoginFlowIndexView(FlowManagerIndexView):
def _prepare_result_json(result):
"""Convert result to JSON."""
if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
data = result.copy()
data.pop('result')
data.pop('data')
return data
if result['type'] != data_entry_flow.RESULT_TYPE_FORM:
return result
import voluptuous_serialize
data = result.copy()
schema = data['data_schema']
if schema is None:
data['data_schema'] = []
else:
data['data_schema'] = voluptuous_serialize.convert(schema)
return data
class LoginFlowIndexView(HomeAssistantView):
"""View to create a config flow."""
url = '/auth/login_flow'
name = 'api:auth:login_flow'
requires_auth = False
def __init__(self, flow_mgr):
"""Initialize the flow manager index view."""
self._flow_mgr = flow_mgr
async def get(self, request):
"""Do not allow index of flows in progress."""
return aiohttp.web.Response(status=405)
return web.Response(status=405)
@RequestDataValidator(vol.Schema({
vol.Required('client_id'): str,
vol.Required('handler'): vol.Any(str, list),
vol.Required('redirect_uri'): str,
vol.Optional('type', default='authorize'): 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']):
if not await indieauth.verify_redirect_uri(
request.app['hass'], 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)
if isinstance(data['handler'], list):
handler = tuple(data['handler'])
else:
handler = data['handler']
try:
result = await self._flow_mgr.async_init(
handler, context={
'ip_address': request[KEY_REAL_IP],
'credential_only': data.get('type') == 'link_user',
})
except data_entry_flow.UnknownHandler:
return self.json_message('Invalid handler specified', 404)
except data_entry_flow.UnknownStep:
return self.json_message('Handler does not support init', 400)
return self.json(_prepare_result_json(result))
class LoginFlowResourceView(FlowManagerResourceView):
class LoginFlowResourceView(HomeAssistantView):
"""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):
def __init__(self, flow_mgr, store_result):
"""Initialize the login flow resource view."""
super().__init__(flow_mgr)
self._store_credentials = store_credentials
self._flow_mgr = flow_mgr
self._store_result = store_result
async def get(self, request, flow_id):
async def get(self, request):
"""Do not allow getting status of a flow in progress."""
return self.json_message('Invalid flow specified', 404)
@@ -152,6 +210,13 @@ class LoginFlowResourceView(FlowManagerResourceView):
return self.json_message('Invalid client id', 400)
try:
# do not allow change ip during login flow
for flow in self._flow_mgr.async_progress():
if (flow['flow_id'] == flow_id and
flow['context']['ip_address'] !=
request.get(KEY_REAL_IP)):
return self.json_message('IP address changed', 400)
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
@@ -161,12 +226,22 @@ class LoginFlowResourceView(FlowManagerResourceView):
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':
if (result.get('errors') is not None and
result['errors'].get('base') in ['invalid_auth',
'invalid_code']):
await process_wrong_login(request)
return self.json(self._prepare_result_json(result))
return self.json(_prepare_result_json(result))
result.pop('data')
result['result'] = self._store_credentials(client_id, result['result'])
result['result'] = self._store_result(client_id, result['result'])
return self.json(result)
async def delete(self, request, flow_id):
"""Cancel a flow in progress."""
try:
self._flow_mgr.async_abort(flow_id)
except data_entry_flow.UnknownFlow:
return self.json_message('Invalid flow specified', 404)
return self.json_message('Flow aborted')

View File

@@ -0,0 +1,134 @@
"""Helpers to setup multi-factor auth module."""
import logging
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components import websocket_api
from homeassistant.core import callback, HomeAssistant
WS_TYPE_SETUP_MFA = 'auth/setup_mfa'
SCHEMA_WS_SETUP_MFA = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_SETUP_MFA,
vol.Exclusive('mfa_module_id', 'module_or_flow_id'): str,
vol.Exclusive('flow_id', 'module_or_flow_id'): str,
vol.Optional('user_input'): object,
})
WS_TYPE_DEPOSE_MFA = 'auth/depose_mfa'
SCHEMA_WS_DEPOSE_MFA = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_DEPOSE_MFA,
vol.Required('mfa_module_id'): str,
})
DATA_SETUP_FLOW_MGR = 'auth_mfa_setup_flow_manager'
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass):
"""Init mfa setup flow manager."""
async def _async_create_setup_flow(handler, context, data):
"""Create a setup flow. hanlder is a mfa module."""
mfa_module = hass.auth.get_auth_mfa_module(handler)
if mfa_module is None:
raise ValueError('Mfa module {} is not found'.format(handler))
user_id = data.pop('user_id')
return await mfa_module.async_setup_flow(user_id)
async def _async_finish_setup_flow(flow, flow_result):
_LOGGER.debug('flow_result: %s', flow_result)
return flow_result
hass.data[DATA_SETUP_FLOW_MGR] = data_entry_flow.FlowManager(
hass, _async_create_setup_flow, _async_finish_setup_flow)
hass.components.websocket_api.async_register_command(
WS_TYPE_SETUP_MFA, websocket_setup_mfa, SCHEMA_WS_SETUP_MFA)
hass.components.websocket_api.async_register_command(
WS_TYPE_DEPOSE_MFA, websocket_depose_mfa, SCHEMA_WS_DEPOSE_MFA)
@callback
@websocket_api.ws_require_user(allow_system_user=False)
def websocket_setup_mfa(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Return a setup flow for mfa auth module."""
async def async_setup_flow(msg):
"""Return a setup flow for mfa auth module."""
flow_manager = hass.data[DATA_SETUP_FLOW_MGR]
flow_id = msg.get('flow_id')
if flow_id is not None:
result = await flow_manager.async_configure(
flow_id, msg.get('user_input'))
connection.send_message(
websocket_api.result_message(
msg['id'], _prepare_result_json(result)))
return
mfa_module_id = msg.get('mfa_module_id')
mfa_module = hass.auth.get_auth_mfa_module(mfa_module_id)
if mfa_module is None:
connection.send_message(websocket_api.error_message(
msg['id'], 'no_module',
'MFA module {} is not found'.format(mfa_module_id)))
return
result = await flow_manager.async_init(
mfa_module_id, data={'user_id': connection.user.id})
connection.send_message(
websocket_api.result_message(
msg['id'], _prepare_result_json(result)))
hass.async_create_task(async_setup_flow(msg))
@callback
@websocket_api.ws_require_user(allow_system_user=False)
def websocket_depose_mfa(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg):
"""Remove user from mfa module."""
async def async_depose(msg):
"""Remove user from mfa auth module."""
mfa_module_id = msg['mfa_module_id']
try:
await hass.auth.async_disable_user_mfa(
connection.user, msg['mfa_module_id'])
except ValueError as err:
connection.send_message(websocket_api.error_message(
msg['id'], 'disable_failed',
'Cannot disable MFA Module {}: {}'.format(
mfa_module_id, err)))
return
connection.send_message(
websocket_api.result_message(
msg['id'], 'done'))
hass.async_create_task(async_depose(msg))
def _prepare_result_json(result):
"""Convert result to JSON."""
if result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY:
data = result.copy()
return data
if result['type'] != data_entry_flow.RESULT_TYPE_FORM:
return result
import voluptuous_serialize
data = result.copy()
schema = data['data_schema']
if schema is None:
data['data_schema'] = []
else:
data['data_schema'] = voluptuous_serialize.convert(schema)
return data

View File

@@ -0,0 +1,35 @@
{
"mfa_setup":{
"totp": {
"title": "TOTP",
"step": {
"init": {
"title": "Set up two-factor authentication using TOTP",
"description": "To activate two factor authentication using time-based one-time passwords, scan the QR code with your authentication app. If you don't have one, we recommend either [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/).\n\n{qr_code}\n\nAfter scanning the code, enter the six digit code from your app to verify the setup. If you have problems scanning the QR code, do a manual setup with code **`{code}`**."
}
},
"error": {
"invalid_code": "Invalid code, please try again. If you get this error consistently, please make sure the clock of your Home Assistant system is accurate."
}
},
"notify": {
"title": "Notify One-Time Password",
"step": {
"init": {
"title": "Set up one-time password delivered by notify component",
"description": "Please select one of the notification services:"
},
"setup": {
"title": "Verify setup",
"description": "A one-time password has been sent via **notify.{notify_service}**. Please enter it below:"
}
},
"abort": {
"no_available_service": "No notification services available."
},
"error": {
"invalid_code": "Invalid code, please try again."
}
}
}
}

View File

@@ -1,5 +1,5 @@
"""
Allow to setup simple automation rules via the config file.
Allow to set up simple automation rules via the config file.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/automation/
@@ -17,7 +17,6 @@ from homeassistant.loader import bind_hass
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE, SERVICE_RELOAD, EVENT_HOMEASSISTANT_START, CONF_ID)
from homeassistant.components import logbook
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import extract_domain_configs, script, condition
from homeassistant.helpers.entity import ToggleEntity
@@ -115,70 +114,26 @@ def is_on(hass, entity_id):
return hass.states.is_state(entity_id, STATE_ON)
@bind_hass
def turn_on(hass, entity_id=None):
"""Turn on specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
@bind_hass
def turn_off(hass, entity_id=None):
"""Turn off specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
@bind_hass
def toggle(hass, entity_id=None):
"""Toggle specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
@bind_hass
def trigger(hass, entity_id=None):
"""Trigger specified automation or all."""
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TRIGGER, data)
@bind_hass
def reload(hass):
"""Reload the automation from config."""
hass.services.call(DOMAIN, SERVICE_RELOAD)
@bind_hass
def async_reload(hass):
"""Reload the automation from config.
Returns a coroutine object.
"""
return hass.services.async_call(DOMAIN, SERVICE_RELOAD)
@asyncio.coroutine
def async_setup(hass, config):
async def async_setup(hass, config):
"""Set up the automation."""
component = EntityComponent(_LOGGER, DOMAIN, hass,
group_name=GROUP_NAME_ALL_AUTOMATIONS)
yield from _async_process_config(hass, config, component)
await _async_process_config(hass, config, component)
@asyncio.coroutine
def trigger_service_handler(service_call):
async def trigger_service_handler(service_call):
"""Handle automation triggers."""
tasks = []
for entity in component.async_extract_from_service(service_call):
tasks.append(entity.async_trigger(
service_call.data.get(ATTR_VARIABLES), True))
service_call.data.get(ATTR_VARIABLES),
skip_condition=True,
context=service_call.context))
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def turn_onoff_service_handler(service_call):
async def turn_onoff_service_handler(service_call):
"""Handle automation turn on/off service calls."""
tasks = []
method = 'async_{}'.format(service_call.service)
@@ -186,10 +141,9 @@ def async_setup(hass, config):
tasks.append(getattr(entity, method)())
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def toggle_service_handler(service_call):
async def toggle_service_handler(service_call):
"""Handle automation toggle service calls."""
tasks = []
for entity in component.async_extract_from_service(service_call):
@@ -199,15 +153,14 @@ def async_setup(hass, config):
tasks.append(entity.async_turn_on())
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
await asyncio.wait(tasks, loop=hass.loop)
@asyncio.coroutine
def reload_service_handler(service_call):
async def reload_service_handler(service_call):
"""Remove all automations and load new ones from config."""
conf = yield from component.async_prepare_reload()
conf = await component.async_prepare_reload()
if conf is None:
return
yield from _async_process_config(hass, conf, component)
await _async_process_config(hass, conf, component)
hass.services.async_register(
DOMAIN, SERVICE_TRIGGER, trigger_service_handler,
@@ -272,15 +225,14 @@ class AutomationEntity(ToggleEntity):
"""Return True if entity is on."""
return self._async_detach_triggers is not None
@asyncio.coroutine
def async_added_to_hass(self) -> None:
async def async_added_to_hass(self) -> None:
"""Startup with initial state or previous state."""
if self._initial_state is not None:
enable_automation = self._initial_state
_LOGGER.debug("Automation %s initial state %s from config "
"initial_state", self.entity_id, enable_automation)
else:
state = yield from async_get_last_state(self.hass, self.entity_id)
state = await async_get_last_state(self.hass, self.entity_id)
if state:
enable_automation = state.state == STATE_ON
self._last_triggered = state.attributes.get('last_triggered')
@@ -298,54 +250,50 @@ class AutomationEntity(ToggleEntity):
# HomeAssistant is starting up
if self.hass.state == CoreState.not_running:
@asyncio.coroutine
def async_enable_automation(event):
async def async_enable_automation(event):
"""Start automation on startup."""
yield from self.async_enable()
await self.async_enable()
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, async_enable_automation)
# HomeAssistant is running
else:
yield from self.async_enable()
await self.async_enable()
@asyncio.coroutine
def async_turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs) -> None:
"""Turn the entity on and update the state."""
if self.is_on:
return
yield from self.async_enable()
await self.async_enable()
@asyncio.coroutine
def async_turn_off(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs) -> None:
"""Turn the entity off."""
if not self.is_on:
return
self._async_detach_triggers()
self._async_detach_triggers = None
yield from self.async_update_ha_state()
await self.async_update_ha_state()
@asyncio.coroutine
def async_trigger(self, variables, skip_condition=False):
async def async_trigger(self, variables, skip_condition=False,
context=None):
"""Trigger automation.
This method is a coroutine.
"""
if skip_condition or self._cond_func(variables):
yield from self._async_action(self.entity_id, variables)
self.async_set_context(context)
await self._async_action(self.entity_id, variables, context)
self._last_triggered = utcnow()
yield from self.async_update_ha_state()
await self.async_update_ha_state()
@asyncio.coroutine
def async_will_remove_from_hass(self):
async def async_will_remove_from_hass(self):
"""Remove listeners when removing automation from HASS."""
yield from self.async_turn_off()
await self.async_turn_off()
@asyncio.coroutine
def async_enable(self):
async def async_enable(self):
"""Enable this automation entity.
This method is a coroutine.
@@ -353,9 +301,9 @@ class AutomationEntity(ToggleEntity):
if self.is_on:
return
self._async_detach_triggers = yield from self._async_attach_triggers(
self._async_detach_triggers = await self._async_attach_triggers(
self.async_trigger)
yield from self.async_update_ha_state()
await self.async_update_ha_state()
@property
def device_state_attributes(self):
@@ -368,8 +316,7 @@ class AutomationEntity(ToggleEntity):
}
@asyncio.coroutine
def _async_process_config(hass, config, component):
async def _async_process_config(hass, config, component):
"""Process config and add automations.
This method is a coroutine.
@@ -411,20 +358,19 @@ def _async_process_config(hass, config, component):
entities.append(entity)
if entities:
yield from component.async_add_entities(entities)
await component.async_add_entities(entities)
def _async_get_action(hass, config, name):
"""Return an action based on a configuration."""
script_obj = script.Script(hass, config, name)
@asyncio.coroutine
def action(entity_id, variables):
async def action(entity_id, variables, context):
"""Execute an action."""
_LOGGER.info('Executing %s', name)
logbook.async_log_entry(
hass, name, 'has been triggered', DOMAIN, entity_id)
yield from script_obj.async_run(variables)
hass.components.logbook.async_log_entry(
name, 'has been triggered', DOMAIN, entity_id)
await script_obj.async_run(variables, context)
return action
@@ -448,8 +394,7 @@ def _async_process_if(hass, config, p_config):
return if_action
@asyncio.coroutine
def _async_process_trigger(hass, config, trigger_configs, name, action):
async def _async_process_trigger(hass, config, trigger_configs, name, action):
"""Set up the triggers.
This method is a coroutine.
@@ -457,13 +402,13 @@ def _async_process_trigger(hass, config, trigger_configs, name, action):
removes = []
for conf in trigger_configs:
platform = yield from async_prepare_setup_platform(
platform = await async_prepare_setup_platform(
hass, config, DOMAIN, conf.get(CONF_PLATFORM))
if platform is None:
return None
remove = yield from platform.async_trigger(hass, conf, action)
remove = await platform.async_trigger(hass, conf, action)
if not remove:
_LOGGER.error("Error setting up trigger %s", name)

View File

@@ -4,7 +4,6 @@ Offer event listening automation rules.
For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/docs/automation/trigger/#event-trigger
"""
import asyncio
import logging
import voluptuous as vol
@@ -25,8 +24,7 @@ TRIGGER_SCHEMA = vol.Schema({
})
@asyncio.coroutine
def async_trigger(hass, config, action):
async def async_trigger(hass, config, action):
"""Listen for events based on configuration."""
event_type = config.get(CONF_EVENT_TYPE)
event_data_schema = vol.Schema(
@@ -45,11 +43,11 @@ def async_trigger(hass, config, action):
# If event data doesn't match requested schema, skip event
return
hass.async_run_job(action, {
hass.async_run_job(action({
'trigger': {
'platform': 'event',
'event': event,
},
})
}, context=event.context))
return hass.bus.async_listen(event_type, handle_event)

View File

@@ -0,0 +1,74 @@
"""
Offer geo location automation rules.
For more details about this automation trigger, please refer to the
documentation at
https://home-assistant.io/docs/automation/trigger/#geo-location-trigger
"""
import voluptuous as vol
from homeassistant.components.geo_location import DOMAIN
from homeassistant.core import callback
from homeassistant.const import (
CONF_EVENT, CONF_PLATFORM, CONF_SOURCE, CONF_ZONE, EVENT_STATE_CHANGED)
from homeassistant.helpers import (
condition, config_validation as cv)
from homeassistant.helpers.config_validation import entity_domain
EVENT_ENTER = 'enter'
EVENT_LEAVE = 'leave'
DEFAULT_EVENT = EVENT_ENTER
TRIGGER_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'geo_location',
vol.Required(CONF_SOURCE): cv.string,
vol.Required(CONF_ZONE): entity_domain('zone'),
vol.Required(CONF_EVENT, default=DEFAULT_EVENT):
vol.Any(EVENT_ENTER, EVENT_LEAVE),
})
def source_match(state, source):
"""Check if the state matches the provided source."""
return state and state.attributes.get('source') == source
async def async_trigger(hass, config, action):
"""Listen for state changes based on configuration."""
source = config.get(CONF_SOURCE).lower()
zone_entity_id = config.get(CONF_ZONE)
trigger_event = config.get(CONF_EVENT)
@callback
def state_change_listener(event):
"""Handle specific state changes."""
# Skip if the event is not a geo_location entity.
if not event.data.get('entity_id').startswith(DOMAIN):
return
# Skip if the event's source does not match the trigger's source.
from_state = event.data.get('old_state')
to_state = event.data.get('new_state')
if not source_match(from_state, source) \
and not source_match(to_state, source):
return
zone_state = hass.states.get(zone_entity_id)
from_match = condition.zone(hass, zone_state, from_state)
to_match = condition.zone(hass, zone_state, to_state)
# pylint: disable=too-many-boolean-expressions
if trigger_event == EVENT_ENTER and not from_match and to_match or \
trigger_event == EVENT_LEAVE and from_match and not to_match:
hass.async_run_job(action({
'trigger': {
'platform': 'geo_location',
'source': source,
'entity_id': event.data.get('entity_id'),
'from_state': from_state,
'to_state': to_state,
'zone': zone_state,
'event': trigger_event,
},
}, context=event.context))
return hass.bus.async_listen(EVENT_STATE_CHANGED, state_change_listener)

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