Compare commits

...

342 Commits

Author SHA1 Message Date
Franck Nijhof
e0367dc721 Bumped version to 0.112.0b0 2020-06-24 21:35:02 +02:00
Markus Bong
35dc5ba742 Add devolo sensor devices (#37049) 2020-06-24 10:06:11 -07:00
rajlaud
f7325a7d35 Squeezebox dispatch helper (#37030) 2020-06-24 10:04:17 -07:00
J. Nick Koston
a798b508bc Raise slow startup timeout to 3h for large db migrations. (#37061) 2020-06-24 09:58:01 -07:00
J. Nick Koston
255d706c24 Avoid creating a column in v8 schema that is removed in v9 schema (#37062) 2020-06-24 09:56:01 -07:00
J. Nick Koston
cc8e0ef942 Handle mysql index/column already exists during migration (#37064) 2020-06-24 09:55:13 -07:00
RogerSelwyn
d9a3b04e30 Fix asuswrt spamming logs with exceptions (#37063) 2020-06-24 18:50:58 +02:00
Andre Lengwenus
4a65bed0eb Fix LCN cover behavior when using output ports (#37034)
* Fixed LCN cover behavior when connected to output ports

* Cover is assumed to be in an open state unless it is fully closed.
2020-06-24 15:10:56 +02:00
Franck Nijhof
02adcc532f Fix rest to use BinarySensorEntity (#37054) 2020-06-24 14:53:17 +02:00
bsmappee
fbd5ccf156 Add smappee binary_sensor platform (#37023) 2020-06-24 14:37:48 +02:00
uvjustin
8d69a4968f Handle unexpected versions in forked_daapd zeroconf (#37053) 2020-06-24 14:19:08 +02:00
Eugene Prystupa
e3b90ea3f7 Add Plum Lightpad config flow (#36802)
* add support for config flow for Plum Lightpad integration

* add support for config flow for Plum Lightpad integration (remove unintended change to requirements_test_all.txt)

* add support for config flow for Plum Lightpad integration (fix lint issues)

* add support for config flow for Plum Lightpad integration (PR feedback)

* add support for config flow for Plum Lightpad integration (fix lint)

* Update homeassistant/components/plum_lightpad/__init__.py

use debug instead of info for logging

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/plum_lightpad/strings.json

switch to use generated references instead of hard-coded strings

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/plum_lightpad/strings.json

switch to use references instead of hard-coded string

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/plum_lightpad/strings.json

removing translated title per suggestion

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/plum_lightpad/strings.json

removing per suggestion

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* remove unnecessary deepcopy

* remove unnecessary logging warning, since ignoring is expected for configuration.yaml scenario

* switch to hass.loop.create_task per PR feedback

* show login errors when configuring integration via UI (PR feedback)

* disable wrongly flag pylint violation

* add except handler to handle connection errors when setting up config flow entry

* address PR feedback regarding exception handling

* Update homeassistant/components/plum_lightpad/config_flow.py

use helper instead of custom code/message-id

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-23 20:40:11 -07:00
mdegat01
6c7355785a Add support for glob matching to entity filters (#36913)
* Added GLOB capability to entityfilter and every place that uses it. All existing tests are passing

* added tests for components affected by glob change

* fixed flake8 error

* mocking the correct listener

* mocking correct bus method in azure test

* tests passing in 3.7 and 3.8

* fixed formatting issue from rebase/conflict

* Checking against glob patterns in more performant way

* perf improvments and reverted unnecessarily adjusted tests

* added new benchmark test around filters

* no longer using get with default in entityfilter

* changed filter name and removed logbook from filter benchmark

* simplified benchmark tests from feedback

* fixed apache tests and returned include exclude schemas to normal

* fixed azure event hub tests to properly go through component logic

* fixed azure test and clean up for other tests

* renaming test files to match standard

* merged mqtt statestream test changes with base

* removed dependency on recorder filter schema from history

* fixed recorder tests after merge and a bunch of lint errors
2020-06-23 20:02:29 -05:00
Oscar Calvo
a1ac1fb091 Part 3: Add support for incoming sms events (#37015)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-23 18:02:08 -07:00
Paulus Schoutsen
8ca5a04a5d Updated frontend to 20200623.3 (#37045) 2020-06-23 17:29:39 -07:00
HomeAssistant Azure
04f75d6557 [ci skip] Translation update 2020-06-24 00:08:54 +00:00
Shulyaka
26f09bae28 Add humidifier reproduce state (#36799)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-23 17:00:32 -07:00
Thomas Le Gentil
2af961513d Remove fortigate integration (#34586) 2020-06-24 00:21:51 +02:00
Hedgehog57
fd1edf1bb6 Add resource_template to rest binary_sensor (#30703) 2020-06-23 15:08:55 -07:00
J. Nick Koston
152a80abed Complete deprecation of hidden attribute (ATTR_HIDDEN) (#37041) 2020-06-23 17:05:32 -05:00
Franck Nijhof
c289f9f099 Toon translation updates + tweaks (#37043) 2020-06-24 00:04:16 +02:00
Chris Talkington
ed89d4869d Fix typo in roku services description (#37042) 2020-06-24 00:00:24 +02:00
Franck Nijhof
5b4187a9eb Merge branch 'master' into dev 2020-06-23 23:22:38 +02:00
J. Nick Koston
5b79c1f9ef Remove support for deprecated hidden attribute from logbook and history (#37039) 2020-06-23 13:40:39 -07:00
Bouwe Westerdijk
b61b0321d6 Bump Plugwise_Smile to v1.1.0 (#37038) 2020-06-23 22:02:50 +02:00
Erik Montnemery
bb17cbdd83 Drop MQTT broker tls_version parameter (#37033) 2020-06-23 12:12:37 -07:00
J. Nick Koston
91e0395c1c Optimize database indexes for existing queries (#37036)
Cleanup indexes as >50% of the db size was indexes,
many of them unused in any current query

Logbook search was having to filter event_types without
an index:

  Created ix_events_event_type_time_fired
  Dropped ix_events_event_type

States had a redundant keys on composite index:

  Dropped ix_states_entity_id
  Its unused since we have ix_states_entity_id_last_updated

De-duplicate storage of context in states as
its always stored in events and can be found
by joining the state on the event_id.

  Dropped ix_states_context_id
  Dropped ix_states_context_parent_id
  Dropped ix_states_context_user_id

After schema v9:

STATES............................................ 10186       40.9%
EVENTS............................................ 5502        22.1%
IX_STATES_ENTITY_ID_LAST_UPDATED.................. 2177         8.7%
IX_EVENTS_EVENT_TYPE_TIME_FIRED................... 1910         7.7%
IX_EVENTS_CONTEXT_ID.............................. 1592         6.4%
IX_EVENTS_TIME_FIRED.............................. 1383         5.6%
IX_STATES_LAST_UPDATED............................ 1079         4.3%
IX_STATES_EVENT_ID................................ 375          1.5%
IX_EVENTS_CONTEXT_PARENT_ID....................... 347          1.4%
IX_EVENTS_CONTEXT_USER_ID......................... 346          1.4%
IX_RECORDER_RUNS_START_END........................ 1            0.004%
RECORDER_RUNS..................................... 1            0.004%
SCHEMA_CHANGES.................................... 1            0.004%
SQLITE_MASTER..................................... 1            0.004%
2020-06-23 10:57:52 -07:00
Paulus Schoutsen
a71e0a4b29 Updated frontend to 20200623.2 (#37035) 2020-06-23 10:37:32 -07:00
Erik Montnemery
4e77969f5e Drop use of mock_mqtt_component (#37013)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-23 10:17:22 -07:00
Shulyaka
cf6480cda0 Add humidifier device actions (#36854) 2020-06-23 10:04:51 -07:00
Erik Montnemery
1b3e5460a9 Remove embedded MQTT broker (#37032) 2020-06-23 09:51:50 -07:00
terminet85
a004e6aa68 Add new Solaredge sensors (#34525)
* Changes to be committed:
	modified:   homeassistant/components/solaredge/const.py
	modified:   homeassistant/components/solaredge/sensor.py

Solaredge as recently changed its policy about local api access, so solaredge-local doesn't work with last firmware update for almost users.
Please check https://github.com/home-assistant/core.git

Anyway the solardge remote api is still working, but doesn't got some usefull sensor information as Power SelfConsumption, Power Exported, Power Imported.
With my update, I'll fetching API energy details where  we got these new sensors.

* Grammar/syntax fix

* Indentation fix

* Black formatting fix

* isort fix

* To force re-check

* Fix too-many-nested-blocks

* Fix indentation

* Fix Black formatting :D

* Fix Redefining built-in var

* Removed comment to force check
2020-06-23 09:06:31 -07:00
Chris Talkington
6610bbe7bb Add service to trigger roku search (#37014) 2020-06-23 09:03:43 -07:00
Thomas Hollstegge
2c7876fa66 Fix alexa flash briefings after removal of api_password auth (#36789) 2020-06-23 08:58:25 -07:00
Joakim Sørensen
835f433cf7 Remove loading of legacy translations (#37021) 2020-06-23 10:58:11 +02:00
Paulus Schoutsen
9d16edc1dc Bump cloud to 0.34.7 (#37018) 2020-06-22 23:42:10 -07:00
Paulus Schoutsen
e6d814da1d Avoid Home connect test doing I/O (#37017) 2020-06-22 23:41:44 -07:00
Ville Skyttä
7c5e852303 Improve detected Huawei LTE device name (#36772)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-22 21:49:05 -07:00
Martin Hjelmare
149a3165e6 Fix coronavirus worldwide sum (#36737)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-22 21:22:52 -07:00
Oscar Calvo
82058f0b50 Part 2: Add signal sensor (#34406) 2020-06-22 20:41:55 -07:00
celestinjr
63baf6fb0f Extend itach repeat functionality (#36535) 2020-06-22 20:30:47 -07:00
J. Nick Koston
ad6315be5c Ensure recorder runs are cleaned up during purge (#36989)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-22 20:10:05 -07:00
Xiaonan Shen
b4489e132c Add DataUpdateCoordinator to pihole (#36684)
Co-authored-by: Ian <vividboarder@gmail.com>
2020-06-22 18:47:37 -07:00
Robert Van Gorkom
2538cda9d4 Fix withings body hydration unit of measure to match documented unit of measure. (#36723) 2020-06-22 18:45:05 -07:00
Allison
49a153a2e5 Add "seconds" as a valid unit_of_measurement (#36811) 2020-06-22 18:40:56 -07:00
Phil Bruckner
d68148417f Retry failed Amcrest commands that change settings (#36917) 2020-06-22 18:36:41 -07:00
Franck Nijhof
8b21b415c4 Fix/Rewrite of Toon integration (#36952)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-22 18:22:41 -07:00
Shulyaka
c28493098a Add new humidifier entity integration (#28693)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-22 17:59:16 -07:00
Erik Montnemery
747490ab34 Support reconfiguring MQTT config entry (#36537) 2020-06-22 17:49:01 -07:00
Alex van den Hoogen
ee816ed3dd Optimize recorder MySQL tables when repacking (#36762)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-22 17:28:03 -07:00
Chris
16f1ef5a44 Add attribute templates to template vacuum (#36518) 2020-06-22 17:21:20 -07:00
zpetr
a2e705880d Tahoma zwave light support (#36584) 2020-06-22 17:18:14 -07:00
HomeAssistant Azure
e39997ca10 [ci skip] Translation update 2020-06-23 00:09:06 +00:00
tomtzeng
620271c782 Add new language to Google Cloud TTS (#36705) 2020-06-22 17:07:09 -07:00
Steven Looman
d974cd4606 Prevent upnp to use None values (#36803) 2020-06-22 16:39:57 -07:00
Jonas Thuresson
247df5874b Xiaomii miio vaccum clean segment service (#36801) 2020-06-22 16:37:05 -07:00
zvldz
e1060f154e Make generic camera stream_source a template (#36123)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-22 16:11:18 -07:00
Oscar Calvo
3f9e3d0905 Part 1: Support for config flow on SMS integration (#35995)
* Add support for config flow;Remove IO on main loop

* Remove not needed const

* Remove not needed method

* Small refactor

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/gateway.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/gammuasync.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Refactor gammu

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Fix pylint

* Apply PR feedback

* Update gammu version with async support

* Apply PR feedback

* Apply PR feedback

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Apply PR feedback

* Apply PR feedback

* Apply PR feedback

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/strings.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/strings.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/sms/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Apply PR feedback

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-22 16:07:58 -07:00
Paulus Schoutsen
becc011135 Handle ArmDisarm execute without arm level (#36942) 2020-06-22 16:06:30 -07:00
Franck Nijhof
6660cf701d Remove lockitron integration (#37012) 2020-06-22 16:34:26 -06:00
Erik Montnemery
280f49540e Drop use of async_mock_mqtt_component (#37011) 2020-06-22 23:59:50 +02:00
Erik Montnemery
a2e2c35011 Use pytest for more MQTT tests (#36859)
* Use pytest for more MQTT tests

* Address review comments

* Break out PAHO client mock in separate fixture.

* tweak.
2020-06-22 22:02:29 +02:00
Robert Svensson
3a83f4bdbe Support guest events in UniFi device tracker (#37008) 2020-06-22 21:50:34 +02:00
Robert Svensson
02d94f2fd0 Bump Axis library to support fence guard events (#37007) 2020-06-22 21:50:05 +02:00
J. Nick Koston
53a91ece4e Improve isoformat timestamp performance (#36991)
* adj

* time_fired_isoformat

* remove unused code

* tests for processing timestamps

* restore missing import lost in merge conflict

* test for None case
2020-06-22 12:06:02 -05:00
Robert Van Gorkom
5446641f09 User defined profile name for Withings (#36864) 2020-06-22 17:55:41 +02:00
J. Nick Koston
e19c97af7c Fix history api with no constraints (#36979) 2020-06-22 10:32:46 -05:00
Franck Nijhof
7e696f19d3 Upgrade numpy to 1.19.0 (#36996) 2020-06-22 10:28:06 -05:00
Mark Coombes
6906a19c01 Bump python-ecobee-api to 0.2.7 (#37002) 2020-06-22 16:52:02 +02:00
Jürgen Haas
93f9f1b320 Add NetdataAlarms sensor to additionally collect alarm information (#34339) 2020-06-22 16:29:29 +02:00
rajlaud
3f427602ba Squeezebox config flow (#35669)
* Squeezebox add config flow and player discovery

* Fixes to config flow

* Unavailable player detection and recovery

* Improved error message for auth failure

* Testing for squeezebox config flow

* Import configuration.yaml

* Support for discovery integration

* Internal server discovery

* Fix bug restoring previously detected squeezebox player

* Tests for user and edit steps in config flow

* Tests for import config flow

* Additional config flow tests and fixes

* Linter fixes

* Check that players are found before iterating them

* Remove noisy logger message

* Update requirements_all after rebase

* Use asyncio.Event in discovery task

* Use common keys in strings.json

* Bump pysqueezebox to v0.2.2 for fixed server discovery using python3.7

* Bump pysqueezebox version to v0.2.3

* Don't trap AbortFlow exception

Co-authored-by: J. Nick Koston <nick@koston.org>

* Refactor validate_input

* Update squeezebox tests

* Build data flow schema using function

* Fix linter error

* Updated en.json

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update .coveragerc for squeezebox config flow test

* Mock TIMEOUT for faster testing

* More schema de-duplication and testing improvements

* Apply suggestions from code review

Co-authored-by: J. Nick Koston <nick@koston.org>

* Testing and config flow improvements

* Remove unused exceptions

* Remove deprecated logger message

* Update homeassistant/components/squeezebox/media_player.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Implement suggestions from code review

* Add async_unload_entry

* Use MockConfigEntry in squeezebox tests

* Remove unnecessary config schema

* Stop server discovery task when last config entry unloaded

* Improvements to async_unload_entry

* Fix bug in _discovery arguments

* Do not await server discovery in async_setup_entry

* Do not await start server discovery in async_setup

* Do not start server discovery from async_setup_entry until homeassistant running

* Re-detect players when server removed and re-added without restart

* Use entry.entry_id instead of unique_id

* Update unittests to avoid patching homeassistant code

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-06-22 09:29:01 -05:00
J. Nick Koston
e25f216fd6 Reduce state conversion overhead in history by lazy loading properties (#36963) 2020-06-22 16:19:33 +02:00
Franck Nijhof
b47be05efc Add new Remote Python Debugger integration (#36960) 2020-06-22 15:17:59 +02:00
jjlawren
9d40ae96b5 Set Plex media_player discovery source as an attribute (#36884) 2020-06-22 14:48:40 +02:00
Franck Nijhof
f131959f4b Correct inst method names in system info and issue templates (#36998) 2020-06-22 14:46:31 +02:00
Adam Žurek
0054904454 highlight Dockerfile.dev as Dockerfile in github ui (#36933) 2020-06-22 14:45:57 +02:00
Robert Van Gorkom
5c8d8a290f Add default admin user for gogogate2 setup (#36722) 2020-06-22 13:13:10 +02:00
Filip Pytloun
dcb3a57476 Add support for daikin humidity sensor (#36475) 2020-06-22 13:10:26 +02:00
Damien Levin
78225c9ddd Set webostv scan interval to 10s for quick updates (#35795)
Co-authored-by: damien.levin@gmail.com <damienlevin@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-06-22 13:08:02 +02:00
Franck Nijhof
215215747e Fix use of AdGuard integration without version (#36995) 2020-06-22 13:06:59 +02:00
Gerard
5cd85f9f00 Upgrade to bimmer_connected 0.7.7 (#36985) 2020-06-22 13:06:18 +02:00
ktnrg45
4674cb406e Bump pyps4-2ndscreen to 1.1.0 (#36987) 2020-06-22 13:02:18 +02:00
James Nimmo
82b77a8108 Bump to pyIntesishome 1.7.5 (#36562) 2020-06-22 12:56:29 +02:00
starkillerOG
1f9721bad3 Add Xiaomi Aqara Config Flow (#35595)
* Xiaomi Aqara Config Flow

* Xiaomi Aqara Config Flow

* Xiaomi Aqara Config Flow

* Xiaomi Aqara Config Flow

* Xiaomi Aqara Config Flow

First tested and working version

* Remove depricated discovery

* Add Xiaomi Aqara Config Flow

* Add Xiaomi Aqara tests

* Update .coveragerc

* Update requirements_test_all.txt

* fix spelling mistake

* fix select scheme

* fix wrong conflict resolve

* add IP to zeroconf discovery title

* black styling

* add getmac requirement

Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>

* add getmac

* add getmac

* Clean up

* Update homeassistant/components/xiaomi_aqara/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/xiaomi_aqara/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/xiaomi_aqara/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/xiaomi_aqara/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/xiaomi_aqara/__init__.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* resolve data storage

* move format_mac down

* Remove discovery_retry from config flow

* remove unused strings

* fix styling

* fix black styling

* fix tests

* remove mac connection

This is needed to prevent a conflict with the Xiaomi Miio integration that I discovered during testing.

* fix flake8

* remove getmac depandance

* check for inavlid_interface + test

* Validate gateway key

* add invalid key tests

* Fix spelling

* Only set up sensors if no key

Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-22 11:54:17 +02:00
Chris Talkington
6aba87f3a6 Use roku media state to detect paused media (#36980) 2020-06-22 09:51:38 +02:00
J. Nick Koston
edad387b12 Make recorder execute avoid native conversion by default (#36938) 2020-06-21 21:58:57 -07:00
HomeAssistant Azure
f4b8a95205 [ci skip] Translation update 2020-06-22 00:05:46 +00:00
David Ryan
29adc6a27b Support Hydrawise API v1.4 (#34448)
* Now supports Hydrawise API v1.4

* Removed dependency and codeowners name.

* Update CODEOWNERS to include hydrawise

* Changes made from review comments.

* Clean up update.

* Added device class for timestamp and switch. Consolodate methods to parent class.

* Cap next_cycle at 2 years to prevent time overflow.

* Addressed review comments.

* Updated DEVICE_MAP and icon() based on review comments.
2020-06-21 16:55:47 -05:00
Andrew Hayworth
fed6625324 Refactor / update Awair integration (#34394)
* Refactor / update Awair integration

This commit does a few things, all in service of making the Awair
integration more modern and reliable. Specifically we do the following:

- Update to python_awair 0.1.1
- Begin using config entries / flow for setting up the integration.
  - YAML support is completely removed.
  - The integration now allows adding multiple Awair accounts, should a
    user wish to do so (I found it _very_ useful in development).
- Group various Awair sensors into devices, using the device registry.
- Renames various sensors and treats the "dust" sensor as a particulate sensor.
- Device update rate-limits are no longer dynamically calculated; the
  Awair API now separates rate-limits on a per-device basis.
- Supports sound pressure and illuminance sensors found on some Awair devices.
- We report the "awair index" for certain sensors as part of device_state_attributes.
  The "index" is a subjective measure of whether or not a sensor reading
  is "good" or "bad" (and to what extent). It's a component of the Awair
  score, and it is useful on its own as an input for those who wish to
  do things like "display this color if the value is 'bad'".

This is a breaking change, and requires updates to documentation and a
warning in the README. The breaking changes in detail, are:

- Support for all YAML configuration is removed, and users will need to
  re-add the integration via the UI.
- We no longer support overriding device discovery via manual
  configuration of device UUIDs. This was previously supported because
  the Awair API had severe limits on the device list endpoints; however
  those have since been removed.
- Gen 1 devices no longer show a "dust" sensor; rather we create a PM2.5
  sensor and a PM10 sensor and just keep the values in sync. This better
  reflects the sensor capabilities: it can detect particles in a range
  from 2.5 -> 10, but cannot differentiate between sizes.
- Sensors are renamed as follows:
  - "sensor.devicename_co2"   -> "sensor.devicename_carbon_dioxide"
  - "sensor.devicename_voc"   -> "sensor.devicename_volatile_organic_compounds"
  - "sensor.devicename_score" -> "sensor.devicename_air_quality_index"
  - I've chosen to call the "Awair Score" an "air quality index" sensor,
    because fundamentally the "Awair Score" and other air quality indices
    (such as CAQI) do the same thing: they calculate a value based on a
    variety of other inputs.

Under the hood, the integration has seen some improvements:

- We use the DataUpdateCoordinator class to handle updates, rather than
  rolling our own update class.
- The code no longer tracks availability based on a timestamp returned
  from the Awair service; we assert that if we have received a response
  and the response has data for our device, then we are available (and
  otherwise, not available). We don't need to test the actual Awair API
  so heavily.
- Test coverage has been expanded to handle a variety of products that
  Awair produces, not just the one I happen to own.
- Test coverage no longer concerns itself with testing behavior that is
  now handled by the DataUpdateCoordinator; nor is it concerned with
  ensuring that the overall component sets up and registers properly.
  These are assumed to be well-tested functionaity of the core and not
  things we need to re-test ourselves.

Finally - between library updates and integration updates, this
integration is well-positioned to support future updates. I have a
proof-of-concept patch for device automations, and the underlying
library now supports subclassing authentication (which clears the way
for us to use OAuth authentication for Awair).

* Wrap test fixture in mock_coro

Truthfully I'm not sure why this was passing on my local dev
environment, but I was developing with python 3.8 before. After
installing python 3.7, I was able to reproduce the CI failures and fix
them.

* Fix broken tests after #34901 and/or #34989

* Do not rename sensors so broadly

We're going to keep the sensors named as they were before, pending the
outcome of any decisions around the air_quality component and what names
should be standardized for air-quality-like devices.

If standardized names are selected (which does seem likely), then we
will update this integration to match them - at which point, it would be
a breaking change.

But for now, we'll keep names mostly identical to what users had before.

Notable in this commit is that we generate the entity_id ourselves,
rather than just allowing it to be auto-generated from the name
attribute. This allows us to provide more human friendly names, while
keeping the old format for entity ids. For example, given an Awair
device called "Living Room", we'll generate an entity id of
"sensor.living_room_voc" but show set the name of the device to "Living
Room Volatile organic compounds".

* Support import from config.yaml

We'll create a config entry from config.yaml the first time we're
loaded, and then defer to it from then on.

We ignore all keys other than the access_token, since we no longer need
to deal with per-account rate-limits (rather, everything is per-device
now).

* Add myself to CODEOWNERS

Since I wrote the initial integration, and now this re-write, it feels
appropriate for me to take care of the integration along with `danielsjf`.

* Remove name mangling

* Update homeassistant/components/awair/manifest.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/awair/config_flow.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/awair/sensor.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/awair/sensor.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Address some review feedback

* Set up reauth flow in a job, rather than awaiting

* Remove unnecessary title string

* Remove unnecessary config schema checking

As pointed out in review, because this comes in via import from
`configuration.yaml`, we can rely on the `PLATFORM_SCHEMA` validation instead.

* Fix tests

* Set unique_id appropriately for legacy devices

For users who have had this integration already installed (and who have
updated their home assistant installation sometime in recent history),
we want to ensure that unique_id's are set to the same thing as before,
to facilitate the upgrade process.

To do that, we add an additional property to the `SENSOR_TYPES` dict
(`ATTR_UNIQUE_ID`) which allows us to map modern sensor names from
python_awair to what older versions called them - ie: `humidity` ->
`HUMID`. We then use that value when constructing the unique ID. This
should allow users to upgrade and not lose configuration even if entity
IDs would otherwise change (because we have changed the name format that
generates entity IDs).

One note is that for the gen1 `DUST` sensor, we have to treat it
differently. This integration used to call that a "PM2.5" sensor, but
the unique_id generated would be something like `awair_12345_DUST`. So
we special-case that sensor, and do the same thing. We do not need to
special-case the PM10 sensor for gen1 devices, because we didn't create
a PM10 sensor in the past (we do now, because the "DUST" sensor is
really a hybrid PM2.5/PM10 sensor).

* Patch async_setup_entry for two tests

* Update awair config_flow to require / use an email address for unique_id

Also, only start one re-auth flow.

* Add a few more tests, try to get coverage up.

* Add another test

* Move attribution to device_state_attributes

* Don't require email

* Switch from Union[dict, None] to Optional[dict]

* Use a mock where requested

* Fix missing constant rename

* Use async_create_task

* Bump test coverage a bit for config_flow

* s/CONF_UNIQUE_ID/UNIQUE_ID/g

* Add warning about deprecated platform config

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-21 21:46:07 +02:00
J. Nick Koston
1de97e3a35 Fix logbook filtering by entity id (#36973)
* Fix logbook filtering by entity_id

* remove debug
2020-06-21 14:34:47 -05:00
Chris Talkington
a6536bb622 Update rokuecp to 0.5.0 (#36975)
* update rokuecp to 0.5.0

* Update requirements_all.txt

* Update requirements_test_all.txt

* add aiohttp mock for media-player

* Create media-player-close.xml

* Update media-player-close.xml

* Create media-player-play.xml

* Create active-app-pluto.xml

* Update apps.xml

* Update apps-tv.xml

* Create media-player-pause.xml

* Create media-player-live.xml

* Update __init__.py
2020-06-21 14:29:39 -05:00
J. Nick Koston
59e43ab6e4 Prefilter more logbook events in sql (#36958)
* Prefilter more logbook events in sql

Prefilter sensor events in _keep_event before humanify

Cache static attribute lookup

Reduces logbook execution time by ~35%

* fix mocking in benchmark

* Update tests for logbook users
2020-06-21 10:50:58 -07:00
Tom Brien
29f128eaad Handle Mobile App registrations for device names containing of only emoji (#36957) 2020-06-21 18:12:15 +02:00
AJ Schmidt
d88efe405e Add armed_night state for AlarmDecoder (#36695)
* add arm night state

* quick refactor
2020-06-21 17:03:36 +02:00
Brian Rogers
f866ff5f2b Rachio Rain Delay Switch Update (#36909)
* Rain Delay Switch Update

* Fix sort

* Update names
2020-06-21 08:53:54 -05:00
Ziv
efa0e1b2ac Dynalite integration - upgrade library version (#36954) 2020-06-21 11:42:34 +02:00
Chris Talkington
4a432781ca Reduce roku scan interval by 5s (#36966) 2020-06-21 11:40:05 +02:00
Michael J. Kidd
6c2dc521a3 Adding PM2.5 and PM2.5 24hr Average (#36967)
* Adding PM2.5 and PM2.5 24hr Average

* Corrected unit to const.py CONCENTRATION_MICROGRAMS_PER_CUBIC_METER

* Imported CONCENTRATION_MICROGRAMS_PER_CUBIC_METER for PM2.5 units

* Fixed code formatting with `black`
2020-06-20 22:14:35 -06:00
HomeAssistant Azure
b112a742b2 [ci skip] Translation update 2020-06-21 00:10:03 +00:00
Franck Nijhof
46cb9cff41 Fix ESPHome discovery for ignored config entries (#36964) 2020-06-21 00:39:08 +02:00
Fredrik Erlandsson
9f65dcf4ba Fix unique_id in Zeroconf flow (#36948) 2020-06-20 22:59:25 +02:00
Davide Varricchio
13c0a59e28 Bump pyaehw4a1 to v.0.3.5 (#36949) 2020-06-20 19:14:27 +02:00
Bram Kragten
7d69b90eac Update frontend to 20200620.0 and add redirects (#36953) 2020-06-20 19:05:22 +02:00
Franck Nijhof
60dd94d5b0 Upgrade ptvsd to 4.3.2 (#36950) 2020-06-20 14:25:02 +02:00
avocadio
2196bd66c7 Add TTS support to Heos (#35386)
* TTS seems unsupported on heos media player #32862 

TTS seems unsupported on heos media player #32862 

The type media_type music which is required by TTS was not covered.

* Update homeassistant/components/heos/media_player.py

Co-authored-by: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com>

* Update to test_media_player.py

Test for TTS support to Heos #35386

* Update test

Add TTS support to Heos #35386

* Update to test_play_media_music

assert set to "Unable to play music: Failure (1)" seems to cause an issue.

* test_play_media_music

syntax

Co-authored-by: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com>
2020-06-20 09:54:44 +02:00
Paulus Schoutsen
8895f9b70a Remove unused webhook dependency from device automation (#36946) 2020-06-20 09:11:17 +02:00
Kit Klein
a074cf4afd Use chip id in Konnected pro boards (#36940)
* use chip id in pro boards

* cleaner failover
2020-06-20 08:39:04 +02:00
ehendrix23
2fd6431cff Allow harmony activity change on start of switching activity (#36699)
* Allow activity change on start of switch

Allow activity to be updated when a switch to a new activity is initiated instead of when it is completed.

* Updates based on feedback

Some items are not required to be done as YAML is not used anymore.
Cleaned-up some code.

* Fix for change on how to set callbacks

How callbacks are set now one has to set the new_activity and new_activity_starting as well, even just with None.

* Added callback update

Added so that when it is changed in the UI the callbacks will be changed as well.

* Added test cases for notify setting

Added test cases for config flow to test new setting for activity notifications.
2020-06-19 21:50:42 -05:00
Paulus Schoutsen
5642027ffb Improve after_dependencies handling (#36898) 2020-06-19 17:24:33 -07:00
HomeAssistant Azure
93272e3083 [ci skip] Translation update 2020-06-20 00:09:26 +00:00
J. Nick Koston
2b5e7c2611 Fix recorder stopping after unserializable state (#36937)
* Ensure unserializable states do not collapse recording

* augment test coverage

* fix wal mode being set every time
2020-06-19 12:03:06 -05:00
Franck Nijhof
683d960fa5 Add discovery to NUT integration (#36827)
* Add discovery to NUT integration

* implement async_step_zeroconf

* Update test to make sure unique id not set

* Remove host/port import when coming from discovery, add title placeholders

* fix mis-paste

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-06-19 17:33:01 +02:00
jjlawren
d445c16697 Handle Plex Live TV sessions (#36919) 2020-06-19 17:12:47 +02:00
Markus Bong
e4df0481da Fix devolo brand name (#36865) 2020-06-19 17:04:31 +02:00
jjlawren
3d1a5f76b2 Fix cert_expiry time-based tests v2 (#36934) 2020-06-19 16:19:43 +02:00
Paulus Schoutsen
263bdaa565 Make panel_custom backwards compatible (#36926) 2020-06-19 16:03:39 +02:00
craiggenner
212660330f Cope with attribute errors being converted (#36911) 2020-06-19 05:36:56 +02:00
HomeAssistant Azure
f83d4e524b [ci skip] Translation update 2020-06-19 00:10:18 +00:00
jjlawren
6bffd9a892 Fix cert_expiry time-based tests (#36924) 2020-06-19 00:26:34 +02:00
Robert Svensson
02e03340df Light control support to Axis devices (#36611)
* IR light support to Axis devices

* Change how to read light state

* Add tests

* Bump dependency to v32

* Assert variables passed to set_intensity
2020-06-18 23:27:08 +02:00
jjlawren
e92e26b73a Add expiration timestamp to cert_expiry sensors (#36399)
* Add expiration timestamp to cert_expiry sensors

* Clear timestamp if cert becomes invalid

* Use timezone-aware timestamps

* Use DataUpdateCoordinator, split timestamp to separate sensor

* Use UTC, simpler add/remove handling

* Review fixes

* Fix incomplete mock that fails in 3.8

* Use static timestamps, improve helper method name
2020-06-18 18:29:46 +02:00
Paulus Schoutsen
f69fc79fd1 Bump Netdisco to 2.7.1 (#36891) 2020-06-18 13:08:43 +02:00
J. Nick Koston
e7d982ee11 Improve db performance of state change events and reduce overall db size (#36883)
* Add old_state_id to states, remove old/new state data from events since it can now be found by a join

* remove state lookup on restart

* Ensure old_state is set for exisitng states
2020-06-17 22:26:41 -05:00
Paulus Schoutsen
94132e5572 Add internal/external url to safe mode (#36894) 2020-06-17 17:14:48 -07:00
HomeAssistant Azure
7920c5e5e8 [ci skip] Translation update 2020-06-18 00:08:13 +00:00
Paulus Schoutsen
df59f87f34 Upgrade requests to 2.24.0 (#36886) 2020-06-17 16:26:29 -07:00
Paulus Schoutsen
c0a2e6b655 Merge pull request #36885 from home-assistant/rc 2020-06-17 16:23:37 -07:00
Paulus Schoutsen
b4b2302e04 Fix test 2020-06-17 16:23:10 -07:00
Phil Bruckner
098d82f4d3 Fix yr HTTP error handling (#36889) 2020-06-17 13:40:22 -07:00
Phil Bruckner
2883aacfa3 Fix yr HTTP error handling (#36889) 2020-06-17 13:40:04 -07:00
jjlawren
ec440dface Bump plexwebsocket to 0.0.11 (#36879) 2020-06-17 15:28:22 -05:00
J. Nick Koston
3d385796da Improve logbook performance when no entities are filtered (#36842)
Various tweaks to reduce the python overhead of
the lazy class
2020-06-17 13:23:42 -07:00
Paulus Schoutsen
3e14b2dc61 Bumped version to 0.111.4 2020-06-17 12:53:30 -07:00
Paulus Schoutsen
7547a92479 Upgrade pymetno (#36880) 2020-06-17 12:22:56 -07:00
Fredrik Erlandsson
625c2aa19d Fix Daikin zeroconf discovery flow error (#36868) 2020-06-17 12:22:35 -07:00
Robert Svensson
ec69edcb79 Bump Axis dependency to fix issue where ports dont initialize (#36860) 2020-06-17 12:18:26 -07:00
fb22
f823afeadc Fix llamalab_automate notify priority (#36845) 2020-06-17 12:18:25 -07:00
Paulus Schoutsen
903db07feb Onboarding to validate redirect uri (#36863) 2020-06-17 12:13:28 -07:00
Paulus Schoutsen
43cee39528 Upgrade pymetno (#36880) 2020-06-17 12:12:50 -07:00
jjlawren
d5cc3208af Discover controllable Plex clients using plex.tv (#36857) 2020-06-17 12:04:47 -07:00
Robert Svensson
94c8d74a66 Bump Axis dependency to fix issue where ports dont initialize (#36860) 2020-06-17 11:07:19 -07:00
Fredrik Erlandsson
b3c851502d Fix Daikin zeroconf discovery flow error (#36868) 2020-06-17 16:53:29 +02:00
Jeffery To
a74e35795c Use the built-in importlib.metadata library in Python 3.8+ (#36225)
importlib_metadata is a backport of this library for Python 3.7 and
older.
2020-06-17 15:21:14 +02:00
Josef Schlehofer
b464096edb Upgrade youtube_dl to version 2020.06.16.1 (#36870) 2020-06-17 15:05:01 +02:00
bsmappee
5228282f69 Renew Smappee (sensors and switches) (#36445)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-17 13:28:28 +02:00
Sean Nyekjær
fd67a079db Add ebusd HotWaterHeader ActualTemperature and OperationMode (#34921) 2020-06-17 13:26:19 +02:00
Bas Nijholt
3ae4eba457 Bump aiokef 0.2.12 (#36753) 2020-06-16 23:15:49 -07:00
marawan31
dba326f16b Add precipitation probability to weather forcast (#36019)
* Add precipitation probability attribute

* bump env_canada version to 0.0.38 in manifest

* bump env_canada version to 0.0.38 in requirements

* Add support for percipiation probability

* formated project with black

* make sure prob is an int

* fix build break

* update usage to correct naming

* revert bad change and apply fix build break

* add tests
2020-06-17 07:39:33 +02:00
HomeAssistant Azure
2b06fbbcf0 [ci skip] Translation update 2020-06-17 00:11:42 +00:00
indykoning
d278dd9477 Add growatt battery devices (#34773)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 21:16:29 +02:00
Adam Belebczuk
fe03c9da68 Update Wemo state when changing state (#36647) 2020-06-16 21:14:04 +02:00
Robert Van Gorkom
a6a6a7b69c Add Withings webhooks (#34447)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 20:16:18 +02:00
Tom
29df13abe9 Fix Plugwise schema name display and non_device_class icons (#36815)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com>
2020-06-16 14:49:13 +02:00
starkillerOG
6db5ff98ed DenonAVR Config Flow (#35255)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 14:46:39 +02:00
Philip Allgaier
25607c7129 Use MDI CPU icon for glances (#36736)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 14:44:37 +02:00
Michael Auchter
58f1d1754e Fix remote_rpi_gpio switch inversion (#34390)
This removes the double-inversion of switches on the remote_rpi_gpio
platform.

Fixes #24571
2020-06-16 14:42:21 +02:00
fb22
6273ad85f8 Fix llamalab_automate notify priority (#36845) 2020-06-16 12:42:12 +02:00
Kevin Fronczak
a68af0a3a9 Add blink tests (#36672)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 11:05:33 +02:00
etheralm
f8de0594b9 Add support for Dyson Pure HP04 purifying heater + fan (#34537)
* fix unnecesary checks

* change ClimateDevice to ClimateEntity

* Clean up

* Formatting

* Fix tests

* Clean tests

* Clean up tests

* Fix device mock

* Use safer patch target path

* Extract constant

* Remove not needed property

* Guard for missing target temperature

* Use async_mock mocks

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-16 05:31:11 +01:00
Zac West
8541ae0360 mobile_app: Camera Stream Webhook (#36839) 2020-06-15 19:09:53 -07:00
Paulus Schoutsen
dfc345a921 Merge pull request #36840 from home-assistant/rc 2020-06-15 17:32:52 -07:00
HomeAssistant Azure
87f236c05c [ci skip] Translation update 2020-06-16 00:03:16 +00:00
Paulus Schoutsen
0127974f09 Bumped version to 0.111.3 2020-06-15 16:35:41 -07:00
Paulus Schoutsen
108082fd07 Fix cloudhooks coming in for non existing webhooks (#36836)
* Fix cloudhooks coming in for non existing webhooks

* Fix tests"
2020-06-15 16:35:32 -07:00
kennedyshead
61d6bd0cb5 Bump aioasuswrt to 1.2.6 (#36820)
Co-authored-by: magnusknutas <magnus@thefarm.se>
2020-06-15 16:35:32 -07:00
Paulus Schoutsen
3a2138131c Fire config changed event during start (#36812) 2020-06-15 16:35:31 -07:00
Tom Harris
ec65ebacd3 Fix X10 devices and adding default links (#36807) 2020-06-15 16:35:30 -07:00
David F. Mulcahey
b45e49902c Bump ZHA quirks (#36797) 2020-06-15 16:35:29 -07:00
N1c093
12781bf842 Fix nanoleaf attribute when running an effect (#36794) 2020-06-15 16:35:28 -07:00
J. Nick Koston
d0ca8e62a4 Fix history graphs with mysql/mariadb (#36769) 2020-06-15 16:35:27 -07:00
Franck Nijhof
c37cd835b9 Upgrade wled 0.4.3 (#36760) 2020-06-15 16:35:27 -07:00
Paulus Schoutsen
3ee3ae7633 Fix cloudhooks coming in for non existing webhooks (#36836)
* Fix cloudhooks coming in for non existing webhooks

* Fix tests"
2020-06-15 16:30:40 -07:00
Franck Nijhof
02f174e2e6 Add support for multiple states/zones in conditions (#36835)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-16 00:53:13 +02:00
Erik Montnemery
16cf16e418 Update MQTT tests to not create duplicated config entries (#36833) 2020-06-15 15:38:56 -07:00
Paulus Schoutsen
de12c21ce7 Fire config changed event during start (#36812) 2020-06-15 15:22:53 -07:00
J. Nick Koston
b8c9f67533 Remove context_id from logbook api (#36796)
Reduces size of api response, execution, and download time.
2020-06-15 16:13:07 -05:00
Franck Nijhof
ba73ac12ba Add support for multiple entity_ids in conditions (#36817) 2020-06-15 22:54:19 +02:00
J. Nick Koston
0a219081ea Only process logbook timestamps for events we will keep (#36792)
* Only process logbook timestamps for events we will keep

Since we group by minute we were previously processing
every timestamp. We can avoid this by making all the
minute checks use the unprocessed datetime since
the groupings will be the same regardless of timezone.

This reduces the number of datetime object recreations
by at least an order of magnitude.
2020-06-15 14:54:20 -05:00
J. Nick Koston
b0163b65c6 Use states to avoid decoding logbook state changed events. (#36768)
avg 4.43s -> 1.88s
2020-06-15 13:53:05 -05:00
J. Nick Koston
83e3f680bf Improve history api performance part 4 (#36783)
This builds on #35822

Minimize the amount of data selected from
the database

Testing:

History API Response time for 1 day
Average of 10 runs with minimal_response

Before: 9.47s
After: 4.43s
2020-06-15 11:02:36 -07:00
J. Nick Koston
e443dc1274 Speed up logbook with a lazy event decoder (#36730) 2020-06-15 10:53:38 -07:00
N1c093
717a21dc7b Fix nanoleaf attribute when running an effect (#36794) 2020-06-15 18:17:54 +02:00
kennedyshead
1b6f0b78aa Bump aioasuswrt to 1.2.6 (#36820)
Co-authored-by: magnusknutas <magnus@thefarm.se>
2020-06-15 14:14:48 +02:00
michaeldavie
e8d4a25635 Add missing dependencies for Environment Canada (#36806) 2020-06-15 13:54:58 +02:00
Tom Harris
162e502962 Fix X10 devices and adding default links (#36807) 2020-06-15 13:49:39 +02:00
Franck Nijhof
3cc94f7d6a ConfigFlow default discovery without unique ID (#36754) 2020-06-15 13:38:38 +02:00
Paulus Schoutsen
dfac9c5e03 Fix TTS key by hashing options values too (#36813) 2020-06-15 13:33:26 +02:00
Ian Harcombe
c96458c7e4 Convert MetOffice to use UI for configuration (#34900)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-15 12:02:25 +02:00
AJ Schmidt
9a867cbb75 Add alarmdecoder brand specific arming sequences (#36692)
* add adext

* rm new config parameter

* update adext
2020-06-15 10:05:45 +02:00
HomeAssistant Azure
b15caf31a9 [ci skip] Translation update 2020-06-15 00:03:32 +00:00
Tom Schneider
0331ebdd47 Add HVV integration (Hamburg public transportation) (#31564)
Co-authored-by: springstan <46536646+springstan@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-15 00:15:20 +02:00
Ville Skyttä
0b7d2aa4d7 Add unique id to huawei_lte config entries (#36765) 2020-06-14 23:05:43 +02:00
David F. Mulcahey
821de0e369 Bump ZHA quirks (#36797) 2020-06-14 14:47:36 -04:00
Paulus Schoutsen
02bcdf5162 Fix translations download (#36770) 2020-06-14 11:38:05 -07:00
Benoit Louy
e7e2f4e786 Set default pjlink timeout (#36781)
* make pjlink timeout configurable

* fix format

* remove timeout configuration and use a default value instead
2020-06-14 20:23:59 +02:00
Eugene Prystupa
e969d364e6 Refactor plum_lightpad (#36761) 2020-06-14 13:59:49 +02:00
Franck Nijhof
4862f6d516 Upgrade wled 0.4.3 (#36760) 2020-06-13 22:29:33 -07:00
Alexei Chetroi
36ed3b1177 Add pi_heating/cooling_demand state attributes (#36775) 2020-06-14 00:25:25 -04:00
J. Nick Koston
9cc20fc6b8 Fix history graphs with mysql/mariadb (#36769) 2020-06-13 21:22:16 -07:00
HomeAssistant Azure
dd239661e7 [ci skip] Translation update 2020-06-14 00:03:01 +00:00
Martin
0bbb56dd05 Add sensor platform for vicare integration (heatpump) (#34385)
* Add sensor platform for vicare (heatpump)

* Formatting and fixes

* Formatting and fixes 2

* Fixes and formatting 3

* Fixes and formatting 4

* Add binary_sensor and more sensors

This moves some more climate attributes to sensors and adds
binary_sensors

* Move ActiveError back to climate component

The data returned by ActiveError is more complex.
It takes further investigation on how to interpret it a s a binary sensor.
Therefore it is moved back as an attribute of the climate component

* Update PyViCare library

* PR changes

* PR changes 2

Co-authored-by: Hans Oischinger <hans.oischinger@gmail.com>
2020-06-13 21:53:42 +02:00
Bram Kragten
0bfcb99c04 Updated frontend to 20200613.0 (#36758) 2020-06-13 20:17:58 +02:00
Robert Van Gorkom
bdd255176c Poll all status data in Vera (#35703)
* Vera now polls for all status data, no only incremental.
Vera polling is not handled using hass event loops with proper backoffs.

* Using long polling.

* Addressing PR feedback.

* Addressing PR feedback.
Adding controller stop on config unload.
2020-06-13 16:36:50 +02:00
Bram Kragten
20428e670b Update frontend to 20200603.3 (#36751) 2020-06-13 12:36:16 +02:00
Robert Svensson
78af0a4705 Make Axis non-privileged users work again (#36731) 2020-06-13 12:35:52 +02:00
J. Nick Koston
65e9e4a2f3 Fix history timezone with postgres and other dbs (#36746) 2020-06-13 12:32:01 +02:00
Erik Montnemery
922b332766 Update config_entry_flow test test_user_has_confirmation (#36633)
* Update test_user_has_confirmation

* Rewrite test according to review comment
2020-06-13 06:43:13 +02:00
Paulus Schoutsen
1ffa8fcbba Platforms cleanup when adding entity fails (#36742) 2020-06-12 17:54:46 -07:00
Franck Nijhof
785786ecd1 Upgrade pytest-cov to 2.10.0 (#36720) 2020-06-13 02:37:59 +02:00
Shane Qi
b78ad7c2b8 Implement device_info for LutronCasetaDevice (#36706) 2020-06-13 02:27:48 +02:00
HomeAssistant Azure
4412ce3b86 [ci skip] Translation update 2020-06-13 00:03:58 +00:00
Franck Nijhof
238430136e Migrate script to use describe_event for logbook (#36729) 2020-06-12 16:44:29 -07:00
mvn23
15113ae854 Add opentherm_gw.set_hot_water_setpoint service (#34579) 2020-06-12 23:15:04 +02:00
mreiling
7ce6ae9421 Bump pynx584 to 0.5 (#36615) 2020-06-12 15:54:19 -04:00
mdegat01
f9bc0c9dab Add influx 2.0 and InfluxCloud support to InfluxDB integration (#35392) 2020-06-12 21:29:46 +02:00
Franck Nijhof
bf95658e24 Fix logbook filtering for described events (#36727) 2020-06-12 20:45:17 +02:00
Paulus Schoutsen
3c57475c8f Guard OwnTracks writing state before hass available (#36680) 2020-06-12 09:27:51 -07:00
Paulus Schoutsen
e8b16f0dfd Enforce unique ID for Hassio discovery (#36671) 2020-06-12 17:38:38 +02:00
Fredrik Erlandsson
c1cf3679aa Fix Daikin discovery (#36704) 2020-06-12 17:25:18 +02:00
zewelor
871afd2bf2 Remove Yeelight model to device type mapping (#36658) 2020-06-12 14:51:48 +02:00
Alexei Chetroi
5595ef0783 Drop ZHA sensor for Analog/Multistate input clusters (#36696) 2020-06-12 06:08:11 -04:00
Alexei Chetroi
5a3e0c6b25 Cleanup zha.storage from stale devices (#36700) 2020-06-12 06:07:34 -04:00
Paulus Schoutsen
a8e9ccbf1a Improve cloud error handling (#36670) 2020-06-11 22:56:00 -07:00
MarBra
44708ed8bb Add timers to xiaomi_miio vacuum (#35417)
* Add timers to xiaomi_miio vacuum

* Add timezone

* Add cron and next_schedule

* Use next_schedule from backend library

* Use as_utc from utils
2020-06-12 06:39:19 +02:00
Alexei Chetroi
8e44d797a3 Fix ZHA Metering channel formatting method (#36697)
Since zigpy change to support bitmap classes, formatting string was incorrectly generated for the newly joined devices with SmartEnergy metering clusters.
2020-06-11 22:37:07 -04:00
HomeAssistant Azure
419a92db43 [ci skip] Translation update 2020-06-12 00:07:41 +00:00
zewelor
986853d497 Fix nightlight support detection (#36656) 2020-06-11 15:46:00 -07:00
jjlawren
d4d233536f Fix missing options in Plex config entry (#36683) 2020-06-11 15:45:00 -07:00
Alexei Chetroi
21acdbbbfd Refactor ZHA Entity availability tracking (#36645)
* Refactor ZHA entity availability

ZHA entity availability tracks on underlying ZHA device availability.

* Update device status without signal.

* Update tests.

* Fix tests.

* Tests for restored devices availability.

* Guard against empty last_seen entry

Refactor device loading a bit.
2020-06-11 17:21:08 -04:00
Franck Nijhof
0146f35687 Fix packages when config schema is fully deprecated (#36674)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-11 10:59:52 -07:00
J. Nick Koston
2d5faaf3f8 Remove powerwall attributes no longer present in latest firmware (#36667) 2020-06-11 10:15:02 -07:00
Paulus Schoutsen
dd6d18102f Update translations 2020-06-11 10:13:42 -07:00
J. Nick Koston
14d715e7fc Update pymyq for latest api requirements (#36666) 2020-06-11 08:40:51 -07:00
starkillerOG
b820b7c47d Xiaomi Miio gateway: add ip to zeroconf discovery title (#36653)
* add ip to discovery title of xiaomi miio gateway

* add flow title to identify multiple gateways by IP

* clearify gateway token

* black formatting

* grammer improvements

Co-authored-by: Franck Nijhof <git@frenck.dev>

* grammer improvements

Co-authored-by: Franck Nijhof <git@frenck.dev>

Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-06-11 17:12:19 +02:00
Tony Phan
afe4647896 Add icon_type as configuration variable (#36594) 2020-06-11 00:11:46 -07:00
Alexei Chetroi
4df186787a Update ZHA dependencies (#36646) 2020-06-10 23:05:14 -04:00
HomeAssistant Azure
b353f45d84 [ci skip] Translation update 2020-06-11 00:02:36 +00:00
Yue Kang
fc2c195ed4 Update Baidu TTS to support more voice (#36532) 2020-06-11 01:00:56 +02:00
Janitha Karunaratne
f1d5f95f7c Allow specifying port for wake_on_lan (#36510) 2020-06-11 00:05:24 +02:00
Franck Nijhof
59f935beb0 Use async_on_remove for WLED DataUpdateCoordinator (#36640) 2020-06-10 16:02:45 -06:00
Franck Nijhof
ddb85cee7b Remove internal qa scale from numato integration (#36629) 2020-06-10 22:55:17 +02:00
Erik Montnemery
14bff5a375 Abort other config flows on import (#36608)
* Abort other flows on import

* Add test
2020-06-10 22:46:14 +02:00
springstan
9311b02369 Use LENGTH_FEET constant (#34053) 2020-06-10 21:38:29 +02:00
Kevin Fronczak
82090f5060 Add options flow to Blink (#35645) 2020-06-10 18:38:17 +02:00
Franck Nijhof
186a299215 Fix name of devolo_home_control integration (#36627) 2020-06-10 18:36:34 +02:00
Fredrik Rambris
00068e88b0 Add ue_smart_radio multiple devices support (#36575) 2020-06-10 18:35:09 +02:00
Rami Mosleh
2c1a76cf92 Add Speedtestdotnet config_flow (#36254) 2020-06-10 18:33:48 +02:00
Xiaonan Shen
c65e72886c Fix command line sensors removing quotes with template (#35559)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-10 18:31:59 +02:00
Franck Nijhof
e13f206a06 Merge branch 'master' into dev 2020-06-10 16:58:51 +02:00
Franck Nijhof
8d405c4585 Upgrade pre-commit to 2.5.1 (#36623) 2020-06-10 15:22:42 +02:00
Igor Gocalinski
2ac4d30736 Add delay in emulated_hue after PUT (#35307) 2020-06-10 15:15:32 +02:00
Fredrik Erlandsson
fa17e6d5ab Fix Daikin config flow for zeroconf devices (#36571) 2020-06-10 15:04:59 +02:00
SukramJ
212b9df87d Bump dependency & add devices for HomematicIP Cloud (#36595)
* update dependency and test data

* Add test for HmIP-SWDO-PL

* Add device HmIP-MOD-HO

* Fix test
2020-06-10 11:34:14 +02:00
J. Nick Koston
44552937b6 Fix missing space in powerwall notification message (#36603) 2020-06-10 11:29:56 +02:00
Paulus Schoutsen
8f3c84b349 Mark config dependency of frontend (#36587) 2020-06-09 23:27:47 -07:00
Ville Skyttä
29b8f76e57 Use past tense in messages for already invalidated deprecated configs (#36591) 2020-06-09 23:26:55 -07:00
Robin Wohlers-Reichel
29838ce1ed Bump solax library to 0.2.3 (#36600)
* Bump solax library to 0.2.3

* oops
2020-06-09 19:34:46 -06:00
Martin Hjelmare
5cf753422b Bump coronavirus to 1.1.1 (#36614) 2020-06-09 19:34:16 -06:00
HomeAssistant Azure
ded5329f03 [ci skip] Translation update 2020-06-10 00:05:07 +00:00
Paulus Schoutsen
e3fc59ff3d Bump hass-nabucasa to 0.34.6 (#36613) 2020-06-09 16:40:08 -07:00
Bram Kragten
6166a7191b Escape <> in owntracks translations (#36612) 2020-06-09 16:09:34 -07:00
mreiling
1169ac568b Change nx584 SCAN_INTERVAL to 10 seconds (#36581) 2020-06-09 21:02:08 +02:00
Donnie
1ab1503641 Fix nanoleaf incorrect effect update (#36517) 2020-06-09 11:26:37 -07:00
Franck Nijhof
2abd3844cf Fix mobile_app missing state in sensor registration (#36604) 2020-06-09 11:06:52 -07:00
Franck Nijhof
854bdfb6f2 Upgrade pre-commit to 2.5.0 (#36596) 2020-06-09 18:44:22 +02:00
mreiling
5f8dcd45c1 Add NX584 alarm binary_sensor zone_number attribute (#36552) 2020-06-09 14:19:46 +02:00
Felipe Martins Diel
38bb8ef4d2 Clean up command learning in the Broadlink integration (#36318) 2020-06-09 14:15:46 +02:00
Paulus Schoutsen
7cc3102209 Fix default for loading games file ps4 (#36592) 2020-06-09 10:23:26 +02:00
Johan Henkens
525e220395 Remove EU code from honeywell integration (#36456) 2020-06-09 09:24:47 +02:00
HomeAssistant Azure
140fd5adaf [ci skip] Translation update 2020-06-09 00:05:22 +00:00
Ian
5b2bf8fd17 Add status_light to Sonos set_option service (#35515) 2020-06-08 23:41:12 +02:00
Bas Nijholt
5e2de4531f bump aiokef to 0.2.10 (#36574)
0.2.9 generated a lot of calls on the event loop.
2020-06-08 14:05:55 -07:00
Paulus Schoutsen
c33edbe5bb Use TestCase.addCleanup (#36560) 2020-06-08 12:26:40 -07:00
Franck Nijhof
85ba29012f Fix mobile_app sensor re-registration handling (#36567) 2020-06-08 21:11:37 +02:00
StevusPrimus
5975ec340b Add service select scene to Yamaha Hifi media player (#36564)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-08 19:31:58 +02:00
Paulus Schoutsen
3adfb86a19 Mobile app fixes (#36559) 2020-06-08 10:20:25 -07:00
Franck Nijhof
b3cbce3566 Fix mobile_app registering/update sensor values with an unknown state (#36566) 2020-06-08 10:07:05 -07:00
ehendrix23
7a2820ded9 Update aioharmony to include support for XMPP connectivity to Hub (#36485)
* Update aioharmony to 0.2.3

* Update aioharmony to 0.2.4
2020-06-08 09:55:05 -05:00
Giel Janssens
af4fb03230 Sonos queue (#35817)
* Add playbase as a model

* Sonos queue

* Match counting of the queue with service play_queue

* Add service remove_from_queue

* Change description of remove_from_queue

* Use ATTR_QUEUE_POSITION instead of ATTR_CURRENT_QUEUE_POSITION

* Removed queue and show queue_position only when playing local

* Add property queue_position

* Return None instead of -1

* Change docstring from -1 to None
2020-06-08 16:37:35 +02:00
mreiling
a5da21a426 Add services to bypass and unbypass zones on NX584 (#36401)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-08 15:55:50 +02:00
z00nx 0
16e36dca97 Updated tesla-powerwall to 0.2.11 (#36568) 2020-06-08 08:48:58 -05:00
Jonas Thuresson
f522c6c8c7 Add Xiaomi miio vaccum goto service (#35737)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-08 14:57:47 +02:00
Alexei Chetroi
1bdbe90d2a Prevent double ZHA channel initialization (#36554)
* Preven double channel initialization.

* Use a setter for setting ZHA device availability.
2020-06-08 08:54:52 -04:00
eyager1
8ed1b1782e Change logging of successful scans to debug priority (#36538) 2020-06-08 11:30:36 +02:00
Markus Bong
31dd06bd12 Improve devolo Home Control (#36557) 2020-06-08 09:29:09 +02:00
Eugene Prystupa
de7bbd3e24 Add plum_lightpad unique ids (#36549) 2020-06-07 21:52:03 -07:00
Jörg Thalheim
fbbc681ad4 Fix intent component initialisation (#36064)
The intent component expect this method from every module that is called intent.
Fixes #35522
2020-06-07 20:45:34 -07:00
shbatm
9e7f516d07 Add Z-Wave Notification Sensor support to ISY994 (#36548) 2020-06-07 20:00:53 -05:00
Hedda
066784c88f Update ZHA config flow Zigbee radio description (#35607)
* Update ZHA config flow Zigbee radio description
Update ZHA config flow Zigbee radio descriptions to match docs https://github.com/home-assistant/home-assistant.io/pull/13437

* Make protocol plus model names more readable for end-users
* Update homeassistant/components/zha/core/const.py
Co-authored-by: Alexei Chetroi <lexoid@gmail.com>

* Update homeassistant/components/zha/core/const.py
Co-authored-by: Alexei Chetroi <lexoid@gmail.com>

* Update homeassistant/components/zha/core/const.py
Co-authored-by: Alexei Chetroi <lexoid@gmail.com>

* Zigbee radio description simplified and examples  listed in alphabetical order
2020-06-07 20:42:23 -04:00
HomeAssistant Azure
81355a0e23 [ci skip] Translation update 2020-06-08 00:02:43 +00:00
Franck Nijhof
215c7e0e14 Fix WLED power and brightness with WLED 0.10+ (#36529) 2020-06-07 23:37:58 +02:00
Markus Ressel
233284056a Upgrade XS1 component to xs1-api-client v3.0.0 (#36500) 2020-06-07 23:18:47 +02:00
Jens Østergaard Nielsen
56c69d9a25 Update ihcsdk to 2.7.0 (#36527) 2020-06-07 19:47:21 +02:00
Erik Montnemery
35cd6b9abf Remove unused parameter for MQTT discovery (#36526)
* Remove unused parameter for MQTT discovery

* Fix tests
2020-06-07 19:35:35 +02:00
Jeff Irion
823f27db5a Fix usage of states 'idle' and 'standby' for Android TV (#36509) 2020-06-07 10:10:20 -05:00
definitio
ad5101c5c0 Set state for MQTT entities to 'unavailable' when no connection to broker (#36479)
* Report 'unavailable' state when not connected

to MQTT broker

* Fix tests

* Rewrite to remove the polling

* Add tests

* Add some fixes
2020-06-07 09:21:16 +02:00
HomeAssistant Azure
3bf389639b [ci skip] Translation update 2020-06-07 00:03:28 +00:00
Joakim Plate
31973de2d5 Arcam config flow (#34384)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-06 13:43:28 -07:00
Josef Schlehofer
524b48be7d Upgrade youtube_dl to version 2020.06.06 (#36493) 2020-06-06 21:35:37 +02:00
Ville Skyttä
0c5ca3084e Add and fix type hints (#36501)
* Fix exceptions.Unauthorized.permission type

* Use auth.permission consts more

* Auth typing improvements

* Helpers typing improvements

* Calculate self.state only once
2020-06-06 20:34:56 +02:00
Paulus Schoutsen
49747684a0 Update netdisco (#36499) 2020-06-06 19:02:39 +02:00
Markus Bong
3495932eb0 Add devolo binary_sensor devices (#36370)
* initial commit

* small corrections

* fix linting error

* add new files to coveragerc

* rename devolo_sensor to devolo_device

* use correct import

* use binary_switch platform

* use binary_switch platform

* add binary_sensor to coverage

* adjustments according PR review

* make super call easier to read

* use f-string instead of concatenating

* update docstrings - remove device_id property

* add will_remove_from_hass
2020-06-06 10:10:05 -05:00
Tim van Cann
d73a4e1ed5 Add Avri config flow (#34288)
* Add config flow to Avri integration

* Add config flow validation

* Update .coveragerc

* Start adding config flow tests

* Fix failing test

* Fix pylint

* Update homeassistant/components/avri/config_flow.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Update homeassistant/components/avri/config_flow.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Fix import order

* Code review comments

* Update homeassistant/components/avri/sensor.py

Co-authored-by: J. Nick Koston <nick@koston.org>

* Remove device information

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-06-06 09:37:31 -05:00
matgad
14f5cab71d Bump version zigpy-cc (#36506) 2020-06-06 15:32:26 +02:00
Bram Kragten
29ab1935cb Update frontend to 20200603.2 (#36494) 2020-06-05 21:58:26 -07:00
Thomas Lovén
bdc098645b Add entity list to light and cover group attributes (#36477)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-06-05 21:23:52 -07:00
HomeAssistant Azure
492874c4a0 [ci skip] Translation update 2020-06-06 00:03:33 +00:00
J. Nick Koston
5e65d8d3c3 Upgrade zeroconf to 0.27.1 (#36277) 2020-06-05 14:33:26 -07:00
Alexei Chetroi
1c329ff708 Process events from ZHA Window Covering Remote (#36489) 2020-06-05 14:11:46 -07:00
jrester
e807274d7e Update tesla-powerwall to 0.2.10 (#36486)
modified:   homeassistant/components/powerwall/manifest.json
	modified:   requirements_all.txt
	modified:   requirements_test_all.txt
2020-06-05 15:03:17 -05:00
Franck Nijhof
4950cbee1c Fix iOS app crashing on None values in Zeroconf service info (#36490) 2020-06-05 12:43:58 -07:00
Anton Tolchanov
0bf64e9a2c Add @knyar as a codeowner for prometheus integration (#36487) 2020-06-05 21:29:06 +02:00
Alexey Kustov
3076fc5f25 Add notify_events notify integration (#36049)
* Add new notify_events notification component

* Fix manifest

* Formatting fix

* Black formatting + CODEOWNERS

* Fix requirements_all.txt

* Flake8 warning fix

* Isort fixes

* Update notify-events library requirement version

* Replace Exception to more suitable and update lib version

* Reformat integration according to "0007-integration-config-yaml-structure.md"

* Update homeassistant/components/notify_events/manifest.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Fix manifest + remove async

* Black formatting

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-05 21:26:56 +02:00
Aaron Bach
b51d81edba Clean up Tile refactor (#36450)
* Clean up Tile refactor

* Code review
2020-06-05 21:12:48 +02:00
Paulus Schoutsen
6faf9e8bbe panel_custom: Allow ES5+latest build, deprecate html (#36464)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-06-05 10:39:18 -07:00
J. Nick Koston
c0307dca3a Upgrade pysonos to 0.0.31 (#36483) 2020-06-05 10:30:20 -07:00
J. Nick Koston
5b94807923 Update myq for latest api changes (#36469) 2020-06-05 10:29:09 -07:00
Paulus Schoutsen
4170eb0f83 Use builtin mock (#36473) 2020-06-05 10:59:55 +02:00
Paulus Schoutsen
874c8fea03 Bump hass-nabucasa to 0.34.5 (#36461) 2020-06-05 00:11:40 -07:00
Lindsay Ward
a4204b440c Fix yeelight_sunflower hs_color using RGB values (#36470) 2020-06-05 08:40:50 +02:00
HomeAssistant Azure
f8e48a9230 [ci skip] Translation update 2020-06-05 00:05:36 +00:00
shbatm
04231bcb54 Fix error on empty UOM for ISY994 Climate Device (#36454) 2020-06-05 00:22:35 +02:00
Erik Montnemery
f170c80bea Bump pychromecast to 6.0.0 (#36414)
* Revert "Prevent race in pychromecast.start_discovery (#36350)"

This reverts commit 391983a0cf.

* Adapt to pychromecast 6.0.0
2020-06-04 20:32:00 +02:00
Paulus Schoutsen
394f16987d Fix invalid device info for Daikin devices (#36448) 2020-06-04 10:00:31 -07:00
Chris Talkington
f06c0a8b54 Add roku exception handling for service calls (#36328) 2020-06-04 09:59:39 -07:00
J. Nick Koston
36b157b85a Ensure verbose logging flag is respected. (#36444) 2020-06-04 09:51:06 -07:00
Aaron Bach
7a3c2e1f6c Add config flow for Tile (#36173)
* Overhaul Tile

* Adjust coverage

* Fix tests

* Code review

* Code review

* Remove unused config flow step

* Revert "Remove unused config flow step"

This reverts commit cb206e0446.

* Fix tests
2020-06-04 10:07:27 -06:00
Paulus Schoutsen
fae80621fb Guard blowing up converting 0 mired/kelvin (#35486) 2020-06-04 08:48:39 -07:00
Ziv
1edbdcb67b Fix Dynalite to explicitly check valid device class (#36418)
* changed back to check for class in DEVICE_CLASSES

* created a flow that would go through everything as it was blocking the commit
and the cv rules prevent an input that would get to that flow

* moved DEFAULT_COVER_CLASS from const to cover
2020-06-04 15:34:28 +02:00
Robert Svensson
99318b7b11 Remove Axis option to enable/disable camera (#36420) 2020-06-04 14:25:50 +02:00
Franck Nijhof
a6107198b9 Upgrade numpy to 1.18.5 (#36434) 2020-06-04 14:13:41 +02:00
Tom
203217c175 Add additional sensors to Plugwise integration (#36431) 2020-06-04 10:21:27 +02:00
Paulus Schoutsen
c316735996 Bump lokalise2 2.5.1 (#36430) 2020-06-04 10:17:25 +02:00
Robert Svensson
40829d9d76 Fix deCONZ groups don't report ctmax/min (#36432)
* Groups don't report ctmax/min
2020-06-04 10:15:30 +02:00
Paulus Schoutsen
68b077ffaa Add partial mobile app sensor validation (#36433) 2020-06-04 10:13:01 +02:00
Chris Talkington
8528b4db3a Update sonarr to 0.2.2 (#36429) 2020-06-04 09:39:49 +02:00
Tom
48d1bc7c13 Cleanup and improve Plugwise merges (#36406)
* Remove period from logging messages

* Save indentation using guard clauses

* Typo

* Walk other files

* Rewalk all files

* Not cleanup, but adding indicatd missing measurements

* Revert new sensors
2020-06-04 08:18:46 +02:00
HomeAssistant Azure
08e85696c1 [ci skip] Translation update 2020-06-04 00:04:15 +00:00
Quentame
e08ba6703c Bump python-synology to 0.8.2 (#36410)
* Bump python-synology to 0.8.2

* state_attributes to device_state_attributes

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-06-04 00:18:43 +02:00
Franck Nijhof
9ef14efa29 Bumped version to 0.112.0dev0 (#36417) 2020-06-03 22:52:34 +02:00
Ville Skyttä
fc395de511 Upgrade mypy to 0.780 (#36416)
http://mypy-lang.blogspot.com/2020/06/mypy-0780-released.html
2020-06-03 22:51:44 +02:00
J. Nick Koston
751e2f4125 Extend logbook api to be able to fetch a slice of data between two time boundaries. (#35847)
* Create logbook_timeslice api

* add end_datetime

* Add tests

* switch to end_time to match history api
2020-06-03 14:07:56 -05:00
1191 changed files with 36078 additions and 15850 deletions

View File

@@ -46,7 +46,6 @@ omit =
homeassistant/components/android_ip_webcam/*
homeassistant/components/anel_pwrctrl/switch.py
homeassistant/components/anthemav/media_player.py
homeassistant/components/apache_kafka/*
homeassistant/components/apcupsd/*
homeassistant/components/apple_tv/*
homeassistant/components/aqualogic/*
@@ -68,8 +67,8 @@ omit =
homeassistant/components/aurora_abb_powerone/sensor.py
homeassistant/components/avea/light.py
homeassistant/components/avion/light.py
homeassistant/components/avri/const.py
homeassistant/components/avri/sensor.py
homeassistant/components/azure_event_hub/*
homeassistant/components/azure_service_bus/*
homeassistant/components/baidu/tts.py
homeassistant/components/beewi_smartclim/sensor.py
@@ -79,7 +78,12 @@ omit =
homeassistant/components/bh1750/sensor.py
homeassistant/components/bitcoin/sensor.py
homeassistant/components/bizkaibus/sensor.py
homeassistant/components/blink/*
homeassistant/components/blink/__init__.py
homeassistant/components/blink/alarm_control_panel.py
homeassistant/components/blink/binary_sensor.py
homeassistant/components/blink/camera.py
homeassistant/components/blink/const.py
homeassistant/components/blink/sensor.py
homeassistant/components/blinksticklight/light.py
homeassistant/components/blinkt/light.py
homeassistant/components/blockchain/sensor.py
@@ -154,9 +158,14 @@ omit =
homeassistant/components/deluge/switch.py
homeassistant/components/denon/media_player.py
homeassistant/components/denonavr/media_player.py
homeassistant/components/denonavr/receiver.py
homeassistant/components/deutsche_bahn/sensor.py
homeassistant/components/devolo_home_control/__init__.py
homeassistant/components/devolo_home_control/binary_sensor.py
homeassistant/components/devolo_home_control/const.py
homeassistant/components/devolo_home_control/devolo_device.py
homeassistant/components/devolo_home_control/sensor.py
homeassistant/components/devolo_home_control/subscriber.py
homeassistant/components/devolo_home_control/switch.py
homeassistant/components/dht/sensor.py
homeassistant/components/digital_ocean/*
@@ -255,7 +264,6 @@ omit =
homeassistant/components/folder_watcher/*
homeassistant/components/foobot/sensor.py
homeassistant/components/fortios/device_tracker.py
homeassistant/components/fortigate/*
homeassistant/components/foscam/camera.py
homeassistant/components/foscam/const.py
homeassistant/components/foursquare/*
@@ -284,6 +292,7 @@ omit =
homeassistant/components/gitlab_ci/sensor.py
homeassistant/components/gitter/sensor.py
homeassistant/components/glances/__init__.py
homeassistant/components/glances/const.py
homeassistant/components/glances/sensor.py
homeassistant/components/gntp/notify.py
homeassistant/components/goalfeed/*
@@ -339,6 +348,8 @@ omit =
homeassistant/components/hunterdouglas_powerview/sensor.py
homeassistant/components/hunterdouglas_powerview/cover.py
homeassistant/components/hunterdouglas_powerview/entity.py
homeassistant/components/hvv_departures/sensor.py
homeassistant/components/hvv_departures/__init__.py
homeassistant/components/hydrawise/*
homeassistant/components/hyperion/light.py
homeassistant/components/ialarm/alarm_control_panel.py
@@ -431,7 +442,6 @@ omit =
homeassistant/components/linux_battery/sensor.py
homeassistant/components/lirc/*
homeassistant/components/llamalab_automate/notify.py
homeassistant/components/lockitron/lock.py
homeassistant/components/logi_circle/__init__.py
homeassistant/components/logi_circle/camera.py
homeassistant/components/logi_circle/const.py
@@ -538,6 +548,7 @@ omit =
homeassistant/components/notion/sensor.py
homeassistant/components/noaa_tides/sensor.py
homeassistant/components/norway_air/air_quality.py
homeassistant/components/notify_events/notify.py
homeassistant/components/nsw_fuel_station/sensor.py
homeassistant/components/nuimo_controller/*
homeassistant/components/nuki/lock.py
@@ -714,7 +725,11 @@ omit =
homeassistant/components/sinch/*
homeassistant/components/slide/*
homeassistant/components/sma/sensor.py
homeassistant/components/smappee/*
homeassistant/components/smappee/__init__.py
homeassistant/components/smappee/api.py
homeassistant/components/smappee/binary_sensor.py
homeassistant/components/smappee/sensor.py
homeassistant/components/smappee/switch.py
homeassistant/components/smarty/*
homeassistant/components/smarthab/*
homeassistant/components/sms/*
@@ -740,7 +755,8 @@ omit =
homeassistant/components/spotcrime/sensor.py
homeassistant/components/spotify/__init__.py
homeassistant/components/spotify/media_player.py
homeassistant/components/squeezebox/*
homeassistant/components/squeezebox/__init__.py
homeassistant/components/squeezebox/media_player.py
homeassistant/components/starline/*
homeassistant/components/starlingbank/sensor.py
homeassistant/components/steam_online/sensor.py
@@ -797,6 +813,7 @@ omit =
homeassistant/components/thomson/device_tracker.py
homeassistant/components/tibber/*
homeassistant/components/tikteck/light.py
homeassistant/components/tile/__init__.py
homeassistant/components/tile/device_tracker.py
homeassistant/components/time_date/sensor.py
homeassistant/components/tmb/sensor.py
@@ -804,7 +821,16 @@ omit =
homeassistant/components/todoist/const.py
homeassistant/components/tof/sensor.py
homeassistant/components/tomato/device_tracker.py
homeassistant/components/toon/*
homeassistant/components/toon/__init__.py
homeassistant/components/toon/binary_sensor.py
homeassistant/components/toon/climate.py
homeassistant/components/toon/const.py
homeassistant/components/toon/coordinator.py
homeassistant/components/toon/helpers.py
homeassistant/components/toon/models.py
homeassistant/components/toon/oauth2.py
homeassistant/components/toon/sensor.py
homeassistant/components/toon/switch.py
homeassistant/components/torque/sensor.py
homeassistant/components/totalconnect/*
homeassistant/components/touchline/climate.py
@@ -891,7 +917,14 @@ omit =
homeassistant/components/xeoma/camera.py
homeassistant/components/xfinity/device_tracker.py
homeassistant/components/xiaomi/camera.py
homeassistant/components/xiaomi_aqara/*
homeassistant/components/xiaomi_aqara/__init__.py
homeassistant/components/xiaomi_aqara/binary_sensor.py
homeassistant/components/xiaomi_aqara/const.py
homeassistant/components/xiaomi_aqara/cover.py
homeassistant/components/xiaomi_aqara/light.py
homeassistant/components/xiaomi_aqara/lock.py
homeassistant/components/xiaomi_aqara/sensor.py
homeassistant/components/xiaomi_aqara/switch.py
homeassistant/components/xiaomi_miio/__init__.py
homeassistant/components/xiaomi_miio/air_quality.py
homeassistant/components/xiaomi_miio/alarm_control_panel.py

2
.gitattributes vendored
View File

@@ -8,3 +8,5 @@
*.png binary
*.zip binary
*.mp3 binary
Dockerfile.dev linguist-language=Dockerfile

View File

@@ -21,7 +21,7 @@
- Home Assistant Core release with the issue:
- Last working Home Assistant Core release (if known):
- Operating environment (Home Assistant/Supervised/Docker/venv):
- Operating environment (OS/Container/Supervised/Core):
- Integration causing this issue:
- Link to integration documentation on our website:

View File

@@ -25,7 +25,7 @@ about: Report an issue with Home Assistant Core
- Home Assistant Core release with the issue:
- Last working Home Assistant Core release (if known):
- Operating environment (Home Assistant/Supervised/Docker/venv):
- Operating environment (OS/Container/Supervised/Core):
- Integration causing this issue:
- Link to integration documentation on our website:

View File

@@ -46,7 +46,7 @@ homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automation/* @home-assistant/core
homeassistant/components/avea/* @pattyland
homeassistant/components/avri/* @timvancann
homeassistant/components/awair/* @danielsjf
homeassistant/components/awair/* @ahayworth @danielsjf
homeassistant/components/aws/* @awarecan @robbiet480
homeassistant/components/axis/* @Kane610
homeassistant/components/azure_event_hub/* @eavanvalkenburg
@@ -57,7 +57,7 @@ homeassistant/components/bizkaibus/* @UgaitzEtxebarria
homeassistant/components/blebox/* @gadgetmobile
homeassistant/components/blink/* @fronzbot
homeassistant/components/bmp280/* @belidzs
homeassistant/components/bmw_connected_drive/* @gerard33
homeassistant/components/bmw_connected_drive/* @gerard33 @rikroe
homeassistant/components/bom/* @maddenp
homeassistant/components/braviatv/* @robbiet480 @bieniu
homeassistant/components/broadlink/* @danielhiversen @felipediel
@@ -86,6 +86,7 @@ homeassistant/components/cpuspeed/* @fabaff
homeassistant/components/cups/* @fabaff
homeassistant/components/daikin/* @fredrike
homeassistant/components/darksky/* @fabaff
homeassistant/components/debugpy/* @frenck
homeassistant/components/deconz/* @Kane610
homeassistant/components/delijn/* @bollewolle @Emilv2
homeassistant/components/demo/* @home-assistant/core
@@ -133,7 +134,6 @@ homeassistant/components/flock/* @fabaff
homeassistant/components/flume/* @ChrisMandich @bdraco
homeassistant/components/flunearyou/* @bachya
homeassistant/components/forked_daapd/* @uvjustin
homeassistant/components/fortigate/* @kifeo
homeassistant/components/fortios/* @kimfrellsen
homeassistant/components/foscam/* @skgsergio
homeassistant/components/foursquare/* @robbiet480
@@ -184,7 +184,10 @@ homeassistant/components/http/* @home-assistant/core
homeassistant/components/huawei_lte/* @scop @fphammerle
homeassistant/components/huawei_router/* @abmantis
homeassistant/components/hue/* @balloob
homeassistant/components/humidifier/* @home-assistant/core @Shulyaka
homeassistant/components/hunterdouglas_powerview/* @bdraco
homeassistant/components/hvv_departures/* @vigonotion
homeassistant/components/hydrawise/* @ptcryan
homeassistant/components/iammeter/* @lewei50
homeassistant/components/iaqualink/* @flz
homeassistant/components/icloud/* @Quentame
@@ -243,6 +246,7 @@ homeassistant/components/melissa/* @kennedyshead
homeassistant/components/met/* @danielhiversen
homeassistant/components/meteo_france/* @victorcerutti @oncleben31 @Quentame
homeassistant/components/meteoalarm/* @rolfberkenbosch
homeassistant/components/metoffice/* @MrHarcombe
homeassistant/components/miflora/* @danielhiversen @ChristianKuehnel
homeassistant/components/mikrotik/* @engrbm87
homeassistant/components/mill/* @danielhiversen
@@ -274,6 +278,7 @@ homeassistant/components/nissan_leaf/* @filcole
homeassistant/components/nmbs/* @thibmaek
homeassistant/components/no_ip/* @fabaff
homeassistant/components/notify/* @home-assistant/core
homeassistant/components/notify_events/* @matrozov @papajojo
homeassistant/components/notion/* @bachya
homeassistant/components/nsw_fuel_station/* @nickw444
homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
@@ -311,9 +316,10 @@ homeassistant/components/plaato/* @JohNan
homeassistant/components/plant/* @ChristianKuehnel
homeassistant/components/plex/* @jjlawren
homeassistant/components/plugwise/* @CoMPaTech @bouwew
homeassistant/components/plum_lightpad/* @ColinHarrington
homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa
homeassistant/components/point/* @fredrike
homeassistant/components/powerwall/* @bdraco @jrester
homeassistant/components/prometheus/* @knyar
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe
homeassistant/components/ps4/* @ktnrg45
homeassistant/components/ptvsd/* @swamp-ig
@@ -362,6 +368,7 @@ homeassistant/components/sinch/* @bendikrb
homeassistant/components/sisyphus/* @jkeljo
homeassistant/components/slide/* @ualex73
homeassistant/components/sma/* @kellerza
homeassistant/components/smappee/* @bsmappee
homeassistant/components/smarthab/* @outadoc
homeassistant/components/smartthings/* @andrewsayre
homeassistant/components/smarty/* @z0mbieprocess
@@ -375,7 +382,7 @@ homeassistant/components/somfy/* @tetienne
homeassistant/components/sonarr/* @ctalkington
homeassistant/components/songpal/* @rytilahti @shenxn
homeassistant/components/spaceapi/* @fabaff
homeassistant/components/speedtestdotnet/* @rohankapoorcom
homeassistant/components/speedtestdotnet/* @rohankapoorcom @engrbm87
homeassistant/components/spider/* @peternijssen
homeassistant/components/spotify/* @frenck
homeassistant/components/sql/* @dgomes
@@ -453,7 +460,6 @@ homeassistant/components/watson_tts/* @rutkai
homeassistant/components/weather/* @fabaff
homeassistant/components/webostv/* @bendavid
homeassistant/components/websocket_api/* @home-assistant/core
homeassistant/components/wemo/* @sqldiablo
homeassistant/components/wiffi/* @mampfes
homeassistant/components/withings/* @vangorra
homeassistant/components/wled/* @frenck

View File

@@ -117,7 +117,8 @@ class TotpAuthModule(MultiFactorAuthModule):
Mfa module should extend SetupFlow
"""
user = await self.hass.auth.async_get_user(user_id) # type: ignore
user = await self.hass.auth.async_get_user(user_id)
assert user is not None
return TotpSetupFlow(self, self.input_schema, user)
async def async_setup_user(self, user_id: str, setup_data: Any) -> str:

View File

@@ -175,7 +175,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
"""Initialize the login flow."""
self._auth_provider = auth_provider
self._auth_module_id: Optional[str] = None
self._auth_manager = auth_provider.hass.auth # type: ignore
self._auth_manager = auth_provider.hass.auth
self.available_mfa_modules: Dict[str, str] = {}
self.created_at = dt_util.utcnow()
self.invalid_mfa_times = 0
@@ -224,6 +224,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
errors = {}
assert self._auth_module_id is not None
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
@@ -234,7 +235,9 @@ class LoginFlow(data_entry_flow.FlowHandler):
auth_module, "async_initialize_login_mfa_step"
):
try:
await auth_module.async_initialize_login_mfa_step(self.user.id)
await auth_module.async_initialize_login_mfa_step( # type: ignore
self.user.id
)
except HomeAssistantError:
_LOGGER.exception("Error initializing MFA step")
return self.async_abort(reason="unknown_error")

View File

@@ -1,6 +1,7 @@
"""Provide methods to bootstrap a Home Assistant instance."""
import asyncio
import contextlib
from datetime import datetime
import logging
import logging.handlers
import os
@@ -20,7 +21,12 @@ from homeassistant.const import (
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import DATA_SETUP, DATA_SETUP_STARTED, async_setup_component
from homeassistant.setup import (
DATA_SETUP,
DATA_SETUP_STARTED,
async_set_domains_to_be_loaded,
async_setup_component,
)
from homeassistant.util.logging import async_activate_log_queue_handler
from homeassistant.util.package import async_get_user_site, is_virtual_env
from homeassistant.util.yaml import clear_secret_cache
@@ -34,12 +40,18 @@ DATA_LOGGING = "logging"
LOG_SLOW_STARTUP_INTERVAL = 60
DEBUGGER_INTEGRATIONS = {"ptvsd"}
DEBUGGER_INTEGRATIONS = {"debugpy", "ptvsd"}
CORE_INTEGRATIONS = ("homeassistant", "persistent_notification")
LOGGING_INTEGRATIONS = {"logger", "system_log", "sentry"}
STAGE_1_INTEGRATIONS = {
LOGGING_INTEGRATIONS = {
# Set log levels
"logger",
# Error logging
"system_log",
"sentry",
# To record data
"recorder",
}
STAGE_1_INTEGRATIONS = {
# To make sure we forward data to other instances
"mqtt_eventstream",
# To provide account link implementations
@@ -50,7 +62,6 @@ STAGE_1_INTEGRATIONS = {
# as possible so problem integrations can
# be removed
"frontend",
"config",
}
@@ -125,8 +136,12 @@ async def async_setup_hass(
await hass.async_block_till_done()
safe_mode = True
old_config = hass.config
hass = core.HomeAssistant()
hass.config.config_dir = config_dir
hass.config.skip_pip = old_config.skip_pip
hass.config.internal_url = old_config.internal_url
hass.config.external_url = old_config.external_url
hass.config.config_dir = old_config.config_dir
if safe_mode:
_LOGGER.info("Starting in safe mode")
@@ -327,76 +342,130 @@ def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
return domains
async def _async_log_pending_setups(
domains: Set[str], setup_started: Dict[str, datetime]
) -> None:
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
while True:
await asyncio.sleep(LOG_SLOW_STARTUP_INTERVAL)
remaining = [domain for domain in domains if domain in setup_started]
if remaining:
_LOGGER.info(
"Waiting on integrations to complete setup: %s", ", ".join(remaining),
)
async def async_setup_multi_components(
hass: core.HomeAssistant,
domains: Set[str],
config: Dict[str, Any],
setup_started: Dict[str, datetime],
) -> None:
"""Set up multiple domains. Log on failure."""
futures = {
domain: hass.async_create_task(async_setup_component(hass, domain, config))
for domain in domains
}
log_task = asyncio.create_task(_async_log_pending_setups(domains, setup_started))
await asyncio.wait(futures.values())
log_task.cancel()
errors = [domain for domain in domains if futures[domain].exception()]
for domain in errors:
exception = futures[domain].exception()
assert exception is not None
_LOGGER.error(
"Error setting up integration %s - received exception",
domain,
exc_info=(type(exception), exception, exception.__traceback__),
)
async def _async_set_up_integrations(
hass: core.HomeAssistant, config: Dict[str, Any]
) -> None:
"""Set up all the integrations."""
setup_started = hass.data[DATA_SETUP_STARTED] = {}
domains_to_setup = _get_domains(hass, config)
async def async_setup_multi_components(domains: Set[str]) -> None:
"""Set up multiple domains. Log on failure."""
# Resolve all dependencies so we know all integrations
# that will have to be loaded and start rightaway
integration_cache: Dict[str, loader.Integration] = {}
to_resolve = domains_to_setup
while to_resolve:
old_to_resolve = to_resolve
to_resolve = set()
async def _async_log_pending_setups() -> None:
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
while True:
await asyncio.sleep(LOG_SLOW_STARTUP_INTERVAL)
remaining = [domain for domain in domains if domain in setup_started]
if remaining:
_LOGGER.info(
"Waiting on integrations to complete setup: %s",
", ".join(remaining),
)
futures = {
domain: hass.async_create_task(async_setup_component(hass, domain, config))
for domain in domains
}
log_task = asyncio.create_task(_async_log_pending_setups())
await asyncio.wait(futures.values())
log_task.cancel()
errors = [domain for domain in domains if futures[domain].exception()]
for domain in errors:
exception = futures[domain].exception()
_LOGGER.error(
"Error setting up integration %s - received exception",
domain,
exc_info=(type(exception), exception, exception.__traceback__),
integrations_to_process = [
int_or_exc
for int_or_exc in await asyncio.gather(
*(
loader.async_get_integration(hass, domain)
for domain in old_to_resolve
),
return_exceptions=True,
)
if isinstance(int_or_exc, loader.Integration)
]
resolve_dependencies_tasks = [
itg.resolve_dependencies()
for itg in integrations_to_process
if not itg.all_dependencies_resolved
]
domains = _get_domains(hass, config)
if resolve_dependencies_tasks:
await asyncio.gather(*resolve_dependencies_tasks)
for itg in integrations_to_process:
integration_cache[itg.domain] = itg
for dep in itg.all_dependencies:
if dep in domains_to_setup:
continue
domains_to_setup.add(dep)
to_resolve.add(dep)
_LOGGER.info("Domains to be set up: %s", domains_to_setup)
logging_domains = domains_to_setup & LOGGING_INTEGRATIONS
# Load logging as soon as possible
if logging_domains:
_LOGGER.info("Setting up logging: %s", logging_domains)
await async_setup_multi_components(hass, logging_domains, config, setup_started)
# Start up debuggers. Start these first in case they want to wait.
debuggers = domains & DEBUGGER_INTEGRATIONS
debuggers = domains_to_setup & DEBUGGER_INTEGRATIONS
if debuggers:
_LOGGER.debug("Starting up debuggers %s", debuggers)
await async_setup_multi_components(debuggers)
domains -= DEBUGGER_INTEGRATIONS
_LOGGER.debug("Setting up debuggers: %s", debuggers)
await async_setup_multi_components(hass, debuggers, config, setup_started)
# Resolve all dependencies of all components so we can find the logging
# and integrations that need faster initialization.
resolved_domains_task = asyncio.gather(
*(loader.async_component_dependencies(hass, domain) for domain in domains),
return_exceptions=True,
)
# calculate what components to setup in what stage
stage_1_domains = set()
# Finish resolving domains
for dep_domains in await resolved_domains_task:
# Result is either a set or an exception. We ignore exceptions
# It will be properly handled during setup of the domain.
if isinstance(dep_domains, set):
domains.update(dep_domains)
# Find all dependencies of any dependency of any stage 1 integration that
# we plan on loading and promote them to stage 1
deps_promotion = STAGE_1_INTEGRATIONS
while deps_promotion:
old_deps_promotion = deps_promotion
deps_promotion = set()
# setup components
logging_domains = domains & LOGGING_INTEGRATIONS
stage_1_domains = domains & STAGE_1_INTEGRATIONS
stage_2_domains = domains - logging_domains - stage_1_domains
for domain in old_deps_promotion:
if domain not in domains_to_setup or domain in stage_1_domains:
continue
if logging_domains:
_LOGGER.info("Setting up %s", logging_domains)
stage_1_domains.add(domain)
await async_setup_multi_components(logging_domains)
dep_itg = integration_cache.get(domain)
if dep_itg is None:
continue
deps_promotion.update(dep_itg.all_dependencies)
stage_2_domains = domains_to_setup - logging_domains - debuggers - stage_1_domains
# Kick off loading the registries. They don't need to be awaited.
asyncio.gather(
@@ -405,49 +474,17 @@ async def _async_set_up_integrations(
hass.helpers.area_registry.async_get_registry(),
)
# Start setup
if stage_1_domains:
_LOGGER.info("Setting up %s", stage_1_domains)
_LOGGER.info("Setting up stage 1: %s", stage_1_domains)
await async_setup_multi_components(hass, stage_1_domains, config, setup_started)
await async_setup_multi_components(stage_1_domains)
# Enables after dependencies
async_set_domains_to_be_loaded(hass, stage_1_domains | stage_2_domains)
# Load all integrations
after_dependencies: Dict[str, Set[str]] = {}
for int_or_exc in await asyncio.gather(
*(loader.async_get_integration(hass, domain) for domain in stage_2_domains),
return_exceptions=True,
):
# Exceptions are handled in async_setup_component.
if isinstance(int_or_exc, loader.Integration) and int_or_exc.after_dependencies:
after_dependencies[int_or_exc.domain] = set(int_or_exc.after_dependencies)
last_load = None
while stage_2_domains:
domains_to_load = set()
for domain in stage_2_domains:
after_deps = after_dependencies.get(domain)
# Load if integration has no after_dependencies or they are
# all loaded
if not after_deps or not after_deps - hass.config.components:
domains_to_load.add(domain)
if not domains_to_load or domains_to_load == last_load:
break
_LOGGER.debug("Setting up %s", domains_to_load)
await async_setup_multi_components(domains_to_load)
last_load = domains_to_load
stage_2_domains -= domains_to_load
# These are stage 2 domains that never have their after_dependencies
# satisfied.
if stage_2_domains:
_LOGGER.debug("Final set up: %s", stage_2_domains)
await async_setup_multi_components(stage_2_domains)
_LOGGER.info("Setting up stage 2: %s", stage_2_domains)
await async_setup_multi_components(hass, stage_2_domains, config, setup_started)
# Wrap up startup
_LOGGER.debug("Waiting for startup to wrap up")

View File

@@ -4,5 +4,8 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/abode",
"requirements": ["abodepy==0.19.0"],
"codeowners": ["@shred86"]
"codeowners": ["@shred86"],
"homekit": {
"models": ["Abode", "Iota"]
}
}

View File

@@ -1,3 +0,0 @@
{
"title": "Abode"
}

View File

@@ -71,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
except AdGuardHomeConnectionError as exception:
raise ConfigEntryNotReady from exception
if LooseVersion(MIN_ADGUARD_HOME_VERSION) > LooseVersion(version):
if version and LooseVersion(MIN_ADGUARD_HOME_VERSION) > LooseVersion(version):
_LOGGER.error(
"This integration requires AdGuard Home v0.99.0 or higher to work correctly"
)

View File

@@ -84,7 +84,7 @@ class AdGuardHomeFlowHandler(ConfigFlow):
errors["base"] = "connection_error"
return await self._show_setup_form(errors)
if LooseVersion(MIN_ADGUARD_HOME_VERSION) > LooseVersion(version):
if version and LooseVersion(MIN_ADGUARD_HOME_VERSION) > LooseVersion(version):
return self.async_abort(
reason="adguard_home_outdated",
description_placeholders={
@@ -105,7 +105,7 @@ class AdGuardHomeFlowHandler(ConfigFlow):
},
)
async def async_step_hassio(self, user_input=None):
async def async_step_hassio(self, discovery_info):
"""Prepare configuration for a Hass.io AdGuard Home add-on.
This flow is triggered by the discovery component.
@@ -113,14 +113,14 @@ class AdGuardHomeFlowHandler(ConfigFlow):
entries = self._async_current_entries()
if not entries:
self._hassio_discovery = user_input
self._hassio_discovery = discovery_info
return await self.async_step_hassio_confirm()
cur_entry = entries[0]
if (
cur_entry.data[CONF_HOST] == user_input[CONF_HOST]
and cur_entry.data[CONF_PORT] == user_input[CONF_PORT]
cur_entry.data[CONF_HOST] == discovery_info[CONF_HOST]
and cur_entry.data[CONF_PORT] == discovery_info[CONF_PORT]
):
return self.async_abort(reason="single_instance_allowed")
@@ -133,8 +133,8 @@ class AdGuardHomeFlowHandler(ConfigFlow):
cur_entry,
data={
**cur_entry.data,
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
},
)

View File

@@ -23,13 +23,13 @@ class AgentFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Initialize the Agent config flow."""
self.device_config = {}
async def async_step_user(self, info=None):
async def async_step_user(self, user_input=None):
"""Handle an Agent config flow."""
errors = {}
if info is not None:
host = info[CONF_HOST]
port = info[CONF_PORT]
if user_input is not None:
host = user_input[CONF_HOST]
port = user_input[CONF_PORT]
server_origin = generate_url(host, port)
agent_client = Agent(server_origin, async_get_clientsession(self.hass))
@@ -48,8 +48,8 @@ class AgentFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured(
updates={
CONF_HOST: info[CONF_HOST],
CONF_PORT: info[CONF_PORT],
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
SERVER_URL: server_origin,
}
)

View File

@@ -4,7 +4,7 @@
"already_configured": "El dispositivo ya est\u00e1 configurado"
},
"error": {
"already_in_progress": "La configuraci\u00f3n del flujo para el dispositivo ya est\u00e1 en marcha.",
"already_in_progress": "El flujo de configuraci\u00f3n para el dispositivo ya est\u00e1 en marcha.",
"device_unavailable": "El dispositivo no est\u00e1 disponible"
},
"step": {

View File

@@ -10,7 +10,7 @@
"step": {
"user": {
"data": {
"api_key": "Airly API-n\u00f8kkel",
"api_key": "API-n\u00f8kkel",
"latitude": "Breddegrad",
"longitude": "Lengdegrad",
"name": "Navn p\u00e5 integrasjonen"

View File

@@ -21,7 +21,7 @@
"node_pro": {
"data": {
"ip_address": "Enhetens IP-adresse / vertsnavn",
"password": "Passord for enhet"
"password": "Passord"
},
"description": "Overv\u00e5ke en personlig AirVisual-enhet. Passordet kan hentes fra enhetens brukergrensesnitt.",
"title": "Konfigurer en AirVisual Node / Pro"

View File

@@ -1,12 +0,0 @@
{
"config": {
"step": {
"user": {
"data": {
"latitude": "Zemepisn\u00e1 \u0161\u00edrka",
"longitude": "Zemepisn\u00e1 d\u013a\u017eka"
}
}
}
}
}

View File

@@ -2,7 +2,7 @@
from datetime import timedelta
import logging
from alarmdecoder import AlarmDecoder
from adext import AdExt
from alarmdecoder.devices import SerialDevice, SocketDevice, USBDevice
from alarmdecoder.util import NoDeviceError
import voluptuous as vol
@@ -189,13 +189,13 @@ def setup(hass, config):
if device_type == "socket":
host = device[CONF_HOST]
port = device[CONF_DEVICE_PORT]
controller = AlarmDecoder(SocketDevice(interface=(host, port)))
controller = AdExt(SocketDevice(interface=(host, port)))
elif device_type == "serial":
path = device[CONF_DEVICE_PATH]
baud = device[CONF_DEVICE_BAUD]
controller = AlarmDecoder(SerialDevice(interface=path))
controller = AdExt(SerialDevice(interface=path))
elif device_type == "usb":
AlarmDecoder(USBDevice.find())
AdExt(USBDevice.find())
return False
controller.on_message += handle_message

View File

@@ -16,6 +16,7 @@ from homeassistant.const import (
ATTR_CODE,
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED,
)
@@ -108,6 +109,8 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
self._state = STATE_ALARM_TRIGGERED
elif message.armed_away:
self._state = STATE_ALARM_ARMED_AWAY
elif message.armed_home and (message.entry_delay_off or message.perimeter_only):
self._state = STATE_ALARM_ARMED_NIGHT
elif message.armed_home:
self._state = STATE_ALARM_ARMED_HOME
else:
@@ -178,28 +181,27 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
def alarm_arm_away(self, code=None):
"""Send arm away command."""
if code:
if self._auto_bypass:
self.hass.data[DATA_AD].send(f"{code!s}6#")
self.hass.data[DATA_AD].send(f"{code!s}2")
elif not self._code_arm_required:
self.hass.data[DATA_AD].send("#2")
self.hass.data[DATA_AD].arm_away(
code=code,
code_arm_required=self._code_arm_required,
auto_bypass=self._auto_bypass,
)
def alarm_arm_home(self, code=None):
"""Send arm home command."""
if code:
if self._auto_bypass:
self.hass.data[DATA_AD].send(f"{code!s}6#")
self.hass.data[DATA_AD].send(f"{code!s}3")
elif not self._code_arm_required:
self.hass.data[DATA_AD].send("#3")
self.hass.data[DATA_AD].arm_home(
code=code,
code_arm_required=self._code_arm_required,
auto_bypass=self._auto_bypass,
)
def alarm_arm_night(self, code=None):
"""Send arm night command."""
if code:
self.hass.data[DATA_AD].send(f"{code!s}7")
elif not self._code_arm_required:
self.hass.data[DATA_AD].send("#7")
self.hass.data[DATA_AD].arm_night(
code=code,
code_arm_required=self._code_arm_required,
auto_bypass=self._auto_bypass,
)
def alarm_toggle_chime(self, code=None):
"""Send toggle chime command."""

View File

@@ -2,6 +2,6 @@
"domain": "alarmdecoder",
"name": "AlarmDecoder",
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
"requirements": ["alarmdecoder==1.13.2"],
"requirements": ["adext==0.3"],
"codeowners": ["@ajschmidt8"]
}

View File

@@ -222,11 +222,6 @@ class Alert(ToggleEntity):
return STATE_ON
return STATE_IDLE
@property
def hidden(self):
"""Hide the alert when it is not firing."""
return not self._can_ack or not self._firing
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)

View File

@@ -17,6 +17,7 @@ from .const import (
CONF_ENTITY_CONFIG,
CONF_FILTER,
CONF_LOCALE,
CONF_PASSWORD,
CONF_SUPPORTED_LOCALES,
CONF_TEXT,
CONF_TITLE,
@@ -56,6 +57,7 @@ CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: {
CONF_FLASH_BRIEFINGS: {
vol.Required(CONF_PASSWORD): cv.string,
cv.string: vol.All(
cv.ensure_list,
[
@@ -67,7 +69,7 @@ CONFIG_SCHEMA = vol.Schema(
vol.Optional(CONF_DISPLAY_URL): cv.template,
}
],
)
),
},
# vol.Optional here would mean we couldn't distinguish between an empty
# smart_home: and none at all.

View File

@@ -19,6 +19,7 @@ CONF_FILTER = "filter"
CONF_ENTITY_CONFIG = "entity_config"
CONF_ENDPOINT = "endpoint"
CONF_LOCALE = "locale"
CONF_PASSWORD = "password"
ATTR_UID = "uid"
ATTR_UPDATE_DATE = "updateDate"
@@ -39,6 +40,7 @@ API_HEADER = "header"
API_PAYLOAD = "payload"
API_SCOPE = "scope"
API_CHANGE = "change"
API_PASSWORD = "password"
CONF_DESCRIPTION = "description"
CONF_DISPLAY_CATEGORIES = "display_categories"

View File

@@ -1,15 +1,17 @@
"""Support for Alexa skill service end point."""
import copy
import hmac
import logging
import uuid
from homeassistant.components import http
from homeassistant.const import HTTP_NOT_FOUND
from homeassistant.const import HTTP_NOT_FOUND, HTTP_UNAUTHORIZED
from homeassistant.core import callback
from homeassistant.helpers import template
import homeassistant.util.dt as dt_util
from .const import (
API_PASSWORD,
ATTR_MAIN_TEXT,
ATTR_REDIRECTION_URL,
ATTR_STREAM_URL,
@@ -18,6 +20,7 @@ from .const import (
ATTR_UPDATE_DATE,
CONF_AUDIO,
CONF_DISPLAY_URL,
CONF_PASSWORD,
CONF_TEXT,
CONF_TITLE,
CONF_UID,
@@ -39,6 +42,7 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
"""Handle Alexa Flash Briefing skill requests."""
url = FLASH_BRIEFINGS_API_ENDPOINT
requires_auth = False
name = "api:alexa:flash_briefings"
def __init__(self, hass, flash_briefings):
@@ -52,7 +56,20 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
"""Handle Alexa Flash Briefing request."""
_LOGGER.debug("Received Alexa flash briefing request for: %s", briefing_id)
if self.flash_briefings.get(briefing_id) is None:
if request.query.get(API_PASSWORD) is None:
err = "No password provided for Alexa flash briefing: %s"
_LOGGER.error(err, briefing_id)
return b"", HTTP_UNAUTHORIZED
if not hmac.compare_digest(
request.query[API_PASSWORD].encode("utf-8"),
self.flash_briefings[CONF_PASSWORD].encode("utf-8"),
):
err = "Wrong password for Alexa flash briefing: %s"
_LOGGER.error(err, briefing_id)
return b"", HTTP_UNAUTHORIZED
if not isinstance(self.flash_briefings.get(briefing_id), list):
err = "No configured Alexa flash briefing was found for: %s"
_LOGGER.error(err, briefing_id)
return b"", HTTP_NOT_FOUND

View File

@@ -94,12 +94,12 @@ class AlmondFlowHandler(config_entry_oauth2_flow.AbstractOAuth2FlowHandler):
data={"type": TYPE_LOCAL, "host": user_input["host"]},
)
async def async_step_hassio(self, user_input=None):
async def async_step_hassio(self, discovery_info):
"""Receive a Hass.io discovery."""
if self._async_current_entries():
return self.async_abort(reason="already_setup")
self.hassio_discovery = user_input
self.hassio_discovery = discovery_info
return await self.async_step_hassio_confirm()

View File

@@ -1,3 +0,0 @@
{
"title": "Almond"
}

View File

@@ -1,3 +0,0 @@
{
"title": "Ambiclimate"
}

View File

@@ -10,6 +10,7 @@ from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import (
ATTR_LOCATION,
ATTR_NAME,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
CONF_API_KEY,
DEGREE,
@@ -126,6 +127,8 @@ TYPE_TEMPF = "tempf"
TYPE_TEMPINF = "tempinf"
TYPE_TOTALRAININ = "totalrainin"
TYPE_UV = "uv"
TYPE_PM25 = "pm25"
TYPE_PM25_24H = "pm25_24h"
TYPE_WEEKLYRAININ = "weeklyrainin"
TYPE_WINDDIR = "winddir"
TYPE_WINDDIR_AVG10M = "winddir_avg10m"
@@ -218,6 +221,13 @@ SENSOR_TYPES = {
TYPE_TEMPINF: ("Inside Temp", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
TYPE_TOTALRAININ: ("Lifetime Rain", "in", TYPE_SENSOR, None),
TYPE_UV: ("uv", "Index", TYPE_SENSOR, None),
TYPE_PM25: ("PM25", CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, TYPE_SENSOR, None),
TYPE_PM25_24H: (
"PM25 24h Avg",
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
TYPE_SENSOR,
None,
),
TYPE_WEEKLYRAININ: ("Weekly Rain", "in", TYPE_SENSOR, None),
TYPE_WINDDIR: ("Wind Dir", DEGREE, TYPE_SENSOR, None),
TYPE_WINDDIR_AVG10M: ("Wind Dir Avg 10m", DEGREE, TYPE_SENSOR, None),

View File

@@ -1,3 +0,0 @@
{
"title": "Ambient PWS"
}

View File

@@ -130,6 +130,10 @@ class CannotSnapshot(Exception):
"""Conditions are not valid for taking a snapshot."""
class AmcrestCommandFailed(Exception):
"""Amcrest camera command did not work."""
class AmcrestCam(Camera):
"""An implementation of an Amcrest IP camera."""
@@ -367,12 +371,12 @@ class AmcrestCam(Camera):
self._model = resp.split("=")[-1]
else:
self._model = "unknown"
self.is_streaming = self._api.video_enabled
self._is_recording = self._api.record_mode == "Manual"
self._motion_detection_enabled = self._api.is_motion_detector_on()
self._audio_enabled = self._api.audio_enabled
self._motion_recording_enabled = self._api.is_record_on_motion_detection()
self._color_bw = _CBW[self._api.day_night_color]
self.is_streaming = self._get_video()
self._is_recording = self._get_recording()
self._motion_detection_enabled = self._get_motion_detection()
self._audio_enabled = self._get_audio()
self._motion_recording_enabled = self._get_motion_recording()
self._color_bw = self._get_color_mode()
self._rtsp_url = self._api.rtsp_url(typeno=self._resolution)
except AmcrestError as error:
log_update_error(_LOGGER, "get", self.name, "camera attributes", error)
@@ -384,11 +388,11 @@ class AmcrestCam(Camera):
def turn_off(self):
"""Turn off camera."""
self._enable_video_stream(False)
self._enable_video(False)
def turn_on(self):
"""Turn on camera."""
self._enable_video_stream(True)
self._enable_video(True)
def enable_motion_detection(self):
"""Enable motion detection in the camera."""
@@ -465,28 +469,53 @@ class AmcrestCam(Camera):
# Methods to send commands to Amcrest camera and handle errors
def _enable_video_stream(self, enable):
def _change_setting(self, value, attr, description, action="set"):
func = description.replace(" ", "_")
description = f"camera {description} to {value}"
tries = 3
while True:
try:
getattr(self, f"_set_{func}")(value)
new_value = getattr(self, f"_get_{func}")()
if new_value != value:
raise AmcrestCommandFailed
except (AmcrestError, AmcrestCommandFailed) as error:
if tries == 1:
log_update_error(_LOGGER, action, self.name, description, error)
return
log_update_error(
_LOGGER, action, self.name, description, error, logging.DEBUG
)
else:
if attr:
setattr(self, attr, new_value)
self.schedule_update_ha_state()
return
tries -= 1
def _get_video(self):
return self._api.video_enabled
def _set_video(self, enable):
self._api.video_enabled = enable
def _enable_video(self, enable):
"""Enable or disable camera video stream."""
# Given the way the camera's state is determined by
# is_streaming and is_recording, we can't leave
# recording on if video stream is being turned off.
if self.is_recording and not enable:
self._enable_recording(False)
try:
self._api.video_enabled = enable
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"camera video stream",
error,
)
else:
self.is_streaming = enable
self.schedule_update_ha_state()
self._change_setting(enable, "is_streaming", "video")
if self._control_light:
self._enable_light(self._audio_enabled or self.is_streaming)
self._change_light()
def _get_recording(self):
return self._api.record_mode == "Manual"
def _set_recording(self, enable):
rec_mode = {"Automatic": 0, "Manual": 1}
self._api.record_mode = rec_mode["Manual" if enable else "Automatic"]
def _enable_recording(self, enable):
"""Turn recording on or off."""
@@ -494,86 +523,56 @@ class AmcrestCam(Camera):
# is_streaming and is_recording, we can't leave
# video stream off if recording is being turned on.
if not self.is_streaming and enable:
self._enable_video_stream(True)
rec_mode = {"Automatic": 0, "Manual": 1}
try:
self._api.record_mode = rec_mode["Manual" if enable else "Automatic"]
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"camera recording",
error,
)
else:
self._is_recording = enable
self.schedule_update_ha_state()
self._enable_video(True)
self._change_setting(enable, "_is_recording", "recording")
def _get_motion_detection(self):
return self._api.is_motion_detector_on()
def _set_motion_detection(self, enable):
self._api.motion_detection = str(enable).lower()
def _enable_motion_detection(self, enable):
"""Enable or disable motion detection."""
try:
self._api.motion_detection = str(enable).lower()
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"camera motion detection",
error,
)
else:
self._motion_detection_enabled = enable
self.schedule_update_ha_state()
self._change_setting(enable, "_motion_detection_enabled", "motion detection")
def _get_audio(self):
return self._api.audio_enabled
def _set_audio(self, enable):
self._api.audio_enabled = enable
def _enable_audio(self, enable):
"""Enable or disable audio stream."""
try:
self._api.audio_enabled = enable
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"camera audio stream",
error,
)
else:
self._audio_enabled = enable
self.schedule_update_ha_state()
self._change_setting(enable, "_audio_enabled", "audio")
if self._control_light:
self._enable_light(self._audio_enabled or self.is_streaming)
self._change_light()
def _enable_light(self, enable):
def _get_indicator_light(self):
return "true" in self._api.command(
"configManager.cgi?action=getConfig&name=LightGlobal"
).content.decode("utf-8")
def _set_indicator_light(self, enable):
self._api.command(
f"configManager.cgi?action=setConfig&LightGlobal[0].Enable={str(enable).lower()}"
)
def _change_light(self):
"""Enable or disable indicator light."""
try:
self._api.command(
f"configManager.cgi?action=setConfig&LightGlobal[0].Enable={str(enable).lower()}"
)
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"indicator light",
error,
)
self._change_setting(
self._audio_enabled or self.is_streaming, None, "indicator light"
)
def _get_motion_recording(self):
return self._api.is_record_on_motion_detection()
def _set_motion_recording(self, enable):
self._api.motion_recording = str(enable).lower()
def _enable_motion_recording(self, enable):
"""Enable or disable motion recording."""
try:
self._api.motion_recording = str(enable).lower()
except AmcrestError as error:
log_update_error(
_LOGGER,
"enable" if enable else "disable",
self.name,
"camera motion recording",
error,
)
else:
self._motion_recording_enabled = enable
self.schedule_update_ha_state()
self._change_setting(enable, "_motion_recording_enabled", "motion recording")
def _goto_preset(self, preset):
"""Move camera position and zoom to preset."""
@@ -584,17 +583,15 @@ class AmcrestCam(Camera):
_LOGGER, "move", self.name, f"camera to preset {preset}", error
)
def _get_color_mode(self):
return _CBW[self._api.day_night_color]
def _set_color_mode(self, cbw):
self._api.day_night_color = _CBW.index(cbw)
def _set_color_bw(self, cbw):
"""Set camera color mode."""
try:
self._api.day_night_color = _CBW.index(cbw)
except AmcrestError as error:
log_update_error(
_LOGGER, "set", self.name, f"camera color mode to {cbw}", error
)
else:
self._color_bw = cbw
self.schedule_update_ha_state()
self._change_setting(cbw, "_color_bw", "color mode")
def _start_tour(self, start):
"""Start camera tour."""

View File

@@ -1,4 +1,6 @@
"""Helpers for amcrest component."""
import logging
from .const import DOMAIN
@@ -7,9 +9,10 @@ def service_signal(service, *args):
return "_".join([DOMAIN, service, *args])
def log_update_error(logger, action, name, entity_type, error):
def log_update_error(logger, action, name, entity_type, error, level=logging.ERROR):
"""Log an update error."""
logger.error(
logger.log(
level,
"Could not %s %s %s due to error: %s",
action,
name,

View File

@@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/androidtv",
"requirements": [
"adb-shell==0.1.3",
"androidtv==0.0.41",
"androidtv==0.0.43",
"pure-python-adb==0.2.2.dev0"
],
"codeowners": ["@JeffLIrion"]

View File

@@ -5,27 +5,15 @@ import logging
from arcam.fmj import ConnectionFailed
from arcam.fmj.client import Client
import async_timeout
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_ZONE,
EVENT_HOMEASSISTANT_STOP,
SERVICE_TURN_ON,
)
from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from .const import (
DEFAULT_NAME,
DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
DOMAIN_DATA_CONFIG,
DOMAIN_DATA_ENTRIES,
DOMAIN_DATA_TASKS,
SIGNAL_CLIENT_DATA,
@@ -35,44 +23,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
def _optional_zone(value):
if value:
return ZONE_SCHEMA(value)
return ZONE_SCHEMA({})
def _zone_name_validator(config):
for zone, zone_config in config[CONF_ZONE].items():
if CONF_NAME not in zone_config:
zone_config[
CONF_NAME
] = f"{DEFAULT_NAME} ({config[CONF_HOST]}:{config[CONF_PORT]}) - {zone}"
return config
ZONE_SCHEMA = vol.Schema(
{
vol.Optional(CONF_NAME): cv.string,
vol.Optional(SERVICE_TURN_ON): cv.SERVICE_SCHEMA,
}
)
DEVICE_SCHEMA = vol.Schema(
vol.All(
{
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.positive_int,
vol.Optional(CONF_ZONE, default={1: _optional_zone(None)}): {
vol.In([1, 2]): _optional_zone
},
vol.Optional(
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
): cv.positive_int,
},
_zone_name_validator,
)
)
CONFIG_SCHEMA = cv.deprecated(DOMAIN, invalidation_version="0.115")
async def _await_cancel(task):
@@ -83,27 +34,10 @@ async def _await_cancel(task):
pass
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.All(cv.ensure_list, [DEVICE_SCHEMA])}, extra=vol.ALLOW_EXTRA
)
async def async_setup(hass: HomeAssistantType, config: ConfigType):
"""Set up the component."""
hass.data[DOMAIN_DATA_ENTRIES] = {}
hass.data[DOMAIN_DATA_TASKS] = {}
hass.data[DOMAIN_DATA_CONFIG] = {}
for device in config[DOMAIN]:
hass.data[DOMAIN_DATA_CONFIG][(device[CONF_HOST], device[CONF_PORT])] = device
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={CONF_HOST: device[CONF_HOST], CONF_PORT: device[CONF_PORT]},
)
)
async def _stop(_):
asyncio.gather(
@@ -116,21 +50,12 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType):
async def async_setup_entry(hass: HomeAssistantType, entry: config_entries.ConfigEntry):
"""Set up an access point from a config entry."""
"""Set up config entry."""
entries = hass.data[DOMAIN_DATA_ENTRIES]
tasks = hass.data[DOMAIN_DATA_TASKS]
client = Client(entry.data[CONF_HOST], entry.data[CONF_PORT])
config = hass.data[DOMAIN_DATA_CONFIG].get(
(entry.data[CONF_HOST], entry.data[CONF_PORT]),
DEVICE_SCHEMA(
{CONF_HOST: entry.data[CONF_HOST], CONF_PORT: entry.data[CONF_PORT]}
),
)
tasks = hass.data.setdefault(DOMAIN_DATA_TASKS, {})
hass.data[DOMAIN_DATA_ENTRIES][entry.entry_id] = {
"client": client,
"config": config,
}
entries[entry.entry_id] = client
task = asyncio.create_task(_run_client(hass, client, DEFAULT_SCAN_INTERVAL))
tasks[entry.entry_id] = task

View File

@@ -1,27 +1,102 @@
"""Config flow to configure the Arcam FMJ component."""
from operator import itemgetter
import logging
from urllib.parse import urlparse
from arcam.fmj.client import Client, ConnectionFailed
from arcam.fmj.utils import get_uniqueid_from_host, get_uniqueid_from_udn
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.ssdp import ATTR_SSDP_LOCATION, ATTR_UPNP_UDN
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN, DOMAIN_DATA_ENTRIES
_GETKEY = itemgetter(CONF_HOST, CONF_PORT)
_LOGGER = logging.getLogger(__name__)
def get_entry_client(hass, entry):
"""Retrieve client associated with a config entry."""
return hass.data[DOMAIN_DATA_ENTRIES][entry.entry_id]
@config_entries.HANDLERS.register(DOMAIN)
class ArcamFmjFlowHandler(config_entries.ConfigFlow):
"""Handle a SimpliSafe config flow."""
"""Handle config flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
async def async_step_import(self, import_config):
"""Import a config entry from configuration.yaml."""
entries = self.hass.config_entries.async_entries(DOMAIN)
import_key = _GETKEY(import_config)
for entry in entries:
if _GETKEY(entry.data) == import_key:
return self.async_abort(reason="already_setup")
async def _async_set_unique_id_and_update(self, host, port, uuid):
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port})
return self.async_create_entry(title="Arcam FMJ", data=import_config)
async def _async_check_and_create(self, host, port):
client = Client(host, port)
try:
await client.start()
except ConnectionFailed:
return self.async_abort(reason="unable_to_connect")
finally:
await client.stop()
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({host})", data={CONF_HOST: host, CONF_PORT: port},
)
async def async_step_user(self, user_input=None):
"""Handle a discovered device."""
errors = {}
if user_input is not None:
uuid = await get_uniqueid_from_host(
async_get_clientsession(self.hass), user_input[CONF_HOST]
)
if uuid:
await self._async_set_unique_id_and_update(
user_input[CONF_HOST], user_input[CONF_PORT], uuid
)
return await self._async_check_and_create(
user_input[CONF_HOST], user_input[CONF_PORT]
)
fields = {
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
return self.async_show_form(
step_id="user", data_schema=vol.Schema(fields), errors=errors
)
async def async_step_confirm(self, user_input=None):
"""Handle user-confirmation of discovered node."""
context = self.context # pylint: disable=no-member
placeholders = {
"host": context[CONF_HOST],
}
context["title_placeholders"] = placeholders
if user_input is not None:
return await self._async_check_and_create(
context[CONF_HOST], context[CONF_PORT]
)
return self.async_show_form(
step_id="confirm", description_placeholders=placeholders
)
async def async_step_ssdp(self, discovery_info):
"""Handle a discovered device."""
host = urlparse(discovery_info[ATTR_SSDP_LOCATION]).hostname
port = DEFAULT_PORT
uuid = get_uniqueid_from_udn(discovery_info[ATTR_UPNP_UDN])
await self._async_set_unique_id_and_update(host, port, uuid)
context = self.context # pylint: disable=no-member
context[CONF_HOST] = host
context[CONF_PORT] = DEFAULT_PORT
return await self.async_step_confirm()

View File

@@ -13,4 +13,3 @@ DEFAULT_SCAN_INTERVAL = 5
DOMAIN_DATA_ENTRIES = f"{DOMAIN}.entries"
DOMAIN_DATA_TASKS = f"{DOMAIN}.tasks"
DOMAIN_DATA_CONFIG = f"{DOMAIN}.config"

View File

@@ -1,8 +1,14 @@
{
"domain": "arcam_fmj",
"name": "Arcam FMJ Receivers",
"config_flow": false,
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/arcam_fmj",
"requirements": ["arcam-fmj==0.4.6"],
"requirements": ["arcam-fmj==0.5.1"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
"manufacturer": "ARCAM"
}
],
"codeowners": ["@elupus"]
}

View File

@@ -1,6 +1,5 @@
"""Arcam media player."""
import logging
from typing import Optional
from arcam.fmj import DecodeMode2CH, DecodeModeMCH, IncomingAudioFormat, SourceCodes
from arcam.fmj.state import State
@@ -17,21 +16,13 @@ from homeassistant.components.media_player.const import (
SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_NAME,
CONF_ZONE,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import callback
from homeassistant.helpers.service import async_call_from_config
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.helpers.typing import HomeAssistantType
from .config_flow import get_entry_client
from .const import (
DOMAIN,
DOMAIN_DATA_ENTRIES,
EVENT_TURN_ON,
SIGNAL_CLIENT_DATA,
SIGNAL_CLIENT_STARTED,
@@ -47,19 +38,17 @@ async def async_setup_entry(
async_add_entities,
):
"""Set up the configuration entry."""
data = hass.data[DOMAIN_DATA_ENTRIES][config_entry.entry_id]
client = data["client"]
config = data["config"]
client = get_entry_client(hass, config_entry)
async_add_entities(
[
ArcamFmj(
config_entry.title,
State(client, zone),
config_entry.unique_id or config_entry.entry_id,
zone_config[CONF_NAME],
zone_config.get(SERVICE_TURN_ON),
)
for zone, zone_config in config[CONF_ZONE].items()
for zone in [1, 2]
],
True,
)
@@ -71,13 +60,13 @@ class ArcamFmj(MediaPlayerEntity):
"""Representation of a media device."""
def __init__(
self, state: State, uuid: str, name: str, turn_on: Optional[ConfigType]
self, device_name, state: State, uuid: str,
):
"""Initialize device."""
self._state = state
self._device_name = device_name
self._name = f"{device_name} - Zone: {state.zn}"
self._uuid = uuid
self._name = name
self._turn_on = turn_on
self._support = (
SUPPORT_SELECT_SOURCE
| SUPPORT_VOLUME_SET
@@ -102,6 +91,11 @@ class ArcamFmj(MediaPlayerEntity):
)
)
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return self._state.zn == 1
@property
def unique_id(self):
"""Return unique identifier if known."""
@@ -111,8 +105,12 @@ class ArcamFmj(MediaPlayerEntity):
def device_info(self):
"""Return a device description for device registry."""
return {
"identifiers": {(DOMAIN, self._state.client.host, self._state.client.port)},
"model": "FMJ",
"name": self._device_name,
"identifiers": {
(DOMAIN, self._uuid),
(DOMAIN, self._state.client.host, self._state.client.port),
},
"model": "Arcam FMJ AVR",
"manufacturer": "Arcam",
}
@@ -229,15 +227,6 @@ class ArcamFmj(MediaPlayerEntity):
if self._state.get_power() is not None:
_LOGGER.debug("Turning on device using connection")
await self._state.set_power(True)
elif self._turn_on:
_LOGGER.debug("Turning on device using service call")
await async_call_from_config(
self.hass,
self._turn_on,
variables=None,
blocking=True,
validate_config=False,
)
else:
_LOGGER.debug("Firing event to turn on device")
self.hass.bus.async_fire(EVENT_TURN_ON, {ATTR_ENTITY_ID: self.entity_id})

View File

@@ -1,7 +1,28 @@
{
"config": {
"abort": {
"already_configured": "Device was already setup.",
"already_in_progress": "Config flow for device is already in progress.",
"unable_to_connect": "Unable to connect to device."
},
"error": {},
"flow_title": "Arcam FMJ on {host}",
"step": {
"confirm": {
"description": "Do you want to add Arcam FMJ on `{host}` to Home Assistant?"
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "Please enter the host name or IP address of device."
}
}
},
"device_automation": {
"trigger_type": {
"turn_on": "{entity_name} was requested to turn on"
}
}
}
}

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -2,7 +2,7 @@
"config": {
"abort": {
"already_configured": "El dispositivo ya est\u00e1 configurado.",
"already_in_progress": "La configuraci\u00f3n del flujo para el dispositivo ya est\u00e1 en marcha.",
"already_in_progress": "El flujo de configuraci\u00f3n para el dispositivo ya est\u00e1 en marcha.",
"unable_to_connect": "No se puede conectar con el dispositivo."
},
"flow_title": "Arcam FMJ en {host}",

View File

@@ -1,4 +1,24 @@
{
"config": {
"abort": {
"already_configured": "L'appareil \u00e9tait d\u00e9j\u00e0 configur\u00e9.",
"already_in_progress": "Le flux de configuration de l'appareil est d\u00e9j\u00e0 en cours.",
"unable_to_connect": "Impossible de se connecter au p\u00e9riph\u00e9rique."
},
"error": {
"one": "Vide",
"other": "Vide"
},
"step": {
"user": {
"data": {
"host": "H\u00f4te",
"port": "Port"
},
"description": "Veuillez saisir le nom d\u2019h\u00f4te ou l\u2019adresse IP du p\u00e9riph\u00e9rique."
}
}
},
"device_automation": {
"trigger_type": {
"turn_on": "Il a \u00e9t\u00e9 demand\u00e9 \u00e0 {nom_de_l'entit\u00e9} de s'allumer"

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -1,4 +1,14 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "Nazwa hosta lub adres IP",
"port": "Port"
}
}
}
},
"device_automation": {
"trigger_type": {
"turn_on": "{entity_name} zostanie poproszony o w\u0142\u0105czenie"

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -1,3 +0,0 @@
{
"title": "Arcam FMJ"
}

View File

@@ -24,6 +24,7 @@ class AsusWrtDeviceScanner(DeviceScanner):
self.last_results = {}
self.success_init = False
self.connection = api
self._connect_error = False
async def async_connect(self):
"""Initialize connection to the router."""
@@ -49,4 +50,15 @@ class AsusWrtDeviceScanner(DeviceScanner):
"""
_LOGGER.debug("Checking Devices")
self.last_results = await self.connection.async_get_connected_devices()
try:
self.last_results = await self.connection.async_get_connected_devices()
if self._connect_error:
self._connect_error = False
_LOGGER.error("Reconnected to ASUS router for device update")
except OSError as err:
if not self._connect_error:
self._connect_error = True
_LOGGER.error(
"Error connecting to ASUS router for device update: %s", err
)

View File

@@ -2,6 +2,6 @@
"domain": "asuswrt",
"name": "ASUSWRT",
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
"requirements": ["aioasuswrt==1.2.5"],
"requirements": ["aioasuswrt==1.2.6"],
"codeowners": ["@kennedyshead"]
}

View File

@@ -49,6 +49,7 @@ class AsuswrtSensor(Entity):
self._devices = None
self._rates = None
self._speed = None
self._connect_error = False
@property
def name(self):
@@ -62,9 +63,23 @@ class AsuswrtSensor(Entity):
async def async_update(self):
"""Fetch status from asuswrt."""
self._devices = await self._api.async_get_connected_devices()
self._rates = await self._api.async_get_bytes_total()
self._speed = await self._api.async_get_current_transfer_rates()
try:
self._devices = await self._api.async_get_connected_devices()
self._rates = await self._api.async_get_bytes_total()
self._speed = await self._api.async_get_current_transfer_rates()
if self._connect_error:
self._connect_error = False
_LOGGER.error(
"Reconnected to ASUS router for %s update", self.entity_id
)
except OSError as err:
if not self._connect_error:
self._connect_error = True
_LOGGER.error(
"Error connecting to ASUS router for %s update: %s",
self.entity_id,
err,
)
class AsuswrtDevicesSensor(AsuswrtSensor):

View File

@@ -1,7 +1,7 @@
{
"config": {
"abort": {
"already_configured": "Nur ein Atag-Ger\u00e4t kann mit Home Assistant verbunden werden."
"already_configured": "Dieses Ger\u00e4t wurde bereits zu HomeAssistant hinzugef\u00fcgt"
},
"error": {
"connection_error": "Verbindung fehlgeschlagen, versuchen Sie es erneut"

View File

@@ -3,7 +3,6 @@
"name": "Auth",
"documentation": "https://www.home-assistant.io/integrations/auth",
"dependencies": ["http"],
"after_dependencies": ["onboarding"],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@@ -19,7 +19,7 @@ DEFAULT_QOS = 0
TRIGGER_SCHEMA = vol.Schema(
{
vol.Required(CONF_PLATFORM): mqtt.DOMAIN,
vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic,
vol.Required(CONF_TOPIC): mqtt.util.valid_subscribe_topic,
vol.Optional(CONF_PAYLOAD): cv.string,
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
vol.Optional(CONF_QOS, default=DEFAULT_QOS): vol.All(

View File

@@ -0,0 +1,24 @@
{
"config": {
"abort": {
"already_configured": "This address is already configured."
},
"error": {
"invalid_country_code": "Unknown 2 letter country code.",
"invalid_house_number": "Invalid house number."
},
"step": {
"user": {
"data": {
"country_code": "2 Letter country code",
"house_number": "House number",
"house_number_extension": "House number extension",
"zip_code": "Zip code"
},
"description": "Enter your address",
"title": "Avri"
}
}
},
"title": "Avri"
}

View File

@@ -0,0 +1,24 @@
{
"config": {
"abort": {
"already_configured": "Dit adres is reeds geconfigureerd."
},
"error": {
"invalid_country_code": "Onbekende landcode",
"invalid_house_number": "Ongeldig huisnummer."
},
"step": {
"user": {
"data": {
"country_code": "2 Letter landcode",
"house_number": "Huisnummer",
"house_number_extension": "Huisnummer toevoeging",
"zip_code": "Postcode"
},
"description": "Vul je adres in.",
"title": "Avri"
}
}
},
"title": "Avri"
}

View File

@@ -1 +1,63 @@
"""The avri component."""
import asyncio
from datetime import timedelta
import logging
from avri.api import Avri
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import (
CONF_COUNTRY_CODE,
CONF_HOUSE_NUMBER,
CONF_HOUSE_NUMBER_EXTENSION,
CONF_ZIP_CODE,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"]
SCAN_INTERVAL = timedelta(hours=4)
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Avri component."""
hass.data[DOMAIN] = {}
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Avri from a config entry."""
client = Avri(
postal_code=entry.data[CONF_ZIP_CODE],
house_nr=entry.data[CONF_HOUSE_NUMBER],
house_nr_extension=entry.data.get(CONF_HOUSE_NUMBER_EXTENSION),
country_code=entry.data[CONF_COUNTRY_CODE],
)
hass.data[DOMAIN][entry.entry_id] = client
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, component)
for component in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@@ -0,0 +1,74 @@
"""Config flow for Avri component."""
import pycountry
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_ID
from .const import (
CONF_COUNTRY_CODE,
CONF_HOUSE_NUMBER,
CONF_HOUSE_NUMBER_EXTENSION,
CONF_ZIP_CODE,
DEFAULT_COUNTRY_CODE,
)
from .const import DOMAIN # pylint:disable=unused-import
DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_ZIP_CODE): str,
vol.Required(CONF_HOUSE_NUMBER): int,
vol.Optional(CONF_HOUSE_NUMBER_EXTENSION): str,
vol.Optional(CONF_COUNTRY_CODE, default=DEFAULT_COUNTRY_CODE): str,
}
)
class AvriConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Avri config flow."""
VERSION = 1
async def _show_setup_form(self, errors=None):
"""Show the setup form to the user."""
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors or {},
)
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
if user_input is None:
return await self._show_setup_form()
zip_code = user_input[CONF_ZIP_CODE].replace(" ", "").upper()
errors = {}
if user_input[CONF_HOUSE_NUMBER] <= 0:
errors[CONF_HOUSE_NUMBER] = "invalid_house_number"
return await self._show_setup_form(errors)
if not pycountry.countries.get(alpha_2=user_input[CONF_COUNTRY_CODE]):
errors[CONF_COUNTRY_CODE] = "invalid_country_code"
return await self._show_setup_form(errors)
unique_id = (
f"{zip_code}"
f" "
f"{user_input[CONF_HOUSE_NUMBER]}"
f'{user_input.get(CONF_HOUSE_NUMBER_EXTENSION, "")}'
)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=unique_id,
data={
CONF_ID: unique_id,
CONF_ZIP_CODE: zip_code,
CONF_HOUSE_NUMBER: user_input[CONF_HOUSE_NUMBER],
CONF_HOUSE_NUMBER_EXTENSION: user_input.get(
CONF_HOUSE_NUMBER_EXTENSION, ""
),
CONF_COUNTRY_CODE: user_input[CONF_COUNTRY_CODE],
},
)

View File

@@ -0,0 +1,8 @@
"""Constants for the Avri integration."""
CONF_COUNTRY_CODE = "country_code"
CONF_ZIP_CODE = "zip_code"
CONF_HOUSE_NUMBER = "house_number"
CONF_HOUSE_NUMBER_EXTENSION = "house_number_extension"
DOMAIN = "avri"
ICON = "mdi:trash-can-outline"
DEFAULT_COUNTRY_CODE = "NL"

View File

@@ -2,6 +2,12 @@
"domain": "avri",
"name": "Avri",
"documentation": "https://www.home-assistant.io/integrations/avri",
"requirements": ["avri-api==0.1.7"],
"codeowners": ["@timvancann"]
}
"requirements": [
"avri-api==0.1.7",
"pycountry==19.8.18"
],
"codeowners": [
"@timvancann"
],
"config_flow": true
}

View File

@@ -1,45 +1,25 @@
"""Support for Avri waste curbside collection pickup."""
from datetime import timedelta
import logging
from avri.api import Avri, AvriException
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_NAME
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, DEVICE_CLASS_TIMESTAMP
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from .const import DOMAIN, ICON
_LOGGER = logging.getLogger(__name__)
CONF_COUNTRY_CODE = "country_code"
CONF_ZIP_CODE = "zip_code"
CONF_HOUSE_NUMBER = "house_number"
CONF_HOUSE_NUMBER_EXTENSION = "house_number_extension"
DEFAULT_NAME = "avri"
ICON = "mdi:trash-can-outline"
SCAN_INTERVAL = timedelta(hours=4)
DEFAULT_COUNTRY_CODE = "NL"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ZIP_CODE): cv.string,
vol.Required(CONF_HOUSE_NUMBER): cv.positive_int,
vol.Optional(CONF_HOUSE_NUMBER_EXTENSION): cv.string,
vol.Optional(CONF_COUNTRY_CODE, default=DEFAULT_COUNTRY_CODE): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
"""Set up the Avri Waste platform."""
client = Avri(
postal_code=config[CONF_ZIP_CODE],
house_nr=config[CONF_HOUSE_NUMBER],
house_nr_extension=config.get(CONF_HOUSE_NUMBER_EXTENSION),
country_code=config[CONF_COUNTRY_CODE],
)
client = hass.data[DOMAIN][entry.entry_id]
integration_id = entry.data[CONF_ID]
try:
each_upcoming = client.upcoming_of_each()
@@ -47,22 +27,23 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
raise PlatformNotReady from ex
else:
entities = [
AvriWasteUpcoming(config[CONF_NAME], client, upcoming.name)
AvriWasteUpcoming(client, upcoming.name, integration_id)
for upcoming in each_upcoming
]
add_entities(entities, True)
async_add_entities(entities, True)
class AvriWasteUpcoming(Entity):
"""Avri Waste Sensor."""
def __init__(self, name: str, client: Avri, waste_type: str):
def __init__(self, client: Avri, waste_type: str, integration_id: str):
"""Initialize the sensor."""
self._waste_type = waste_type
self._name = f"{name}_{self._waste_type}"
self._name = f"{self._waste_type}".title()
self._state = None
self._client = client
self._state_available = False
self._integration_id = integration_id
@property
def name(self):
@@ -72,13 +53,7 @@ class AvriWasteUpcoming(Entity):
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return (
f"{self._waste_type}"
f"-{self._client.country_code}"
f"-{self._client.postal_code}"
f"-{self._client.house_nr}"
f"-{self._client.house_nr_extension}"
)
return (f"{self._integration_id}" f"-{self._waste_type}").replace(" ", "")
@property
def state(self):
@@ -90,13 +65,21 @@ class AvriWasteUpcoming(Entity):
"""Return True if entity is available."""
return self._state_available
@property
def device_class(self):
"""Return the device class of the sensor."""
return DEVICE_CLASS_TIMESTAMP
@property
def icon(self):
"""Icon to use in the frontend."""
return ICON
def update(self):
"""Update device state."""
async def async_update(self):
"""Update the data."""
if not self.enabled:
return
try:
pickup_events = self._client.upcoming_of_each()
except AvriException as ex:

View File

@@ -0,0 +1,24 @@
{
"title": "Avri",
"config": {
"abort": {
"already_configured": "This address is already configured."
},
"error": {
"invalid_house_number": "Invalid house number.",
"invalid_country_code": "Unknown 2 letter country code."
},
"step": {
"user": {
"data": {
"zip_code": "Zip code",
"house_number": "House number",
"house_number_extension": "House number extension",
"country_code": "2 Letter country code"
},
"description": "Enter your address",
"title": "Avri"
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"config": {
"abort": {
"already_configured": "\u062a\u0645 \u062a\u0643\u0648\u064a\u0646 \u0647\u0630\u0627 \u0627\u0644\u0639\u0646\u0648\u0627\u0646 \u0628\u0627\u0644\u0641\u0639\u0644."
}
}
}

View File

@@ -1,6 +1,10 @@
{
"config": {
"abort": {
"already_configured": "Cette adresse est d\u00e9j\u00e0 configur\u00e9e."
},
"error": {
"invalid_country_code": "Code pays \u00e0 2 lettres inconnu.",
"invalid_house_number": "Num\u00e9ro de maison invalide."
},
"step": {

View File

@@ -1 +1,112 @@
"""The awair component."""
from asyncio import gather
from typing import Any, Optional
from async_timeout import timeout
from python_awair import Awair
from python_awair.exceptions import AuthError
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import API_TIMEOUT, DOMAIN, LOGGER, UPDATE_INTERVAL, AwairResult
PLATFORMS = ["sensor"]
async def async_setup(hass: HomeAssistant, config: Config) -> bool:
"""Set up Awair integration."""
return True
async def async_setup_entry(hass, config_entry) -> bool:
"""Set up Awair integration from a config entry."""
session = async_get_clientsession(hass)
coordinator = AwairDataUpdateCoordinator(hass, config_entry, session)
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = coordinator
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True
async def async_unload_entry(hass, config_entry) -> bool:
"""Unload Awair configuration."""
tasks = []
for platform in PLATFORMS:
tasks.append(
hass.config_entries.async_forward_entry_unload(config_entry, platform)
)
unload_ok = all(await gather(*tasks))
if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id)
return unload_ok
class AwairDataUpdateCoordinator(DataUpdateCoordinator):
"""Define a wrapper class to update Awair data."""
def __init__(self, hass, config_entry, session) -> None:
"""Set up the AwairDataUpdateCoordinator class."""
access_token = config_entry.data[CONF_ACCESS_TOKEN]
self._awair = Awair(access_token=access_token, session=session)
self._config_entry = config_entry
super().__init__(hass, LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL)
async def _async_update_data(self) -> Optional[Any]:
"""Update data via Awair client library."""
with timeout(API_TIMEOUT):
try:
LOGGER.debug("Fetching users and devices")
user = await self._awair.user()
devices = await user.devices()
results = await gather(
*[self._fetch_air_data(device) for device in devices]
)
return {result.device.uuid: result for result in results}
except AuthError as err:
flow_context = {
"source": "reauth",
"unique_id": self._config_entry.unique_id,
}
matching_flows = [
flow
for flow in self.hass.config_entries.flow.async_progress()
if flow["context"] == flow_context
]
if not matching_flows:
self.hass.async_create_task(
self.hass.config_entries.flow.async_init(
DOMAIN, context=flow_context, data=self._config_entry.data,
)
)
raise UpdateFailed(err)
except Exception as err:
raise UpdateFailed(err)
async def _fetch_air_data(self, device):
"""Fetch latest air quality data."""
LOGGER.debug("Fetching data for %s", device.uuid)
air_data = await device.air_data_latest()
LOGGER.debug(air_data)
return AwairResult(device=device, air_data=air_data)

View File

@@ -0,0 +1,109 @@
"""Config flow for Awair."""
from typing import Optional
from python_awair import Awair
from python_awair.exceptions import AuthError, AwairError
import voluptuous as vol
from homeassistant.config_entries import CONN_CLASS_CLOUD_POLL, ConfigFlow
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN, LOGGER # pylint: disable=unused-import
class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Awair."""
VERSION = 1
CONNECTION_CLASS = CONN_CLASS_CLOUD_POLL
async def async_step_import(self, conf: dict):
"""Import a configuration from config.yaml."""
if self.hass.config_entries.async_entries(DOMAIN):
return self.async_abort(reason="already_setup")
user, error = await self._check_connection(conf[CONF_ACCESS_TOKEN])
if error is not None:
return self.async_abort(reason=error)
await self.async_set_unique_id(user.email)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=f"{user.email} ({user.user_id})",
data={CONF_ACCESS_TOKEN: conf[CONF_ACCESS_TOKEN]},
)
async def async_step_user(self, user_input: Optional[dict] = None):
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
user, error = await self._check_connection(user_input[CONF_ACCESS_TOKEN])
if user is not None:
await self.async_set_unique_id(user.email)
self._abort_if_unique_id_configured()
title = f"{user.email} ({user.user_id})"
return self.async_create_entry(title=title, data=user_input)
if error != "auth":
return self.async_abort(reason=error)
errors = {CONF_ACCESS_TOKEN: "auth"}
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str}),
errors=errors,
)
async def async_step_reauth(self, user_input: Optional[dict] = None):
"""Handle re-auth if token invalid."""
errors = {}
if user_input is not None:
access_token = user_input[CONF_ACCESS_TOKEN]
_, error = await self._check_connection(access_token)
if error is None:
for entry in self._async_current_entries():
if entry.unique_id == self.unique_id:
self.hass.config_entries.async_update_entry(
entry, data=user_input
)
return self.async_abort(reason="reauth_successful")
if error != "auth":
return self.async_abort(reason=error)
errors = {CONF_ACCESS_TOKEN: error}
return self.async_show_form(
step_id="reauth",
data_schema=vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str}),
errors=errors,
)
async def _check_connection(self, access_token: str):
"""Check the access token is valid."""
session = async_get_clientsession(self.hass)
awair = Awair(access_token=access_token, session=session)
try:
user = await awair.user()
devices = await user.devices()
if not devices:
return (None, "no_devices")
return (user, None)
except AuthError:
return (None, "auth")
except AwairError as err:
LOGGER.error("Unexpected API error: %s", err)
return (None, "unknown")

View File

@@ -0,0 +1,120 @@
"""Constants for the Awair component."""
from dataclasses import dataclass
from datetime import timedelta
import logging
from python_awair.devices import AwairDevice
from homeassistant.const import (
ATTR_DEVICE_CLASS,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_TEMPERATURE,
TEMP_CELSIUS,
UNIT_PERCENTAGE,
)
API_CO2 = "carbon_dioxide"
API_DUST = "dust"
API_HUMID = "humidity"
API_LUX = "illuminance"
API_PM10 = "particulate_matter_10"
API_PM25 = "particulate_matter_2_5"
API_SCORE = "score"
API_SPL_A = "sound_pressure_level"
API_TEMP = "temperature"
API_TIMEOUT = 20
API_VOC = "volatile_organic_compounds"
ATTRIBUTION = "Awair air quality sensor"
ATTR_ICON = "icon"
ATTR_LABEL = "label"
ATTR_UNIT = "unit"
ATTR_UNIQUE_ID = "unique_id"
DOMAIN = "awair"
DUST_ALIASES = [API_PM25, API_PM10]
LOGGER = logging.getLogger(__package__)
UPDATE_INTERVAL = timedelta(minutes=5)
SENSOR_TYPES = {
API_SCORE: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_UNIT: UNIT_PERCENTAGE,
ATTR_LABEL: "Awair score",
ATTR_UNIQUE_ID: "score", # matches legacy format
},
API_HUMID: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_ICON: None,
ATTR_UNIT: UNIT_PERCENTAGE,
ATTR_LABEL: "Humidity",
ATTR_UNIQUE_ID: "HUMID", # matches legacy format
},
API_LUX: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_ILLUMINANCE,
ATTR_ICON: None,
ATTR_UNIT: "lx",
ATTR_LABEL: "Illuminance",
ATTR_UNIQUE_ID: "illuminance",
},
API_SPL_A: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:ear-hearing",
ATTR_UNIT: "dBa",
ATTR_LABEL: "Sound level",
ATTR_UNIQUE_ID: "sound_level",
},
API_VOC: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:cloud",
ATTR_UNIT: CONCENTRATION_PARTS_PER_BILLION,
ATTR_LABEL: "Volatile organic compounds",
ATTR_UNIQUE_ID: "VOC", # matches legacy format
},
API_TEMP: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_ICON: None,
ATTR_UNIT: TEMP_CELSIUS,
ATTR_LABEL: "Temperature",
ATTR_UNIQUE_ID: "TEMP", # matches legacy format
},
API_PM25: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
ATTR_LABEL: "PM2.5",
ATTR_UNIQUE_ID: "PM25", # matches legacy format
},
API_PM10: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
ATTR_LABEL: "PM10",
ATTR_UNIQUE_ID: "PM10", # matches legacy format
},
API_CO2: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:cloud",
ATTR_UNIT: CONCENTRATION_PARTS_PER_MILLION,
ATTR_LABEL: "Carbon dioxide",
ATTR_UNIQUE_ID: "CO2", # matches legacy format
},
}
@dataclass
class AwairResult:
"""Wrapper class to hold an awair device and set of air data."""
device: AwairDevice
air_data: dict

View File

@@ -2,6 +2,7 @@
"domain": "awair",
"name": "Awair",
"documentation": "https://www.home-assistant.io/integrations/awair",
"requirements": ["python_awair==0.0.4"],
"codeowners": ["@danielsjf"]
"requirements": ["python_awair==0.1.1"],
"codeowners": ["@ahayworth", "@danielsjf"],
"config_flow": true
}

View File

@@ -1,248 +1,245 @@
"""Support for the Awair indoor air quality monitor."""
"""Support for Awair sensors."""
from datetime import timedelta
import logging
import math
from typing import Callable, List, Optional
from python_awair import AwairClient
from python_awair.devices import AwairDevice
import voluptuous as vol
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
CONF_ACCESS_TOKEN,
CONF_DEVICES,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
TEMP_CELSIUS,
UNIT_PERCENTAGE,
)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.components.awair import AwairDataUpdateCoordinator, AwairResult
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS, CONF_ACCESS_TOKEN
from homeassistant.helpers import device_registry as dr
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle, dt
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
_LOGGER = logging.getLogger(__name__)
from .const import (
API_DUST,
API_PM25,
API_SCORE,
API_TEMP,
API_VOC,
ATTR_ICON,
ATTR_LABEL,
ATTR_UNIQUE_ID,
ATTR_UNIT,
ATTRIBUTION,
DOMAIN,
DUST_ALIASES,
LOGGER,
SENSOR_TYPES,
)
ATTR_SCORE = "score"
ATTR_TIMESTAMP = "timestamp"
ATTR_LAST_API_UPDATE = "last_api_update"
ATTR_COMPONENT = "component"
ATTR_VALUE = "value"
ATTR_SENSORS = "sensors"
CONF_UUID = "uuid"
DEVICE_CLASS_PM2_5 = "PM2.5"
DEVICE_CLASS_PM10 = "PM10"
DEVICE_CLASS_CARBON_DIOXIDE = "CO2"
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS = "VOC"
DEVICE_CLASS_SCORE = "score"
SENSOR_TYPES = {
"TEMP": {
"device_class": DEVICE_CLASS_TEMPERATURE,
"unit_of_measurement": TEMP_CELSIUS,
"icon": "mdi:thermometer",
},
"HUMID": {
"device_class": DEVICE_CLASS_HUMIDITY,
"unit_of_measurement": UNIT_PERCENTAGE,
"icon": "mdi:water-percent",
},
"CO2": {
"device_class": DEVICE_CLASS_CARBON_DIOXIDE,
"unit_of_measurement": CONCENTRATION_PARTS_PER_MILLION,
"icon": "mdi:periodic-table-co2",
},
"VOC": {
"device_class": DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
"unit_of_measurement": CONCENTRATION_PARTS_PER_BILLION,
"icon": "mdi:cloud",
},
# Awair docs don't actually specify the size they measure for 'dust',
# but 2.5 allows the sensor to show up in HomeKit
"DUST": {
"device_class": DEVICE_CLASS_PM2_5,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"icon": "mdi:cloud",
},
"PM25": {
"device_class": DEVICE_CLASS_PM2_5,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"icon": "mdi:cloud",
},
"PM10": {
"device_class": DEVICE_CLASS_PM10,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"icon": "mdi:cloud",
},
"score": {
"device_class": DEVICE_CLASS_SCORE,
"unit_of_measurement": UNIT_PERCENTAGE,
"icon": "mdi:percent",
},
}
AWAIR_QUOTA = 300
# This is the minimum time between throttled update calls.
# Don't bother asking us for state more often than that.
SCAN_INTERVAL = timedelta(minutes=5)
AWAIR_DEVICE_SCHEMA = vol.Schema({vol.Required(CONF_UUID): cv.string})
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ACCESS_TOKEN): cv.string,
vol.Optional(CONF_DEVICES): vol.All(cv.ensure_list, [AWAIR_DEVICE_SCHEMA]),
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_ACCESS_TOKEN): cv.string}, extra=vol.ALLOW_EXTRA,
)
# Awair *heavily* throttles calls that get user information,
# and calls that get the list of user-owned devices - they
# allow 30 per DAY. So, we permit a user to provide a static
# list of devices, and they may provide the same set of information
# that the devices() call would return. However, the only thing
# used at this time is the `uuid` value.
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Connect to the Awair API and find devices."""
"""Import Awair configuration from YAML."""
LOGGER.warning(
"Loading Awair via platform setup is deprecated. Please remove it from your configuration."
)
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=config,
)
)
token = config[CONF_ACCESS_TOKEN]
client = AwairClient(token, session=async_get_clientsession(hass))
try:
all_devices = []
devices = config.get(CONF_DEVICES, await client.devices())
async def async_setup_entry(
hass: HomeAssistantType,
config_entry: ConfigType,
async_add_entities: Callable[[List[Entity], bool], None],
):
"""Set up Awair sensor entity based on a config entry."""
coordinator = hass.data[DOMAIN][config_entry.entry_id]
sensors = []
# Try to throttle dynamically based on quota and number of devices.
throttle_minutes = math.ceil(60 / ((AWAIR_QUOTA / len(devices)) / 24))
throttle = timedelta(minutes=throttle_minutes)
data: List[AwairResult] = coordinator.data.values()
for result in data:
if result.air_data:
sensors.append(AwairSensor(API_SCORE, result.device, coordinator))
device_sensors = result.air_data.sensors.keys()
for sensor in device_sensors:
if sensor in SENSOR_TYPES:
sensors.append(AwairSensor(sensor, result.device, coordinator))
for device in devices:
_LOGGER.debug("Found awair device: %s", device)
awair_data = AwairData(client, device[CONF_UUID], throttle)
await awair_data.async_update()
for sensor in SENSOR_TYPES:
if sensor in awair_data.data:
awair_sensor = AwairSensor(awair_data, device, sensor, throttle)
all_devices.append(awair_sensor)
# The "DUST" sensor for Awair is a combo pm2.5/pm10 sensor only
# present on first-gen devices in lieu of separate pm2.5/pm10 sensors.
# We handle that by creating fake pm2.5/pm10 sensors that will always
# report identical values, and we let users decide how they want to use
# that data - because we can't really tell what kind of particles the
# "DUST" sensor actually detected. However, it's still useful data.
if API_DUST in device_sensors:
for alias_kind in DUST_ALIASES:
sensors.append(AwairSensor(alias_kind, result.device, coordinator))
async_add_entities(all_devices, True)
return
except AwairClient.AuthError:
_LOGGER.error("Awair API access_token invalid")
except AwairClient.RatelimitError:
_LOGGER.error("Awair API ratelimit exceeded.")
except (
AwairClient.QueryError,
AwairClient.NotFoundError,
AwairClient.GenericError,
) as error:
_LOGGER.error("Unexpected Awair API error: %s", error)
raise PlatformNotReady
async_add_entities(sensors)
class AwairSensor(Entity):
"""Implementation of an Awair device."""
"""Defines an Awair sensor entity."""
def __init__(self, data, device, sensor_type, throttle):
"""Initialize the sensor."""
self._uuid = device[CONF_UUID]
self._device_class = SENSOR_TYPES[sensor_type]["device_class"]
self._name = f"Awair {self._device_class}"
unit = SENSOR_TYPES[sensor_type]["unit_of_measurement"]
self._unit_of_measurement = unit
self._data = data
self._type = sensor_type
self._throttle = throttle
def __init__(
self, kind: str, device: AwairDevice, coordinator: AwairDataUpdateCoordinator,
) -> None:
"""Set up an individual AwairSensor."""
self._kind = kind
self._device = device
self._coordinator = coordinator
@property
def name(self):
def should_poll(self) -> bool:
"""Return the polling requirement of the entity."""
return False
@property
def name(self) -> str:
"""Return the name of the sensor."""
return self._name
name = SENSOR_TYPES[self._kind][ATTR_LABEL]
if self._device.name:
name = f"{self._device.name} {name}"
return name
@property
def device_class(self):
"""Return the device class."""
return self._device_class
def unique_id(self) -> str:
"""Return the uuid as the unique_id."""
unique_id_tag = SENSOR_TYPES[self._kind][ATTR_UNIQUE_ID]
# This integration used to create a sensor that was labelled as a "PM2.5"
# sensor for first-gen Awair devices, but its unique_id reflected the truth:
# under the hood, it was a "DUST" sensor. So we preserve that specific unique_id
# for users with first-gen devices that are upgrading.
if self._kind == API_PM25 and API_DUST in self._air_data.sensors:
unique_id_tag = "DUST"
return f"{self._device.uuid}_{unique_id_tag}"
@property
def icon(self):
"""Icon to use in the frontend."""
return SENSOR_TYPES[self._type]["icon"]
def available(self) -> bool:
"""Determine if the sensor is available based on API results."""
# If the last update was successful...
if self._coordinator.last_update_success and self._air_data:
# and the results included our sensor type...
if self._kind in self._air_data.sensors:
# then we are available.
return True
# or, we're a dust alias
if self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
return True
# or we are API_SCORE
if self._kind == API_SCORE:
# then we are available.
return True
# Otherwise, we are not.
return False
@property
def state(self):
"""Return the state of the device."""
return self._data.data[self._type]
def state(self) -> float:
"""Return the state, rounding off to reasonable values."""
state: float
# Special-case for "SCORE", which we treat as the AQI
if self._kind == API_SCORE:
state = self._air_data.score
elif self._kind in DUST_ALIASES and API_DUST in self._air_data.sensors:
state = self._air_data.sensors.dust
else:
state = self._air_data.sensors[self._kind]
if self._kind == API_VOC or self._kind == API_SCORE:
return round(state)
if self._kind == API_TEMP:
return round(state, 1)
return round(state, 2)
@property
def device_state_attributes(self):
"""Return additional attributes."""
return self._data.attrs
# The Awair device should be reporting metrics in quite regularly.
# Based on the raw data from the API, it looks like every ~10 seconds
# is normal. Here we assert that the device is not available if the
# last known API timestamp is more than (3 * throttle) minutes in the
# past. It implies that either hass is somehow unable to query the API
# for new data or that the device is not checking in. Either condition
# fits the definition for 'not available'. We pick (3 * throttle) minutes
# to allow for transient errors to correct themselves.
@property
def available(self):
"""Device availability based on the last update timestamp."""
if ATTR_LAST_API_UPDATE not in self.device_state_attributes:
return False
last_api_data = self.device_state_attributes[ATTR_LAST_API_UPDATE]
return (dt.utcnow() - last_api_data) < (3 * self._throttle)
def icon(self) -> str:
"""Return the icon."""
return SENSOR_TYPES[self._kind][ATTR_ICON]
@property
def unique_id(self):
"""Return the unique id of this entity."""
return f"{self._uuid}_{self._type}"
def device_class(self) -> str:
"""Return the device_class."""
return SENSOR_TYPES[self._kind][ATTR_DEVICE_CLASS]
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity."""
return self._unit_of_measurement
def unit_of_measurement(self) -> str:
"""Return the unit the value is expressed in."""
return SENSOR_TYPES[self._kind][ATTR_UNIT]
async def async_update(self):
"""Get the latest data."""
await self._data.async_update()
@property
def device_state_attributes(self) -> dict:
"""Return the Awair Index alongside state attributes.
The Awair Index is a subjective score ranging from 0-4 (inclusive) that
is is used by the Awair app when displaying the relative "safety" of a
given measurement. Each value is mapped to a color indicating the safety:
class AwairData:
"""Get data from Awair API."""
0: green
1: yellow
2: light-orange
3: orange
4: red
def __init__(self, client, uuid, throttle):
"""Initialize the data object."""
self._client = client
self._uuid = uuid
self.data = {}
self.attrs = {}
self.async_update = Throttle(throttle)(self._async_update)
The API indicates that both positive and negative values may be returned,
but the negative values are mapped to identical colors as the positive values.
Knowing that, we just return the absolute value of a given index so that
users don't have to handle positive/negative values that ultimately "mean"
the same thing.
async def _async_update(self):
"""Get the data from Awair API."""
resp = await self._client.air_data_latest(self._uuid)
https://docs.developer.getawair.com/?version=latest#awair-score-and-index
"""
attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
if self._kind in self._air_data.indices:
attrs["awair_index"] = abs(self._air_data.indices[self._kind])
elif self._kind in DUST_ALIASES and API_DUST in self._air_data.indices:
attrs["awair_index"] = abs(self._air_data.indices.dust)
if not resp:
return
return attrs
timestamp = dt.parse_datetime(resp[0][ATTR_TIMESTAMP])
self.attrs[ATTR_LAST_API_UPDATE] = timestamp
self.data[ATTR_SCORE] = resp[0][ATTR_SCORE]
@property
def device_info(self) -> dict:
"""Device information."""
info = {
"identifiers": {(DOMAIN, self._device.uuid)},
"manufacturer": "Awair",
"model": self._device.model,
}
# The air_data_latest call only returns one item, so this should
# be safe to only process one entry.
for sensor in resp[0][ATTR_SENSORS]:
self.data[sensor[ATTR_COMPONENT]] = round(sensor[ATTR_VALUE], 1)
if self._device.name:
info["name"] = self._device.name
_LOGGER.debug("Got Awair Data for %s: %s", self._uuid, self.data)
if self._device.mac_address:
info["connections"] = {
(dr.CONNECTION_NETWORK_MAC, self._device.mac_address)
}
return info
async def async_added_to_hass(self) -> None:
"""Connect to dispatcher listening for entity data notifications."""
self.async_on_remove(
self._coordinator.async_add_listener(self.async_write_ha_state)
)
async def async_update(self) -> None:
"""Update Awair entity."""
await self._coordinator.async_request_refresh()
@property
def _air_data(self) -> Optional[AwairResult]:
"""Return the latest data for our device, or None."""
result: Optional[AwairResult] = self._coordinator.data.get(self._device.uuid)
if result:
return result.air_data
return None

View File

@@ -0,0 +1,29 @@
{
"config": {
"step": {
"user": {
"description": "You must register for an Awair developer access token at: https://developer.getawair.com/onboard/login",
"data": {
"access_token": "[%key:common::config_flow::data::access_token%]",
"email": "[%key:common::config_flow::data::email%]"
}
},
"reauth": {
"description": "Please re-enter your Awair developer access token.",
"data": {
"access_token": "[%key:common::config_flow::data::access_token%]",
"email": "[%key:common::config_flow::data::email%]"
}
}
},
"error": {
"auth": "[%key:common::config_flow::error::invalid_access_token%]",
"unknown": "Unknown Awair API error."
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"no_devices": "[%key:common::config_flow::abort::no_devices_found%]",
"reauth_successful": "[%key:common::config_flow::data::access_token%] updated successfully"
}
}
}

View File

@@ -0,0 +1,28 @@
{
"config": {
"abort": {
"already_configured": "El compte ja ha estat configurat",
"no_devices": "No s'han trobat dispositius a la xarxa",
"reauth_successful": "Token d'acc\u00e9s actualitzat correctament"
},
"error": {
"auth": "Token d'acc\u00e9s no v\u00e0lid",
"unknown": "Error desconegut de l'API Awair."
},
"step": {
"reauth": {
"data": {
"access_token": "Token d'acc\u00e9s",
"email": "Correu electr\u00f2nic"
},
"description": "Torna a introduir el token d'acc\u00e9s de desenvolupador d'Awair."
},
"user": {
"data": {
"access_token": "Token d'acc\u00e9s",
"email": "Correu electr\u00f2nic"
}
}
}
}
}

View File

@@ -0,0 +1,29 @@
{
"config": {
"abort": {
"already_configured": "Account is already configured",
"no_devices": "No devices found on the network",
"reauth_successful": "Access Token updated successfully"
},
"error": {
"auth": "Invalid access token",
"unknown": "Unknown Awair API error."
},
"step": {
"reauth": {
"data": {
"access_token": "Access Token",
"email": "Email"
},
"description": "Please re-enter your Awair developer access token."
},
"user": {
"data": {
"access_token": "Access Token",
"email": "Email"
},
"description": "You must register for an Awair developer access token at: https://developer.getawair.com/onboard/login"
}
}
}
}

View File

@@ -0,0 +1,29 @@
{
"config": {
"abort": {
"already_configured": "La cuenta ya ha sido configurada",
"no_devices": "No se encontraron dispositivos en la red",
"reauth_successful": "Token de acceso actualizado correctamente "
},
"error": {
"auth": "Token de acceso no v\u00e1lido",
"unknown": "Error desconocido en API Awair"
},
"step": {
"reauth": {
"data": {
"access_token": "Token de acceso",
"email": "Correo electr\u00f3nico"
},
"description": "Por favor, vuelve a introducir tu token de acceso de desarrollador Awair."
},
"user": {
"data": {
"access_token": "Token de acceso",
"email": "Correo electr\u00f3nico"
},
"description": "Debes registrarte para obtener un token de acceso de desarrollador Awair en: https://developer.getawair.com/onboard/login"
}
}
}
}

View File

@@ -0,0 +1,21 @@
{
"config": {
"error": {
"unknown": "Ukjent Awair API-feil."
},
"step": {
"reauth": {
"data": {
"email": "Epost"
},
"description": "Skriv inn tilgangstokenet for Awair-utviklere p\u00e5 nytt."
},
"user": {
"data": {
"email": "Epost "
},
"description": "Du m\u00e5 registrere deg for et Awair-utviklertilgangstoken p\u00e5: https://developer.getawair.com/onboard/login"
}
}
}
}

View File

@@ -0,0 +1,29 @@
{
"config": {
"abort": {
"already_configured": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430.",
"no_devices": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.",
"reauth_successful": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d."
},
"error": {
"auth": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430.",
"unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."
},
"step": {
"reauth": {
"data": {
"access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430",
"email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b"
},
"description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0412\u0430\u0448 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430."
},
"user": {
"data": {
"access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430",
"email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b"
},
"description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a Awair \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443: https://developer.getawair.com/onboard/login"
}
}
}
}

View File

@@ -0,0 +1,29 @@
{
"config": {
"abort": {
"already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"no_devices": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u8a2d\u5099",
"reauth_successful": "\u5b58\u53d6\u5bc6\u9470 \u5df2\u6210\u529f\u66f4\u65b0"
},
"error": {
"auth": "\u5b58\u53d6\u5bc6\u9470\u7121\u6548",
"unknown": "\u672a\u77e5 Awair API \u932f\u8aa4\u3002"
},
"step": {
"reauth": {
"data": {
"access_token": "\u5b58\u53d6\u5bc6\u9470",
"email": "\u96fb\u5b50\u90f5\u4ef6"
},
"description": "\u8acb\u91cd\u65b0\u8f38\u5165 Awair \u958b\u767c\u8005\u5b58\u53d6\u5bc6\u9470\u3002"
},
"user": {
"data": {
"access_token": "\u5b58\u53d6\u5bc6\u9470",
"email": "\u96fb\u5b50\u90f5\u4ef6"
},
"description": "\u5fc5\u9808\u5148\u8a3b\u518a Awair \u958b\u767c\u8005\u5b58\u53d6\u5bc6\u9470\uff1ahttps://developer.getawair.com/onboard/login"
}
}
}
}

View File

@@ -42,7 +42,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add binary sensor from Axis device."""
event = device.api.event[event_id]
if event.CLASS != CLASS_OUTPUT:
if event.CLASS != CLASS_OUTPUT and not (
event.CLASS == CLASS_LIGHT and event.TYPE == "Light"
):
async_add_entities([AxisBinarySensor(event, device)], True)
device.listeners.append(

View File

@@ -27,7 +27,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
device = hass.data[AXIS_DOMAIN][config_entry.unique_id]
if not device.option_camera:
if not device.api.vapix.params.image_format:
return
async_add_entities([AxisCamera(device)])

View File

@@ -3,6 +3,7 @@ import logging
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
LOGGER = logging.getLogger(__package__)
@@ -11,7 +12,6 @@ DOMAIN = "axis"
ATTR_MANUFACTURER = "Axis Communications AB"
CONF_CAMERA = "camera"
CONF_EVENTS = "events"
CONF_MODEL = "model"
CONF_STREAM_PROFILE = "stream_profile"
@@ -20,4 +20,4 @@ DEFAULT_EVENTS = True
DEFAULT_STREAM_PROFILE = "No stream profile"
DEFAULT_TRIGGER_TIME = 0
PLATFORMS = [BINARY_SENSOR_DOMAIN, CAMERA_DOMAIN, SWITCH_DOMAIN]
PLATFORMS = [BINARY_SENSOR_DOMAIN, CAMERA_DOMAIN, LIGHT_DOMAIN, SWITCH_DOMAIN]

View File

@@ -29,7 +29,6 @@ from homeassistant.setup import async_when_setup
from .const import (
ATTR_MANUFACTURER,
CONF_CAMERA,
CONF_EVENTS,
CONF_MODEL,
CONF_STREAM_PROFILE,
@@ -78,12 +77,6 @@ class AxisNetworkDevice:
"""Return the serial number of this device."""
return self.config_entry.unique_id
@property
def option_camera(self):
"""Config entry option defining if camera should be used."""
supported_formats = self.api.vapix.params.image_format
return self.config_entry.options.get(CONF_CAMERA, bool(supported_formats))
@property
def option_events(self):
"""Config entry option defining if platforms based on events should be created."""

View File

@@ -0,0 +1,116 @@
"""Support for Axis lights."""
from axis.event_stream import CLASS_LIGHT
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
SUPPORT_BRIGHTNESS,
LightEntity,
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .axis_base import AxisEventBase
from .const import DOMAIN as AXIS_DOMAIN
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up a Axis light."""
device = hass.data[AXIS_DOMAIN][config_entry.unique_id]
if not device.api.vapix.light_control:
return
@callback
def async_add_sensor(event_id):
"""Add light from Axis device."""
event = device.api.event[event_id]
if event.CLASS == CLASS_LIGHT and event.TYPE == "Light":
async_add_entities([AxisLight(event, device)], True)
device.listeners.append(
async_dispatcher_connect(hass, device.signal_new_event, async_add_sensor)
)
class AxisLight(AxisEventBase, LightEntity):
"""Representation of a light Axis event."""
def __init__(self, event, device):
"""Initialize the Axis light."""
super().__init__(event, device)
self.light_id = f"led{self.event.id}"
self.current_intensity = 0
self.max_intensity = 0
self._features = SUPPORT_BRIGHTNESS
async def async_added_to_hass(self) -> None:
"""Subscribe lights events."""
await super().async_added_to_hass()
def get_light_capabilities():
"""Get light capabilities."""
current_intensity = self.device.api.vapix.light_control.get_current_intensity(
self.light_id
)
self.current_intensity = current_intensity["data"]["intensity"]
max_intensity = self.device.api.vapix.light_control.get_valid_intensity(
self.light_id
)
self.max_intensity = max_intensity["data"]["ranges"][0]["high"]
await self.hass.async_add_executor_job(get_light_capabilities)
@property
def supported_features(self):
"""Flag supported features."""
return self._features
@property
def name(self):
"""Return the name of the light."""
light_type = self.device.api.vapix.light_control[self.light_id].light_type
return f"{self.device.name} {light_type} {self.event.TYPE} {self.event.id}"
@property
def is_on(self):
"""Return true if light is on."""
return self.event.is_tripped
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return int((self.current_intensity / self.max_intensity) * 255)
def turn_on(self, **kwargs):
"""Turn on light."""
if not self.is_on:
self.device.api.vapix.light_control.activate_light(self.light_id)
if ATTR_BRIGHTNESS in kwargs:
intensity = int((kwargs[ATTR_BRIGHTNESS] / 255) * self.max_intensity)
self.device.api.vapix.light_control.set_manual_intensity(
self.light_id, intensity
)
def turn_off(self, **kwargs):
"""Turn off light."""
if self.is_on:
self.device.api.vapix.light_control.deactivate_light(self.light_id)
def update(self):
"""Update brightness."""
current_intensity = self.device.api.vapix.light_control.get_current_intensity(
self.light_id
)
self.current_intensity = current_intensity["data"]["intensity"]
@property
def should_poll(self):
"""Brightness needs polling."""
return True

View File

@@ -3,7 +3,7 @@
"name": "Axis",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/axis",
"requirements": ["axis==30"],
"requirements": ["axis==33"],
"zeroconf": ["_axis-video._tcp.local."],
"after_dependencies": ["mqtt"],
"codeowners": ["@Kane610"]

View File

@@ -8,7 +8,7 @@
},
"error": {
"already_configured": "El dispositivo ya est\u00e1 configurado",
"already_in_progress": "El flujo de configuraci\u00f3n del dispositivo ya est\u00e1 en curso.",
"already_in_progress": "El flujo de configuraci\u00f3n del dispositivo ya est\u00e1 en marcha.",
"device_unavailable": "El dispositivo no est\u00e1 disponible",
"faulty_credentials": "Credenciales de usuario incorrectas"
},

View File

@@ -12,6 +12,7 @@ _LOGGER = logging.getLogger(__name__)
SUPPORTED_LANGUAGES = ["zh"]
DEFAULT_LANG = "zh"
SUPPORTED_PERSON = [0, 1, 3, 4, 5, 103, 106, 110, 111]
CONF_APP_ID = "app_id"
CONF_SECRET_KEY = "secret_key"
@@ -35,9 +36,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
vol.Optional(CONF_VOLUME, default=5): vol.All(
vol.Coerce(int), vol.Range(min=0, max=15)
),
vol.Optional(CONF_PERSON, default=0): vol.All(
vol.Coerce(int), vol.Range(min=0, max=4)
),
vol.Optional(CONF_PERSON, default=0): vol.In(SUPPORTED_PERSON),
}
)

View File

@@ -12,6 +12,7 @@
"step": {
"user": {
"data": {
"host": "Adresse IP",
"port": "Port"
},
"description": "Configurez votre BleBox pour l'int\u00e9grer \u00e0 Home Assistant.",

View File

@@ -13,6 +13,7 @@
"step": {
"user": {
"data": {
"host": "IP adresse",
"port": "Port"
},
"description": "Konfigurer BleBox-en til \u00e5 integreres med Home Assistant.",

View File

@@ -14,6 +14,7 @@ from homeassistant.const import (
CONF_SCAN_INTERVAL,
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from .const import (
@@ -58,7 +59,7 @@ def _blink_startup_wrapper(entry):
no_prompt=True,
device_id=DEVICE_ID,
)
blink.refresh_rate = entry.data[CONF_SCAN_INTERVAL]
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
try:
blink.login_response = entry.data["login_response"]
@@ -91,6 +92,8 @@ async def async_setup(hass, config):
async def async_setup_entry(hass, entry):
"""Set up Blink via config entry."""
_async_import_options_from_data_if_missing(hass, entry)
hass.data[DOMAIN][entry.entry_id] = await hass.async_add_executor_job(
_blink_startup_wrapper, entry
)
@@ -130,6 +133,16 @@ async def async_setup_entry(hass, entry):
return True
@callback
def _async_import_options_from_data_if_missing(hass, entry):
options = dict(entry.options)
if CONF_SCAN_INTERVAL not in entry.options:
options[CONF_SCAN_INTERVAL] = entry.data.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
)
hass.config_entries.async_update_entry(entry, options=options)
async def async_unload_entry(hass, entry):
"""Unload Blink entry."""
unload_ok = all(

View File

@@ -11,6 +11,7 @@ from homeassistant.const import (
CONF_SCAN_INTERVAL,
CONF_USERNAME,
)
from homeassistant.core import callback
from .const import DEFAULT_OFFSET, DEFAULT_SCAN_INTERVAL, DEVICE_ID, DOMAIN
@@ -40,10 +41,15 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self.data = {
CONF_USERNAME: "",
CONF_PASSWORD: "",
CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL,
"login_response": None,
}
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get options flow for this handler."""
return BlinkOptionsFlowHandler(config_entry)
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
errors = {}
@@ -54,7 +60,7 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(self.data[CONF_USERNAME])
if CONF_SCAN_INTERVAL in user_input:
self.data[CONF_SCAN_INTERVAL] = user_input["scan_interval"]
self.data[CONF_SCAN_INTERVAL] = user_input[CONF_SCAN_INTERVAL]
self.blink = Blink(
username=self.data[CONF_USERNAME],
@@ -107,6 +113,40 @@ class BlinkConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_user(import_data)
class BlinkOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle Blink options."""
def __init__(self, config_entry):
"""Initialize Blink options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)
self.blink = None
async def async_step_init(self, user_input=None):
"""Manage the Blink options."""
self.blink = self.hass.data[DOMAIN][self.config_entry.entry_id]
self.options[CONF_SCAN_INTERVAL] = self.blink.refresh_rate
return await self.async_step_simple_options()
async def async_step_simple_options(self, user_input=None):
"""For simple options."""
if user_input is not None:
self.options.update(user_input)
self.blink.refresh_rate = user_input[CONF_SCAN_INTERVAL]
return self.async_create_entry(title="", data=self.options)
options = self.config_entry.options
scan_interval = options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
return self.async_show_form(
step_id="simple_options",
data_schema=vol.Schema(
{vol.Optional(CONF_SCAN_INTERVAL, default=scan_interval,): int}
),
)
class Require2FA(exceptions.HomeAssistantError):
"""Error to indicate we require 2FA."""

View File

@@ -21,5 +21,16 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
},
"options": {
"step": {
"simple_options": {
"data": {
"scan_interval": "Scan Interval (seconds)"
},
"title": "Blink options",
"description": "Configure Blink integration"
}
}
}
}

View File

@@ -29,7 +29,9 @@
"simple_options": {
"data": {
"scan_interval": "Interval d'escaneig (segons)"
}
},
"description": "Configura la integraci\u00f3 Blink",
"title": "Opcions de Blink"
}
}
}

View File

@@ -1,5 +1,8 @@
{
"config": {
"abort": {
"already_configured": "P\u00e9riph\u00e9rique d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"invalid_auth": "Authentification invalide",
"unknown": "Erreur inattendue"
@@ -20,5 +23,15 @@
"title": "Connectez-vous avec un compte Blink"
}
}
},
"options": {
"step": {
"simple_options": {
"data": {
"scan_interval": "Intervalle de balayage (secondes)"
},
"title": "Options de clignotement"
}
}
}
}

View File

@@ -23,5 +23,16 @@
"title": "Mam Blink Kont verbannen"
}
}
},
"options": {
"step": {
"simple_options": {
"data": {
"scan_interval": "Scan Intervall (sekonnen)"
},
"description": "Blink Integratioun ariichten",
"title": "Blink Optiounen"
}
}
}
}

View File

@@ -41,6 +41,7 @@ _SERVICE_MAP = {
"light_flash": "trigger_remote_light_flash",
"sound_horn": "trigger_remote_horn",
"activate_air_conditioning": "trigger_remote_air_conditioning",
"find_vehicle": "trigger_remote_vehicle_finder",
}

View File

@@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.7.5"],
"requirements": ["bimmer_connected==0.7.7"],
"dependencies": [],
"codeowners": ["@gerard33"]
"codeowners": ["@gerard33", "@rikroe"]
}

View File

@@ -35,6 +35,16 @@ activate_air_conditioning:
The vehicle identification number (VIN) of the vehicle, 17 characters
example: WBANXXXXXX1234567
find_vehicle:
description: >
Request vehicle to update the gps location. The vehicle is identified via the vin
(see below).
fields:
vin:
description: >
The vehicle identification number (VIN) of the vehicle, 17 characters
example: WBANXXXXXX1234567
update_state:
description: >
Fetch the last state of the vehicles of all your accounts from the BMW

View File

@@ -19,7 +19,7 @@
},
"user": {
"data": {
"host": "TV-vertsnavn eller IP-adresse"
"host": "Vert"
},
"description": "Sett opp Sony Bravia TV-integrasjon. Hvis du har problemer med konfigurasjonen, g\u00e5 til: [https://www.home-assistant.io/integrations/braviatv](https://www.home-assistant.io/integrations/braviatv)\n\n Forsikre deg om at TV-en er sl\u00e5tt p\u00e5.",
"title": ""

View File

@@ -2,7 +2,6 @@
import asyncio
from base64 import b64decode, b64encode
from binascii import unhexlify
from datetime import timedelta
import logging
import re
@@ -13,7 +12,7 @@ from homeassistant.const import CONF_HOST
import homeassistant.helpers.config_validation as cv
from homeassistant.util.dt import utcnow
from .const import CONF_PACKET, DOMAIN, SERVICE_LEARN, SERVICE_SEND
from .const import CONF_PACKET, DOMAIN, LEARNING_TIMEOUT, SERVICE_LEARN, SERVICE_SEND
_LOGGER = logging.getLogger(__name__)
@@ -84,7 +83,7 @@ async def async_setup_service(hass, host, device):
_LOGGER.info("Press the key you want Home Assistant to learn")
start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=20):
while (utcnow() - start_time) < LEARNING_TIMEOUT:
await asyncio.sleep(1)
try:
packet = await device.async_request(device.api.check_data)

View File

@@ -1,7 +1,8 @@
"""Constants for broadlink platform."""
from datetime import timedelta
CONF_PACKET = "packet"
DEFAULT_LEARNING_TIMEOUT = 20
DEFAULT_NAME = "Broadlink"
DEFAULT_PORT = 80
DEFAULT_RETRY = 3
@@ -9,6 +10,8 @@ DEFAULT_TIMEOUT = 5
DOMAIN = "broadlink"
LEARNING_TIMEOUT = timedelta(seconds=30)
SERVICE_LEARN = "learn"
SERVICE_SEND = "send"

View File

@@ -24,7 +24,6 @@ from homeassistant.components.remote import (
ATTR_DELAY_SECS,
ATTR_DEVICE,
ATTR_NUM_REPEATS,
ATTR_TIMEOUT,
DEFAULT_DELAY_SECS,
DOMAIN as COMPONENT,
PLATFORM_SCHEMA,
@@ -40,10 +39,10 @@ from homeassistant.util.dt import utcnow
from . import DOMAIN, data_packet, hostname, mac_address
from .const import (
DEFAULT_LEARNING_TIMEOUT,
DEFAULT_NAME,
DEFAULT_PORT,
DEFAULT_TIMEOUT,
LEARNING_TIMEOUT,
RM4_TYPES,
RM_TYPES,
)
@@ -74,10 +73,7 @@ SERVICE_SEND_SCHEMA = MINIMUM_SERVICE_SCHEMA.extend(
)
SERVICE_LEARN_SCHEMA = MINIMUM_SERVICE_SCHEMA.extend(
{
vol.Optional(ATTR_ALTERNATIVE, default=False): cv.boolean,
vol.Optional(ATTR_TIMEOUT, default=DEFAULT_LEARNING_TIMEOUT): cv.positive_int,
}
{vol.Optional(ATTR_ALTERNATIVE, default=False): cv.boolean}
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@@ -267,7 +263,6 @@ class BroadlinkRemote(RemoteEntity):
commands = kwargs[ATTR_COMMAND]
device = kwargs[ATTR_DEVICE]
toggle = kwargs[ATTR_ALTERNATIVE]
timeout = kwargs[ATTR_TIMEOUT]
if not self._state:
return
@@ -275,66 +270,47 @@ class BroadlinkRemote(RemoteEntity):
should_store = False
for command in commands:
try:
should_store |= await self._async_learn_code(
command, device, toggle, timeout
)
except (AuthorizationError, DeviceOfflineError):
code = await self._async_learn_command(command)
if toggle:
code = [code, await self._async_learn_command(command)]
except (AuthorizationError, DeviceOfflineError) as err_msg:
_LOGGER.error("Failed to learn '%s': %s", command, err_msg)
break
except BroadlinkException:
pass
except (BroadlinkException, TimeoutError) as err_msg:
_LOGGER.error("Failed to learn '%s': %s", command, err_msg)
continue
else:
self._codes.setdefault(device, {}).update({command: code})
should_store = True
if should_store:
await self._code_storage.async_save(self._codes)
async def _async_learn_code(self, command, device, toggle, timeout):
"""Learn a code from a remote.
Capture an additional code for toggle commands.
"""
async def _async_learn_command(self, command):
"""Learn a command from a remote."""
try:
if not toggle:
code = await self._async_capture_code(command, timeout)
else:
code = [
await self._async_capture_code(command, timeout),
await self._async_capture_code(command, timeout),
]
except TimeoutError:
_LOGGER.error("Failed to learn '%s/%s': No code received", command, device)
return False
await self.device.async_request(self.device.api.enter_learning)
except BroadlinkException as err_msg:
_LOGGER.error("Failed to learn '%s/%s': %s", command, device, err_msg)
_LOGGER.debug("Failed to enter learning mode: %s", err_msg)
raise
self._codes.setdefault(device, {}).update({command: code})
return True
async def _async_capture_code(self, command, timeout):
"""Enter learning mode and capture a code from a remote."""
await self.device.async_request(self.device.api.enter_learning)
self.hass.components.persistent_notification.async_create(
f"Press the '{command}' button.",
title="Learn command",
notification_id="learn_command",
)
code = None
start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=timeout):
await asyncio.sleep(1)
try:
code = await self.device.async_request(self.device.api.check_data)
except (ReadError, StorageError):
continue
else:
break
self.hass.components.persistent_notification.async_dismiss(
notification_id="learn_command"
)
if code is None:
raise TimeoutError
return b64encode(code).decode("utf8")
try:
start_time = utcnow()
while (utcnow() - start_time) < LEARNING_TIMEOUT:
await asyncio.sleep(1)
try:
code = await self.device.async_request(self.device.api.check_data)
except (ReadError, StorageError):
continue
return b64encode(code).decode("utf8")
raise TimeoutError("No code received")
finally:
self.hass.components.persistent_notification.async_dismiss(
notification_id="learn_command"
)

View File

@@ -69,16 +69,18 @@ class BrotherConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)
async def async_step_zeroconf(self, user_input=None):
async def async_step_zeroconf(self, discovery_info):
"""Handle zeroconf discovery."""
if user_input is None:
if discovery_info is None:
return self.async_abort(reason="connection_error")
if not user_input.get("name") or not user_input["name"].startswith("Brother"):
if not discovery_info.get("name") or not discovery_info["name"].startswith(
"Brother"
):
return self.async_abort(reason="not_brother_printer")
# Hostname is format: brother.local.
self.host = user_input["hostname"].rstrip(".")
self.host = discovery_info["hostname"].rstrip(".")
self.brother = Brother(self.host)
try:

View File

@@ -13,7 +13,7 @@
"step": {
"user": {
"data": {
"host": "Vertsnavn eller IP-adresse til skriveren",
"host": "Vert",
"type": "Skriver type"
},
"description": "Sett opp Brother skriver integrasjonen. Hvis du har problemer med konfigurasjonen, bes\u00f8k dokumentasjonen her: [https://www.home-assistant.io/integrations/brother](https://www.home-assistant.io/integrations/brother)"

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