Compare commits

...

571 Commits

Author SHA1 Message Date
Bram Kragten 66e045f570 Bumped version to 2021.2.0b3 2021-02-02 22:57:18 +01:00
Bram Kragten 0435586bb1 Update frontend to 20210127.7 (#45874) 2021-02-02 22:54:12 +01:00
Martin Hjelmare 76c27a7d5a Use new zwave_js client (#45872)
* Use new zwave_js client

* Remove client callbacks

* Clean up on connect and on disconnect

* Clean log

* Add stop listen to unsubscribe callbacks

* Fix most tests

* Adapt to new listen interface

* Fix most tests

* Remove stale connection state feature

* Bump zwave-js-server-python to 0.16.0

* Clean up disconnect
2021-02-02 22:54:11 +01:00
Marcel van der Veldt d759fa3407 Guard for missing value (#45867)
* guard for missing value

* update comment
2021-02-02 22:54:10 +01:00
Martin Hjelmare 34bc5ef0cd Fix zwave_js device remove test (#45864) 2021-02-02 22:54:10 +01:00
Martin Hjelmare f4ff2708e0 Fix zwave_js sensor device class attribute error (#45863) 2021-02-02 22:54:09 +01:00
Marcel van der Veldt d3a92dcac6 Update zwave_js discovery schema for light platform (#45861) 2021-02-02 22:54:09 +01:00
Raman Gupta b575b0a700 Add current humidity to zwave_js climate platform (#45857) 2021-02-02 22:54:08 +01:00
Raman Gupta 7ff60601c9 Remove zwave_js devices that the controller is no longer connected to on initialization (#45853)
* Remove zwave_js devices that the controller is no longer connected to on initialization

* remove extra line break

* fix test

* Clean up

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

* Lint

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-02-02 22:54:07 +01:00
Thomas Friedel bba7c0454c Enable Osramlightify again (#45849) 2021-02-02 22:54:07 +01:00
Franck Nijhof 277aa01088 Disable Osramlightify, upstream package is missing (#45775) 2021-02-02 22:54:06 +01:00
Marcel van der Veldt 67ee2fb822 Fix sensor discovery for zwave_js integration (#45834)
Co-authored-by: Raman Gupta <7243222+raman325@users.noreply.github.com>
2021-02-02 22:47:29 +01:00
Raman Gupta a56b250e31 Add notification events to zwave_js integration (#45827)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-02-02 22:47:28 +01:00
Marcel van der Veldt e169ad93c2 Add value notification events to zwave_js integration (#45814) 2021-02-02 22:47:27 +01:00
Erik Montnemery fba781a535 Improve MQTT JSON light to allow non-ambiguous states (#45522) 2021-02-02 22:47:27 +01:00
Paulus Schoutsen 88a5ff4a51 Bumped version to 2021.2.0b2 2021-02-01 22:32:23 +00:00
Joakim Sørensen cd1c8b78a1 Bump awesomeversion from 21.1.6 to 21.2.0 (#45821) 2021-02-01 22:31:07 +00:00
Martin Hjelmare b705468b57 Bump zwave-js-server-python to 0.15.0 (#45813) 2021-02-01 22:31:06 +00:00
J. Nick Koston 938e5ff435 Fix missing async for lutron_caseta timeout (#45812) 2021-02-01 22:31:05 +00:00
Raman Gupta 000def5ded Add zwave_js binary sensors property name for Notification CC (#45810) 2021-02-01 22:31:04 +00:00
Raman Gupta 760b75a5c1 Search all endpoints for value in zwave_js (#45809)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-02-01 22:31:04 +00:00
J. Nick Koston bf819df388 Fix shutdown deadlock with run_callback_threadsafe (#45807) 2021-02-01 22:31:03 +00:00
Chris 493f3bc1ce Add stop_cover service for zwave_js (#45805) 2021-02-01 22:31:02 +00:00
Marcel van der Veldt 18df06d6a6 Bump zwave-js-server-python to 0.14.2 (#45800) 2021-02-01 22:31:01 +00:00
Pierre Ståhl 0df1f8bfbd Bump pyatv to 0.7.6 (#45799) 2021-02-01 22:31:00 +00:00
Matthias Alphart 4f73105896 Prevent AttributError for uninitilized KNX ClimateMode (#45793) 2021-02-01 22:31:00 +00:00
J. Nick Koston 53db89e13d Ensure lutron_caseta is only discovered once (#45792) 2021-02-01 22:30:59 +00:00
Erik Montnemery 078579de69 Bump pychromecast to 8.0.0 (#45776) 2021-02-01 22:30:58 +00:00
J. Nick Koston bbb18ffec4 Add timeout to lutron_caseta to prevent it blocking startup (#45769) 2021-02-01 22:30:57 +00:00
Bram Kragten a8ad51ceb2 Update frontend to 20210127.6 (#45760) 2021-02-01 22:30:57 +00:00
Joakim Sørensen 68ea62f5ef Bump awesomeversion from 21.1.3 to 21.1.6 (#45738) 2021-02-01 22:30:56 +00:00
J. Nick Koston a81a4ad44b Fix exception when a unifi config entry is ignored (#45735)
* Fix exception when a unifi config entry is ignored

* Fix existing test
2021-02-01 22:30:55 +00:00
Aaron Bach 4a2ad442c7 Bump simplisafe-python to 9.6.4 (#45716)
* Bump simplisafe-python to 9.6.4

* Fix imports
2021-02-01 22:30:55 +00:00
Bram Kragten 498f8db0d8 Updated frontend to 20210127.5 (#45714) 2021-02-01 22:30:54 +00:00
Martin Hjelmare 71b67ba572 Fix mqtt check in ozw (#45709) 2021-02-01 22:30:53 +00:00
bsmappee 7a746adb04 Bump pysmappee to 0.2.16 (#45699) 2021-02-01 22:30:53 +00:00
Pascal Vizeli 869bc2c4ce Update docker base image 2021.01.1 (#45697) 2021-02-01 22:30:52 +00:00
Vladimír Záhradník b072a6c91a SSDP response decode: replace invalid utf-8 characters (#42681)
* SSDP response decode: replace invalid utf-8 characters

* Add test to validate replaced data

Co-authored-by: Joakim Plate <elupus@ecce.se>
2021-02-01 22:30:51 +00:00
Paulus Schoutsen 6070c7c83a Bumped version to 2021.2.0b1 2021-01-29 09:13:06 +01:00
Bram Kragten af0ca31d77 Update frontend to 20210127.3 (#45679) 2021-01-29 09:12:41 +01:00
Marcel van der Veldt 80e176aab3 Fix removing nodes in zwave_js integration (#45676) 2021-01-29 09:12:40 +01:00
J. Nick Koston d1b677d0f9 Update httpcore to prevent unhandled exception on dropped connection (#45667)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-29 09:12:39 +01:00
J. Nick Koston 9e5bf6b9f6 Ensure history LazyState state value is always a string (#45644)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-29 09:12:39 +01:00
Erik Montnemery 385bdc4058 Bump hatasmota to 0.2.7 (#45625) 2021-01-29 09:12:38 +01:00
Fabian Affolter 4dbd2f2a6b Upgrade pyyaml to 5.4.1 (CVE-2020-14343) (#45624) 2021-01-29 09:12:37 +01:00
Paulus Schoutsen 800c7f84ff Include relative path in tts get url (#45623)
* Include relative path in tts get url

* Always cal get_url when requested
2021-01-29 09:12:36 +01:00
Marc Mueller 40b3ed1419 Add additional error handling for automation script run (#45613) 2021-01-29 09:12:35 +01:00
jjlawren a05f839d3d Allow Plex playback using provided playqueue ID (#45580) 2021-01-29 09:12:34 +01:00
Franck Nijhof d3daf02ef3 Bumped version to 2021.2.0b0 2021-01-27 20:45:52 +01:00
Aaron Bach b420c1ceb7 Fix Notion binary sensor bug due to API changes (#45616) 2021-01-27 20:41:03 +01:00
Marc Mueller 6aadf14bdd Add mobile_app notify ClientError handling (#45480) 2021-01-27 20:39:43 +01:00
Bram Kragten 73a04e653f Update frontend to 20210127.1 (#45612) 2021-01-27 19:32:44 +01:00
starkillerOG ef3bdd1afc Bump motionblinds to 0.4.8 (#45609) 2021-01-27 18:01:00 +01:00
J. Nick Koston a1662b3bb9 Restore the device id after deleting and re-adding an integration (#45348) 2021-01-27 17:10:57 +01:00
springstan 566058f701 Add config flow to fritzbox_callmonitor (#40736) 2021-01-27 16:53:45 +01:00
Tobias Sauerwein f14c4412b7 Migrate RMV to use httpx (#42560) 2021-01-27 15:57:58 +01:00
Dennis Schroer 8d572af77a Add Huisbaasje integration (#42716)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2021-01-27 15:53:25 +01:00
joe248 b4af17e02d Add service to move camera to PTZ preset (#43083) 2021-01-27 15:43:36 +01:00
Marc Mueller fb39185420 Add schema error handling to websocket_api (#45602)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-27 15:20:22 +01:00
linebp 211ef60d96 Convert media_player unittest tests to pytest style (#41950)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-27 14:50:01 +01:00
Shay Levy 5bc4db5ef9 Fix exception when last Shelly device is removed (#45476) 2021-01-27 08:15:56 -05:00
Robert Chmielowiec 80c2efa9f2 Add total energy, preheater and RMOT sensors to comfoconnect (#45373) 2021-01-27 08:10:58 -05:00
Shulyaka eea50c8ccc Add device actions for Number (#44240) 2021-01-27 13:40:13 +01:00
Maciej Bieniek 5d955eb039 Change update interval tests in AccuWeather integration (#45562) 2021-01-27 13:28:29 +01:00
Marcel van der Veldt 32c6509d55 Remove unwanted brightness check in zwave_js light (#45597) 2021-01-27 13:07:02 +01:00
Thomas Hollstegge 78b057ce02 Make sure generated slugs are not empty (#43153) 2021-01-27 12:25:49 +01:00
CtrlZvi 1433cdaa12 Prefer shorter keys for intent matching (#43672)
When using fuzzy matching to match entity names for intents, whichever
entity is first is preferred in the case of equal matches. This leads
to situations where entities with similar names (such as entities named
for their area and then specific area location) may be used when the
whole area is wanted.

I ran into this with the my Phillips Hue lights. I have each individual
light named such that its room is the first part of the name, and its
location within the room after. So my living room has:
Living Room West
Living Room Northwest
Living Room North
Living Room Northeast

I then have a group for the whole room:
Living Room

Because the group is the last of the entities, trying to adjust the
whole room only activates one light, because all of the lights match
equally well.

By preferring the shortest of equal matches, we prefer keys that have
the least amount of extra information, causing "Living Room" to match
the group instead of an individual light.
2021-01-27 12:16:19 +01:00
Aaron Bach 3841f0e42d Re-architect Tile integration with new pytile (#43071) 2021-01-27 12:06:09 +01:00
Franck Nijhof c8ad06e58a Merge branch 'master' into dev 2021-01-27 12:00:43 +01:00
Lars Marowsky-Brée 34194da1b6 New methods for input_select component (#42968)
This adds a `cycle` attribute to select_previous/next, and
select_first and select_last services.

This is quite useful for streamlining using input_select via
automations, such as when they represent a list of states to step
through; if the first option is the dimmest and the last the brightest,
one may not want to accidentally cycle from the first to the last, for
example.

Similarly, being able to directly select the first or last removes
adjustment in related automations.
2021-01-27 11:17:59 +01:00
Martin Hjelmare 122a4e03f8 Bump zwave-js-server-python to 0.14.1 (#45598) 2021-01-27 11:17:47 +01:00
Josias Montag fb2db34334 Fix home connect lights setting color (#45470) 2021-01-27 11:02:32 +01:00
Ryan Fleming 459236fcdd Camera Status and Motion record status (#44936) 2021-01-27 10:50:44 +01:00
Jim Ekman 67b309394f Esphome fan direction (#44495) 2021-01-27 10:40:33 +01:00
Adrian Suwała e12e2377af Rewrite hddtemp unittest tests to pytest (#42513) 2021-01-27 10:24:04 +01:00
Franck Nijhof 26e266181d Explicitly return None in Velux covers (#45596) 2021-01-27 10:17:26 +01:00
Berni Moses 491e66793e Add check and remove temporary fix for lg soundbar eq and source list (#38798) 2021-01-27 10:17:04 +01:00
Felipe Martins Diel c225f4b4ea Implement remote.delete_command in the Broadlink integration (#44041) 2021-01-27 10:14:11 +01:00
Yannik Ache Eicher d99118f6ba Optimized state handling of Panasonic Viera TVs (#42913) 2021-01-27 10:07:33 +01:00
Dermot Duffy 890eaf840c Add advanced Hyperion entities (#45410)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-27 09:35:13 +01:00
Thomas Lovén 06ade6129c Add selectors for text and arbitrary objects (#45112)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-27 09:20:19 +01:00
Philip Allgaier 6800f4b6fd Add temperature step size for generic_thermostat (#41972) 2021-01-27 09:12:24 +01:00
Martin Hjelmare f387e833c3 Add zwave_js add-on config flow support (#45552) 2021-01-27 08:56:16 +01:00
Marc Mueller c805baf88c Refactor slack tests (#45561)
* Refactor slack tests

* Changes

* Let service register for tests
2021-01-27 04:32:14 +01:00
J. Nick Koston 25f411ef6e Add support for Pico and Shade remotes to Lutron Caseta (#45315) 2021-01-26 16:32:08 -06:00
Matej Drobnič b533b91b10 Add reminder support to todoist.new_task service (#44142)
* add reminder support to todoist.new_task service

* fix time support on reminder_date

* fix time support on todoist due_date
2021-01-26 23:26:29 +01:00
Chris Talkington 14785660b0 Avoid proxied Roku images during internal requests (#43547) 2021-01-26 23:21:25 +01:00
Paul Daumlechner fb527814f9 Add tilt functionality to Velux covers (#43669) 2021-01-26 23:19:08 +01:00
Yuval Aboulafia 73d2ae76a9 Round Mold Indicator attributes (#43622) 2021-01-26 23:14:53 +01:00
Sami Heino ee8d88e85c Add support for EDS0068 onewire sensor (#44029)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2021-01-26 23:07:04 +01:00
ollo69 08e7247201 Remove tuya/climate precision property override (#44148) 2021-01-26 22:49:42 +01:00
jpcornil-git 74a44e03fa Extend add_update_listener support to bound methods (#44238) 2021-01-26 22:19:10 +01:00
guillempages 67fcdc5a9c Do not reset ble tracker adapter to read battery (#44229)
Do not reset the adapter to read the battery status, to avoid issues
in devices installations without sudo
2021-01-26 15:17:46 -06:00
Paulus Schoutsen 352d0870e3 Light significant changes + sensor tweaks (#45583) 2021-01-26 22:11:06 +01:00
Dror Eiger 74efe78d0a Add device metadata for Google Assistant (#45507)
* Add device metadata for Google Assistant

* Increase test coverage

* Refactor fetching the device and entity entries.
2021-01-26 21:59:43 +01:00
Paulus Schoutsen 712a5a098d Add significant change filtering to Google (#45566) 2021-01-26 21:45:09 +01:00
Dermot Duffy 568962552b Remove hyperion entry from registry only when instances are fully removed (#44488) 2021-01-26 21:39:02 +01:00
Robert Svensson 7a39a86eb9 Add gen3 Hue remote to deCONZ device trigger (#45581) 2021-01-26 21:36:12 +01:00
Jc2k 3b0a440770 Add support for homekit_controller secondary entities like power usage (#44013) 2021-01-26 13:45:01 -06:00
Franck Nijhof f53a83e084 Tweak stale policies (#45568)
* Tweak stale policies

* Remove stale comments
2021-01-26 19:35:53 +01:00
Franck Nijhof 7816eccee7 Upgrade coverage to 5.4 (#45570) 2021-01-26 19:20:42 +01:00
Franck Nijhof 21d23a6f94 Upgrade pytest to 6.2.2 (#45572) 2021-01-26 19:20:28 +01:00
J. Nick Koston 56253a6245 Bump httplib2 in google and remember_the_milk (#45578)
Fixes CVE-2020-11078
2021-01-26 18:51:20 +01:00
kpine daddb76e1d Fix zwave_js Color Switch Device Type discovery for RGB dimmers (#45571) 2021-01-26 18:39:56 +01:00
Franck Nijhof 52c56fdac7 Upgrade pillow to 8.1.0 (#45574) 2021-01-26 18:12:02 +01:00
Alexei Chetroi 9ae4818a23 Update ZHA dependencies (#45569) 2021-01-26 11:54:05 -05:00
jjlawren 76be67fa64 Bump plexapi to 4.3.0 (#45567) 2021-01-26 17:25:45 +01:00
Robert Svensson 26764a805b Don't parse previous messages when UniFi connection state change to available (#45544)
* Don't parse previous messages when connection state change to available

* Disable pylint for arguments-differ W0221 message
2021-01-26 16:41:28 +01:00
radovanbauer baab9b9a81 Added command templates for the mqtt climate component. (#44976)
This allows integrating with devices which require more complex payloads to be posted when updating their values.

Old feature request: https://github.com/home-assistant/core/issues/11496
There are numerous posts requesting this feature, example: https://community.home-assistant.io/t/need-help-with-value-template-for-mqtt-hvac/73395/68https://community.home-assistant.io/t/need-help-with-value-template-for-mqtt-hvac/73395/68

Command templates have been added for the following:
- fan_mode
- hold
- mode
- swing_mode
- temperature
- temperature high/low

This doesn't add templates for aux, away mode, power since these already accept custom payload_on/off (although they all share the same payload). It should be straightforward to add templates for them as well if needed.
2021-01-26 16:12:33 +01:00
Joakim Sørensen b1c2cde40b Changes to filename and path validation (#45529)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-26 15:53:21 +01:00
Paulus Schoutsen 4739e8a207 Limit data sending to Alexa to significant changes (#45563) 2021-01-26 15:48:35 +01:00
Fabian Affolter 893406c834 Upgrade shodan to 1.25.0 (#45560) 2021-01-26 15:44:51 +01:00
Paulus Schoutsen d082be787f Add "significant change" base (#45555) 2021-01-26 14:13:27 +01:00
Maikel Punie 38361b134a Bump python-velbus version to 2.1.2 (#45553) 2021-01-26 12:20:03 +01:00
popboxgun ab710f2154 Add support for Discord embed in messages (#44986)
Co-authored-by: Joakim Sørensen <hi@ludeeus.dev>
2021-01-26 11:39:48 +01:00
starkillerOG c16fd0a1ac Set hyperion icon to lightbulb when off (#45351)
Co-authored-by: Dermot Duffy <dermot.duffy@gmail.com>
2021-01-26 10:46:54 +01:00
starkillerOG 3647d549b0 Fix Hyperion brightness setting (#45335) 2021-01-26 10:45:41 +01:00
Paulus Schoutsen b9a525a9a7 Add an HTTP view to dump the Z-Wave JS state (#45452)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-26 10:27:20 +01:00
William Scanlon 260d9f8e16 Upgrade econet to use new API (#44427)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-26 09:18:20 +01:00
north3221 f2a8ccdbae Add tado service set temperature offset (#45014) 2021-01-26 09:11:29 +01:00
Robert Svensson 622906965d Add better message to deCONZ event logbook when information is available (#45545)
* Add better message when information is available

* Fix pylint
2021-01-26 08:15:39 +01:00
Greg Dowling d110d42913 Fix Vera race condition on start (#45535) 2021-01-26 08:15:07 +01:00
Marc Mueller ad677b9d41 Improve Slack notify component (#45479)
* Add typing information

* Small improvments

* Use %r for exceptions
* Added exception handlers for aiohttp.ClientError
* Added testcase

* Changes after review

* Bugfixes
2021-01-25 17:03:12 -07:00
Tobias Sauerwein ec47df4880 Clean up unnecessary method from Netatmo code (#45546) 2021-01-26 01:02:48 +01:00
Franck Nijhof 25469f1a07 Increase stalebot operations per run (#45548) 2021-01-26 00:29:32 +01:00
Marcel van der Veldt 11009b99bd Improve zwave_js light targetvalue vs currentValue selection (#45477) 2021-01-25 22:09:12 +01:00
Daniel Hjelseth Høyer 094844f834 Tibber, fix spreading requests (#45533)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-01-25 17:48:42 +01:00
Greg Dowling 3de2c900f3 Fix Roon play_media (#45532) 2021-01-25 17:44:18 +01:00
Ian Tewksbury 666a94a8e1 Add zwave_js ZWavePropertyBinarySensory (#45504) 2021-01-25 15:50:21 +01:00
Leonardo Figueiro d174c8265e Add WiLight Fan (#39541)
* Add WiLight Fan

Add fan to WiLigt integration

* Updated fan.py and test_fan.py

* Creating new fan test

* Update homeassistant/components/wilight/__init__.py

OK!
Done!

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

* Update homeassistant/components/wilight/fan.py

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

* Update homeassistant/components/wilight/fan.py

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

* Update homeassistant/components/wilight/fan.py

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

* As MartinHjelmare requested

* Update fan.py

* Update tests/components/wilight/test_fan.py

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

* Update tests/components/wilight/test_fan.py

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

* Update test_fan.py

As Martin Hjelmare suggested

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-25 14:03:11 +01:00
Joakim Sørensen edfb8c3423 Add version to hassfest for custom integrations (#45523)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-25 13:31:14 +01:00
Xavier Decuyper 1c0a74f18a Fix Tado fallback modes (overlay) (#44665) 2021-01-25 13:23:01 +01:00
SukramJ 104107dd95 Bump dependency for HomematicIP Cloud to 0.13.1 (#45475) 2021-01-25 13:19:11 +01:00
Paulus Schoutsen bf8d17f1b5 Better wait for Z-Wave to be initialized (#45520) 2021-01-25 13:12:58 +01:00
Paulus Schoutsen a7e8c62204 Bump Z-Wave JS Server Python to 0.13 (#45524) 2021-01-25 12:31:14 +01:00
dependabot[bot] c61331e8c5 Bump actions/stale from v3.0.14 to v3.0.15 (#45516)
Bumps [actions/stale](https://github.com/actions/stale) from v3.0.14 to v3.0.15.
- [Release notes](https://github.com/actions/stale/releases)
- [Commits](https://github.com/actions/stale/compare/v3.0.14...86561461b92875de77a8b2d2e75f004c826e8f45)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-25 09:39:06 +01:00
On Freund 47e34bb129 Use image proxy in volumio media browser (#43819)
* Use image proxy in volumio media browser

* Add thumbnail cache

* Clear thumbnail cache on browse

* Use built-in hash instead of hashlib
2021-01-24 18:16:10 -06:00
Matthias Alphart 616328c7c4 Update xknx to 0.16.2 (#45511) 2021-01-25 00:51:38 +01:00
Vinny Furia 34f701a69b Fix for radiotherm component stall (#45482) 2021-01-24 17:01:41 -06:00
Eric Severance 12e31b9571 Bump pyWeMo to 0.6.1 (#45501) 2021-01-24 19:16:12 +01:00
Xiaonan Shen dbaca51bb3 Rewrite dyson climate tests (#45493)
* Rewrite dyson climate tests

* Cleanup
2021-01-24 17:37:48 +01:00
Robert Svensson b69d9860b6 Improve Axis tests following late review in SSDP PR (#45489) 2021-01-24 14:14:49 +01:00
Josef Schlehofer 4bb6911b76 Upgrade youtube_dl to version 2021.01.16 (#45484) 2021-01-24 10:16:46 +01:00
J. Nick Koston 47c0adb312 Remove YAML support from tado (#45384) 2021-01-24 01:33:30 +01:00
Raman Gupta c2900ff888 Add support for zwave_js lock services (#45451) 2021-01-23 18:21:31 -05:00
Robert Svensson 1e7e5220a3 Add SSDP discovery support to Axis integration (#45474)
* Add SSDP discovery support to Axis integration

* Remove unnecessary f-string

* Remove the last of the f-string...
2021-01-23 23:51:49 +01:00
Xiaonan Shen 59b0a4d060 Rewrite dyson air quality tests (#45466) 2021-01-23 14:54:58 -05:00
Erik Montnemery 0e068a5f39 Truncate MQTT payload in debug messages to 8kB (#45465) 2021-01-23 14:52:43 -05:00
Paulus Schoutsen eb339b9793 Fix flaky SRP_Energy test (#45471) 2021-01-23 19:53:45 +01:00
Paulus Schoutsen 6715eae3d7 Merge pull request #45472 from home-assistant/rc 2021-01-23 19:08:21 +01:00
Paulus Schoutsen db7c260ffb Bumped version to 2021.1.5 2021-01-23 18:37:07 +01:00
Paulus Schoutsen 7abdad4a99 Avoid misuse sanitize_path, clarify docs (#45469) 2021-01-23 18:37:01 +01:00
Tobias Sauerwein f7df00bbbd Bump pyatmo to v4.2.2 (#45386) 2021-01-23 18:37:00 +01:00
Santobert 09f5c7f4c5 Bump pybotvac to 0.0.20 (#45367) 2021-01-23 18:36:59 +01:00
Martin Weinelt 3b71ac2ec9 Update python-mpd2 to 3.0.3 (#45141)
Fixes: #44931
2021-01-23 18:36:58 +01:00
Paulus Schoutsen 0930aae208 Avoid misuse sanitize_path, clarify docs (#45469) 2021-01-23 18:28:57 +01:00
Erik Montnemery f86beed7b0 Subscribe only to valid MQTT discovery topics (#45456) 2021-01-23 08:51:25 -05:00
J. Nick Koston a0b906005d Remove YAML support from nuheat (#45380) 2021-01-23 06:27:32 +01:00
Robert Svensson e92c4c99d5 Step Axis integration to platinum score on Integration Quality Scale (#45442) 2021-01-23 06:25:01 +01:00
J. Nick Koston 954ad854fb Remove the ability for mqtt to set speeds that are not in the speed_list (#45445) 2021-01-23 06:24:06 +01:00
Anders Melchiorsen 011d5208fd Upgrade aiolifx to 0.6.9 (#45448) 2021-01-23 06:21:57 +01:00
Dermot Duffy e40f0bf429 Disconnect Hyperion client in error conditions (#45411) 2021-01-23 06:21:38 +01:00
Alexei Chetroi daf24dc508 Always apply default light profiles, unless a profile is given (#45450) 2021-01-23 06:20:53 +01:00
J. Nick Koston 431b143eec Add a one touch pairing config flow for lutron caseta (#45136)
Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
2021-01-22 17:48:06 -06:00
Robert Svensson 03fb73c0ae Option to select what video source Axis camera should use (#45268)
* Fully working proposal of config option to select what video source camera entity should use

* Bump dependency to v43
Reflect dependency changes in how image sources is now a dict

* Fix bdracos comment
2021-01-23 00:15:58 +01:00
Chris 68e7ecb74b Add zwave-js fan platform (#45439)
* Add zwave-js fan platform

* Update remaining tests

* Missing awaits, tests fixed

* Fix typing

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-23 00:11:32 +01:00
Robert Svensson 22a6e55e70 Base deCONZ group supported features on the sum of all its lights (#45440) 2021-01-22 23:39:34 +01:00
Robert Svensson 57fa7f926a Introduce reauth flow to deCONZ (#45443) 2021-01-22 23:37:16 +01:00
Robert Svensson 18c7ae9a8b Fix hs_color crashing when activating scenes in deCONZ on color_temp lights (#45159) 2021-01-22 22:40:22 +01:00
Robert Svensson aaf4cd4a25 Basic logbook support for deCONZ events (#45400)
* Basic logbook support

* Improve formulation of message

* Improve typing
2021-01-22 22:39:11 +01:00
Charles Garwood 6813454821 Bump zwave-js-server-python to 0.12.0 (#45438) 2021-01-22 13:32:52 -05:00
Simone Chemelli 86cd7911de Clarify ConfigEntryNotReady log (#45425)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Charles Garwood <cgarwood@gmail.com>
2021-01-22 12:13:23 -05:00
Chris 198b875a6f Add cover platform to zwave js (#45193) 2021-01-22 11:51:39 -05:00
Xiaonan Shen 7e8d0a263c Rewrite dyson init test (#45409) 2021-01-22 10:27:43 -05:00
Matthias Alphart 4aceb0dd27 Add KNX service event_register (#45248) 2021-01-22 15:27:51 +01:00
Philip Allgaier 89fc92f68a Slightly improve error message in case of duplicate entity IDs (#45424) 2021-01-22 09:16:13 -05:00
Fabian Affolter 2bc59c1d8e Upgrade sendgrid to 6.5.0 (#45412) 2021-01-22 07:35:48 -05:00
Pascal Vizeli 8c573ae29c Bump hass-nabucasa 0.41.0 (#45422) 2021-01-22 07:30:49 -05:00
Fabian Affolter 30ed998d83 Upgrade pylast to 4.1.0 (#45413) 2021-01-22 10:16:20 +01:00
Fabian Affolter 148bc6081f Upgrade TwitterAPI to 2.6.3 (#45415) 2021-01-22 10:01:20 +01:00
Raman Gupta 8f7a4a68b2 Fix zwave_js climate entity temperature unit (#45377) 2021-01-22 09:23:16 +01:00
J. Nick Koston 38d4af1a6e Remove YAML support from powerwall (#45381) 2021-01-21 21:59:33 -05:00
Erik Montnemery 59f178ab9e Bump hatasmota to 0.2.6 (#45403) 2021-01-21 21:57:46 -05:00
Shay Levy f117ddc6fa Stop update_coordinator schedule refresh when HA is stopping (#45338)
* Stop update_coordinator schedule refresh when HA is stopping

* Add unittests

* Fix event type
2021-01-22 00:23:50 +01:00
J. Nick Koston 3029a95410 Remove YAML support from rachio (#45383) 2021-01-21 15:07:47 -05:00
J. Nick Koston d733292982 Remove YAML support from hunterdouglas_powerview (#45376) 2021-01-21 13:51:08 -05:00
J. Nick Koston db83aea1df Remove YAML support from nexia (#45379) 2021-01-21 13:45:52 -05:00
Xiaonan Shen 5de8639798 Rewrite dyson sensor test (#45382) 2021-01-21 13:43:52 -05:00
J. Nick Koston b68c287ff1 Add SSDP discovery to unifi (#45364) 2021-01-21 11:03:54 -06:00
Ville Skyttä ded242a8fe Treat Huawei LTE response error code -1 as apparently unsupported (#45304) 2021-01-21 14:34:24 +02:00
Robert Svensson b601e7e497 Introduce reauthentication flow to Axis integration (#45307) 2021-01-21 12:56:04 +01:00
Esben Damgaard 25adc6dd4f Don't update systemmonitor static boot time timestamp (#45165) 2021-01-21 12:35:37 +01:00
jjlawren 1f66457a34 Separate Plex/Sonos cross-integration tests (#45370) 2021-01-21 11:22:19 +01:00
Franck Nijhof 16843d9ec7 Revert "Update azure-pipelines-ci.yml to enable black mode in isort" (#45390)
This reverts commit 7a81ff55bc.
2021-01-21 11:01:22 +01:00
Franck Nijhof 03711b1d97 Revert "Update .pre-commit-config.yaml to prevent loops between isort and black" (#45391)
This reverts commit c03b4d8aee.
2021-01-21 11:00:09 +01:00
Jérôme W fd363f9c3a Format SNMP sensor data which are not strings/counters (#44823) 2021-01-21 09:41:09 +01:00
Dermot Duffy 30622b5575 Bump required hyperion-py version (#45385) 2021-01-21 09:32:16 +01:00
J. Nick Koston bb1224d06f Remove YAML support from myq (#45375) 2021-01-21 09:27:04 +01:00
Xiaonan Shen daa9449f18 Fix yeelight brightness in music mode (#45358) 2021-01-21 08:54:24 +01:00
Tobias Sauerwein e97448a201 Bump pyatmo to v4.2.2 (#45386) 2021-01-21 08:30:28 +01:00
J. Nick Koston e8cda598ac Fix memory leak in dhcp integration (#45378)
* Fix memory leak in dhcp integration

Passing the L2socket to AsyncSniffer caused a memory
leak on some systems. To ensure we can create a socket,
we do a test creation before starting AsyncSniffer
since the sniffer will create it in another thread
and we cannot see any permission error otherwise.

* Update tests

* space

* do not store packets
2021-01-21 08:26:58 +01:00
Hans Oischinger 2925474a5d Bump pyVicare to 0.2.5 (#45301)
Also removes the power sensor that is no more supported

Full diff between 0.2.0 and 0.2.5:
https://github.com/somm15/PyViCare/compare/b602c1670ea6041337782269b20851c5dedba32c...b443a4df75a7f976f762d3656fead5dd5e727545
2021-01-21 08:12:31 +01:00
Mick Vleeshouwer 9b14586568 Add DemoCover with only tilt controls (#45228)
* Add DemoCover with only tilt controls

* Add default tilt position to Pergola Roof

* Apply githooks (isort)

* Add new demo device to Google Assistant fixtures
2021-01-20 19:11:55 -06:00
Manuel Durán 7a81ff55bc Prevent isort from modifying files formatted with black and causing pipeline to fail (#45371)
Prevent isort from modifying files formatted with black and causing pipeline to fail
2021-01-20 19:08:18 -06:00
J. Nick Koston 536e835b39 Switch frequently used unifi controller properties to attributes (#45365) 2021-01-20 16:58:02 -06:00
J. Nick Koston 243014bff1 Fix test_wait_template_with_utcnow (#45356) 2021-01-20 23:56:00 +01:00
Xiaonan Shen 96448c6778 Change Dyson PureCoolLink fan speeds to adhere the standard (#45331) 2021-01-20 16:27:16 -05:00
J. Nick Koston a7741be9bb Wait for all triggers when one fails to attach (#45361) 2021-01-20 22:13:21 +01:00
Robert Svensson da4404e8cf Introduce reauthentication flow to UniFi integration (#45360)
* Improve site selection

* Reauth flow and tests
Add **kwargs to mock_aiohttp_client create_session to support inputting verify_ssl and cookie_jar

* Update homeassistant/components/unifi/config_flow.py

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

* Minor improvements

* Improve coverage

Co-authored-by: J. Nick Koston <nick@koston.org>
2021-01-20 22:10:40 +01:00
Aaron Bach 7ff02fe8d4 Replace strange "dict logic" in AirVisual pollutant level sensors (1 of 2) (#44868) 2021-01-20 15:24:56 -05:00
Maciej Bieniek 14f5eb7305 Increase update interval in AccuWeather integration (#44984) 2021-01-20 15:17:53 -05:00
Xiaonan Shen e46f1c0a10 Remove yeelight init dispatcher on unloading (#45359) 2021-01-20 15:10:01 -05:00
Santobert ab62a4ce39 Bump pybotvac to 0.0.20 (#45367) 2021-01-20 15:08:44 -05:00
Charles Garwood 0f5a3c4359 Add default URL to zwave_js config flow (#45366) 2021-01-20 15:03:15 -05:00
Xiaonan Shen 7a01d33814 Add empty password support to pi-hole (#37958) 2021-01-20 16:40:23 +01:00
Allen Porter bf0e012d1e Repair stream test_recorder.py and mark not flaky (#45054)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-20 14:44:24 +01:00
Charles Garwood d284c6369e Add permanent_hold attribute to Honeywell climate (#45341) 2021-01-20 08:34:14 -05:00
Manuel Durán c03b4d8aee Update .pre-commit-config.yaml to prevent loops between isort and black (#45321)
The pre-commit hook gets stuck in a file formatting loop where black and isort modify each other results. According to the isort webpage, the option --profile black has to be used to enhance compatibility. This has been run successfully in my local and has solved the loop.

See:
https://github.com/home-assistant/core/issues/45318
https://github.com/home-assistant/core/pull/45314
2021-01-20 08:01:13 -05:00
David F. Mulcahey ef8ee38274 bump libs for zha (#45345) 2021-01-19 21:19:46 -05:00
J. Nick Koston 3ae527c158 Switch dhcp to use async sniff for faster shutdown (#45339)
* Switch dhcp to use async sniff for faster shutdown

* Do not actually start the thread since we do not know when it will finish starting
2021-01-19 20:49:49 +01:00
Martin Hjelmare a9a0f8938f Move device tracker scanner attributes to ScannerEntity (#45312) 2021-01-19 11:04:08 -06:00
Philip Allgaier 52cfc06e12 Replace outdated / closed URL for reporting HA startup blocks (#45334) 2021-01-19 11:01:51 -05:00
Charles Garwood c1addde6f9 Add node_status websocket API command to zwave_js (#45325) 2021-01-19 16:41:24 +01:00
kpine d7a0f1e467 Include config entry id in zwave_js signal names (#45282) 2021-01-19 16:40:01 +01:00
Paulus Schoutsen f1646f4ecc Disable pytest enabled in vscode default settings (#45317) 2021-01-19 10:25:02 +01:00
Xiaonan Shen c929fbeea3 Rewrite dyson fan test (#45295)
* Improve device fixture to take parameter

* Remove unused code in dyson/fan

* Rewrite dyson fan test
2021-01-19 10:12:38 +01:00
Robert Svensson 07c3981de7 Use methods available to verify link local address (#45309) 2021-01-19 09:15:41 +01:00
Erik Montnemery 852136ccfe Bump hatasmota to 0.2.5 (#45297) 2021-01-18 22:26:33 +01:00
Ville Skyttä 94dbcc9d2b Generics and other type hint improvements (#45250) 2021-01-18 22:23:25 +01:00
Alan Tse 4928476abe Allow notify services to update existing targets (#45283) 2021-01-18 21:42:20 +01:00
Matthias Alphart 8d3564e275 Update xknx to 0.16.1 (#45247) 2021-01-18 10:15:28 -05:00
Charles Garwood 8de0b7b948 Add add & remove node commands to zwave_js websocket api (#45232) 2021-01-18 10:08:52 -05:00
Greg Dowling adab9adbce Pyroon discovery (#44811)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-18 10:00:30 -05:00
Xiaonan Shen 5ee4479151 Rewrite dyson vacuum test (#45265) 2021-01-18 09:56:54 -05:00
J. Nick Koston c621c0fa5d Remove YAML support from nut (#45276) 2021-01-18 15:16:42 +01:00
Robert Svensson 8e0addd216 Fix error with Axis light events without representation in light control (#45277) 2021-01-18 15:00:27 +01:00
gregod cf9ea6f82d Sanitize user-agent in wrong_login message (#45251) 2021-01-18 09:21:30 +01:00
Raman Gupta b2f914823d Add climate platform to zwave_js (#45177)
* add zwave_js support for climate

* fix

* add fixture

* rename fixture

* fix variable name error

* add tests

* fix tests and handle set_temp properly based on unit

* update call being tested

* fix tests

* improve coverage

* fix docstring

* address review comments

* fix test

* update enum class name

* bump zwave-js-server-python version and assume primary_value is always set

* add additional coverage

* fix docstrings and move populating modes/presets into initialization

* attempt to address comments

* improve comment

* move mode value into a variable so its easier to iterate in the future

* dont assume mode as a discovery point

* assume all values are available when node is ready

* fix order of operations

* switch to valueerror

* use primary value

* readd property and type to discovery schema
2021-01-18 03:45:06 +01:00
Greg Dowling a1b0d6baad Improve Roon media player play_media (#45221)
* Use revised play_media api.

* Move split path function to library.
2021-01-17 21:00:30 +01:00
J. Nick Koston ae3d038baa Update gogogate2 to be async (#42066) 2021-01-17 11:38:30 -06:00
jlvaillant a50fba4e0b more graceful shutdown in the presence of a proxy_stream (#45246) 2021-01-17 17:10:22 +01:00
Andre Lengwenus 21b9b6b2c8 Bump pypck to 0.7.9 (#45267) 2021-01-17 15:56:21 +01:00
Daniel Hjelseth Høyer 34a9b93d41 Update Tibber library, pyTibber==0.16.1 (#45263)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-01-17 14:42:52 +01:00
Xiaonan Shen dcd17530cd Add device classes to Dyson sensors (#45264) 2021-01-17 14:41:32 +01:00
Wim Fournier e828e18156 Add preheater current power to comfoconnect (#44083)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2021-01-17 12:30:38 +01:00
J. Nick Koston fbc98b1291 Make DHCP discovery ignore self assigned ip addresses (#45256) 2021-01-17 10:35:02 +01:00
Xiaonan Shen a42d43d054 Improve dyson code (#45172)
* Improve air_quality

* Improve climate

* Improve sensor

* Improve vacuum

* Improve fan

* Fix pylint

* Improve on_message

* Change unique ID back

* Remove unused attribute

* Remove redundant current_humidity

* Merge current_temperature

* Rename fan device to fan entity

* Fix filter life sensors

* Remove unneeded context switch

* Remove entity_type

* Fix pylint

* Add comment on humidity check
2021-01-17 10:24:26 +01:00
J. Nick Koston 28a611f3da Use standard device tracker attributes for "ip" and "mac" in Mikrotik (#45255)
* Mikrotik now uses the device_tracker standard attributes for "ip" and "mac"

The "mac_address" and "ip_address" attributes have been removed.

* Update homeassistant/components/mikrotik/device_tracker.py
2021-01-17 10:18:07 +01:00
J. Nick Koston 41e7d960ee Use dispatcher for unifi heartbeat tracking (#45211)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-17 03:10:52 +01:00
J. Nick Koston b71a9b5e28 Prefill the ip address for powerwall discovery (#45209) 2021-01-17 03:09:04 +01:00
J. Nick Koston 11cbf1152d Add dhcp discovery properties to mikrotik (#45205) 2021-01-17 00:26:32 +01:00
Diogo Gomes 1454349813 Re-add support for input_number as source of filter integration (#45245) 2021-01-16 23:57:32 +01:00
Allen Porter bf4b4623aa Bump google-nest-sdm to 0.2.9 (#45244)
This library contains user experience for the case where oauth
token cannot be refreshed on startup, which regressed after
https://github.com/home-assistant/core/pull/44686
2021-01-16 23:39:33 +01:00
Klaudiusz Staniek d4b3cf9c47 Fix the use of muted volume if not previously set in mpd (#45018) 2021-01-16 22:40:59 +01:00
Paulus Schoutsen 2de0b2aeca Merge pull request #45242 from home-assistant/rc 2021-01-16 22:35:52 +01:00
Sean Dague 278c5193a6 Bump mychevy version to 2.1.0 (#44810) 2021-01-16 22:25:41 +01:00
Paulus Schoutsen 34b90fd925 Bumped version to 2021.1.4 2021-01-16 22:05:42 +01:00
Alexei Chetroi 120304f235 Bump up ZHA dependency (#45230) 2021-01-16 22:05:08 +01:00
Philip Allgaier a66528c640 Fix all forecast datetime values in OpenWeatherMap (#45202) 2021-01-16 22:02:58 +01:00
Jacob Southard 2d4576ddcf Fix HomeKit climate integration for devices with a single set point in Heat_Cool mode. (#45065)
* Check supported flags in auto mode, and add tests.

* Fix test description.
2021-01-16 22:02:57 +01:00
Sjack-Sch 7d2fd4bce5 Fix Home Connect ambient color (#45038) 2021-01-16 22:02:56 +01:00
Martin Weinelt cecb711fe5 Update python-mpd2 to 3.0.3 (#45141)
Fixes: #44931
2021-01-16 20:45:54 +01:00
J. Nick Koston 3a3e5e636b Switch somfy to zeroconf discovery (#45158) 2021-01-16 20:41:41 +01:00
Robert Chmielowiec fa8ae77a6f Bump pycomfoconnect to 0.4 (#45201) 2021-01-16 20:29:21 +01:00
Penny Wood c8990e373d Add Homekit discovery for iZone (#45223) 2021-01-16 20:22:03 +01:00
Robert Svensson d80ef6c5fb Create shortcuts for config entry parameters in Axis device (#45233) 2021-01-16 19:54:48 +01:00
Alexei Chetroi 0b9687f7bd Bump up ZHA dependency (#45230) 2021-01-16 13:43:35 -05:00
Martin Hjelmare e72e66ae6f Bump zwave-js-server-python to 0.11.0 (#45231) 2021-01-16 19:26:15 +01:00
Robert Svensson 3cc45697cb Prettier Zeroconf discovery name for Axis devices (#45216)
* Change zeroconf name to be based of prettier name rather than hostname to help user understand what device is discovered
Have a full zeroconf discovery message in tests
Clean up unusued globals

* Use non-formatted serial number for config entry title as well
2021-01-16 18:59:57 +01:00
Shay Levy 8151721fbc Move switcher_kis services to entity services (#45204) 2021-01-16 18:18:40 +01:00
Raman Gupta 562d30319b Add lock platform to zwave_js (#45175)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-16 11:43:25 -05:00
Franck Nijhof b6148bbbe7 Add DHCP discovery to Toon (#45222) 2021-01-16 15:20:56 +01:00
J. Nick Koston 74082194a6 Add dhcp discovery to squeezebox (#45210) 2021-01-16 14:25:15 +01:00
Penny Wood 7c7b357357 Handle older Izone controller (#45218)
* Updated to new library
* Fixed config flow exception
2021-01-16 21:24:00 +08:00
Robert Svensson 2044b33eb6 Add DHCP discovery support to Axis integration (#45167) 2021-01-16 11:03:56 +01:00
J. Nick Koston 233f923cd7 Add support for discovering individual roombas (#45200)
* Add support for discovering individual roombas

* add missing translation string

* Update homeassistant/components/roomba/strings.json

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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-15 21:28:12 -10:00
J. Nick Koston 5e01b828af Discover devices from device_trackers with router sources (#45160)
* Discover devices from device_trackers with router sources

* Update homeassistant/components/dhcp/__init__.py

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

* fix stop being called on the wrong context

* clean

* move it to base

* cleanup was too agressive

* Update homeassistant/components/dhcp/__init__.py

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

* coverage

* revert legacy changes

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-16 01:01:37 +01:00
Robert Svensson 598a0d19b1 Normalise unique ID in Axis integration (#45203)
* Move adding unique id to config entry from setup_entry to migrate_entry

* Normalise unique ID

* MQTT subscribe should still use the serial number in the way the device itself expects
2021-01-16 01:01:14 +01:00
J. Nick Koston b3764da912 Fix exception when trying to configure an ignored somfy mylink (#45198) 2021-01-16 00:48:19 +01:00
Philip Allgaier 5677adc104 Fix all forecast datetime values in OpenWeatherMap (#45202) 2021-01-15 23:32:38 +01:00
On Freund 03b2fbd043 Fix Typo in Onkyo A/V info (#45194) 2021-01-15 15:18:45 -05:00
J. Nick Koston cffb1458a1 Improve dhcp discovery for somfy_mylink to handle multiple devices (#45166)
* Improve dhcp discovery for somfy_mylink to handle multiple devices

* Fix ip

* normalize

* logging debug

* force str

* we need a str

* fix tests

* add tests

* pylint
2021-01-15 19:47:25 +01:00
Sjack-Sch 7ce897f373 Fix Home Connect ambient color (#45038) 2021-01-15 19:40:29 +01:00
Martin Hjelmare 74c81b7c01 Bump zwave-js-server-python to 0.10.0 (#45189) 2021-01-15 19:32:10 +01:00
Sergio Conde Gómez 2e42a862b0 Remove service and config schemas from foscam (#45154) 2021-01-15 18:24:31 +01:00
Charles Garwood 9dbf14188a Add WebSocket API Foundation for zwave_js (#45151) 2021-01-15 15:17:40 +01:00
Marcel van der Veldt 071c8cc67d Add binary_sensor platform to zwave_js (#45081)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-15 09:15:03 -05:00
Franck Nijhof b4268edd6a Merge pull request #45186 from home-assistant/rc 2021-01-15 14:33:52 +01:00
Franck Nijhof e70abf5434 Merge branch 'master' into rc 2021-01-15 14:04:21 +01:00
Franck Nijhof 572b323ade Bumped version to 2021.1.3 2021-01-15 13:58:52 +01:00
Pascal Vizeli f91d40ac38 Bump aioHTTP 3.7.3 - YARL 1.6.3 (#45180) 2021-01-15 13:58:19 +01:00
Franck Nijhof 48be748058 Extend filter and filter tests (#45179) 2021-01-15 13:58:15 +01:00
Franck Nijhof c2deadf994 Add filtering 2021-01-15 13:57:56 +01:00
Pascal Vizeli e1427c45f2 Bump aioHTTP 3.7.3 - YARL 1.6.3 (#45180) 2021-01-15 13:19:22 +01:00
Tobias Sauerwein b5690053a9 Only add Netatmo climate schedule attribute if not None (#45176) 2021-01-15 13:15:59 +01:00
Philip Allgaier 41b45c7f78 Auto-create friendly name for persistent notifications (#42427) 2021-01-15 12:13:56 +01:00
Paulus Schoutsen 93c2f2bbeb Bump Z-Wave JS Server Python to 0.9.0 (#45181) 2021-01-15 10:59:35 +01:00
Franck Nijhof dee0f887de Extend filter and filter tests (#45179) 2021-01-15 10:30:29 +01:00
Marcel van der Veldt a276f2d19e Fix zwave_js adding not fully interviewed nodes (#45162)
* fix situation were nodes are being added or not fully interviewed

* adjust test

* lint
2021-01-15 01:09:03 +01:00
J. Nick Koston 7fada806af Downgrade dhcp log message error message when running without CAP_NET_RAW (#45157) 2021-01-14 10:46:15 -10:00
Paulus Schoutsen 2600fdfa67 Merge pull request #45156 from home-assistant/rc 2021-01-14 20:50:23 +01:00
Franck Nijhof 0bd2c13e26 Add filtering 2021-01-14 20:26:08 +01:00
Philip Allgaier 0fdda9d0f6 Fix OpenWeatherMap forecast timestamp (#45124) 2021-01-14 19:20:16 +00:00
ehendrix23 7ac208ead6 Bump MyQ to 2.0.14 (#45067) 2021-01-14 19:20:15 +00:00
Joakim Sørensen e1989399f0 Fallback to tag for any AfterShip tracking that have no checkpoints (#45053) 2021-01-14 19:20:14 +00:00
Santobert d0216307f3 Fix neato battery sensor not ready (#44946)
* Fix neato battery sensor not ready

* Edit available attribute

* Remove unnecessary condition
2021-01-14 19:20:14 +00:00
Paulus Schoutsen f141f18db7 Bumped version to 2021.1.2 2021-01-14 19:08:37 +00:00
rikroe 2294f11070 Bump bimmer_connected to 0.7.14 (#45086)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2021-01-14 19:08:29 +00:00
Diogo Gomes 3771a800d3 Update the Utility Meter sensor status on HA start (#44765)
* fix status on HA start

* better coverage and fix

* fix test

* address review
2021-01-14 19:08:03 +00:00
Erik Montnemery a1368ad3ed Remove beta tag from Tasmota integration (#45153) 2021-01-14 20:02:34 +01:00
Franck Nijhof f047d04882 Add filtering 2021-01-14 20:02:01 +01:00
bchastain 3800a4feee Add pressure to OWM forecast data (#43843) 2021-01-14 19:47:48 +01:00
unaiur ab518a7755 Migrate to maxcube-api 0.3.0 version (#45126)
Upgrade maxcube-api to solve bugs fixed in last 3 years.
2021-01-14 11:33:02 +01:00
Corbeno 4bca9596ee Rework Proxmoxve to use a DataUpdateCoordinator (#45068)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-14 11:31:37 +01:00
J. Nick Koston 4efe6762c4 Remove YAML support from harmony (#45140) 2021-01-14 09:45:32 +01:00
J. Nick Koston 23a73dc5b1 Mark YAML support for DoorBird deprecated (#45139) 2021-01-14 09:44:01 +01:00
On Freund 2ac658d257 Get A/V info for Onkyo receivers (#34477)
* Get A/V info for Onkyo receivers

* Fix lint errors

* Remove blank line

* Trigger CI
2021-01-14 09:39:11 +01:00
Erik Montnemery e0c8b1aab6 Remove from_state from alarm device triggers (#45127) 2021-01-14 09:12:32 +01:00
J. Nick Koston da677f7d5a Add support for discovery via DHCP (#45087)
* Add support for discovery via DHCP

* additional tesla ouis

* merge tests

* dhcp test

* merge requirements test

* dhcp test

* dhcp discovery

* dhcp discovery

* pylint

* pylint

* pylint

* fix

* Add matching tests

* 100% cover

* cleanup

* fix codespell

* Update exception handling

* remove unneeded comment

* fix options handling exception

* fix options handling exception
2021-01-14 09:09:08 +01:00
Philip Allgaier 402a0ea7da Fix OpenWeatherMap forecast timestamp (#45124) 2021-01-14 08:47:45 +01:00
ehendrix23 17cb071173 Bump MyQ to 2.0.14 (#45067) 2021-01-13 20:15:23 -10:00
Petro31 3ebc5d45a8 Add energy and power sensor tests & fix device_class (#45122) 2021-01-13 15:37:54 -05:00
Nikolay Vasilchuk eca6bc6a73 Starline OBD information (#37608)
* Starline OBD data

* Small fix

* Review (comments)

* Review (service description)

* Review (service method)

* starline updated to 0.1.5

* Small typo fix
2021-01-13 20:44:24 +01:00
TimothyLeeAdams 7872e6caf8 Change attribute key for Lutron cover to lutron_integration_id (#45114)
Currently, covers return "Lutron Integration ID" as a state attribute. This is inconsistent with the light, switch, and binary_sensor which return "lutron_integration_id".
2021-01-13 18:59:57 +01:00
Julien Roy 94417e3e14 Add start torrent and stop torrent service for transmission integration (#43920) 2021-01-13 17:44:57 +01:00
Tobias Sauerwein e05bb7e858 Expose selected Netatmo schedule (#45077) 2021-01-13 17:01:29 +01:00
Paulus Schoutsen 732cf47ff6 Filter some Alexa reports that are duplicate (#45093)
* Filter some Alexa reports that are duplicate

* When state changes during reporting, only report last state, not all state changes
2021-01-13 16:54:54 +01:00
Diogo Gomes 81c77942eb Update the Utility Meter sensor status on HA start (#44765)
* fix status on HA start

* better coverage and fix

* fix test

* address review
2021-01-13 16:42:28 +01:00
Martin Hjelmare 79d37fdf12 Add zwave_js light platform tests (#45107)
* Add bulb 6 multi color device state fixture

* Add light test foundation

* Add no cover comment for todo code

* Update hs_color

* Test turn on light

* Test light turn off

* Fix brightness comparison

* Test setting same brightness

* Test setting same rgb color

* Test color temp update

* Test setting same color temp

* Add entity module to coverage calculation

* Fix typing
2021-01-13 16:28:51 +01:00
Alexei Chetroi de8f273bd0 Allow input_number entity_id as for numeric_state trigger thresholds (#45091)
* Allow input_number as limits for numeric_state trigger

* Rename threshold schema
2021-01-13 16:20:59 +01:00
Sergio Conde Gómez 83b210061d Add config_flow and stream selection to foscam (#41429)
* Add config_flow and stream selection to foscam

* Simplify config_flow steps

* Make debug log entry more useful

* Deprecate config and platform schemas

* Simplify config loading

* Add config flow testing

* Remove unneeded CONFIG_SCHEMA deprecation

* Improve test coverage

* Unload service by tracking loaded entries

* Address comment

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-13 16:09:05 +01:00
Martin Hjelmare 3537a7c3d5 Correct zwave_js value changed callback signature (#45110) 2021-01-13 15:31:31 +01:00
rikroe 938d8be0c8 Bump bimmer_connected to 0.7.14 (#45086)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2021-01-13 15:25:28 +01:00
jjlawren 411cc6542c Move Plex->Sonos playback to built-in service (#45066)
* Move Plex->Sonos playback service from integration to platform

* Test against 'native' Plex media_players

* Add Plex to Sonos after_dependencies

* Remove circular dependency

* Raise exceptions in failed service calls

* Add test to forward service call from Sonos

* Additional Sonos->Plex tests

* Fix docstring
2021-01-13 15:24:44 +01:00
Jacob Southard 3364e945aa Fix HomeKit climate integration for devices with a single set point in Heat_Cool mode. (#45065)
* Check supported flags in auto mode, and add tests.

* Fix test description.
2021-01-13 15:21:32 +01:00
Jesse Hills f78b02b163 Do not try to connect to disabled ESPHome devices. (#45092) 2021-01-13 15:17:13 +01:00
J. Nick Koston ffd9c4e410 Add additional roku model to discovery (#45103) 2021-01-13 15:14:45 +01:00
badguy99 b9e4d5988f Soma: fix battery drain issue caused by excess update requests (#45104)
* split up update and throttle update on sensor

* Update imports

* Add blank lines for isort
2021-01-13 15:14:15 +01:00
Robert Svensson 6325bc8bfe Follow Axis library changes and improve tests (#44126) 2021-01-13 14:03:54 +01:00
ErnstEeldert ff3a1f2050 Add device class attribute to tado humidity sensor state (#45084)
* add device class attribute to humidity sensor state

* * explict return none
* use const for device class value

* removed unnecessary icon definitions
2021-01-13 13:45:11 +01:00
Alexei Chetroi ec038bc6ea Allow any parameter of a light profile as an optional parameter (#44079)
* No code duplication for profile application

* Refactor color profile as a dataclass

* Typing

* Make color_x and color_y of a Light profile optional

* Update tests

* Make brightness field of a Light profile optional

* Transition can be of a float type

* Allow fractional transition times in light profiles

Make transition of a float type.
Allow transition to be optional with 5 column CSV files.

* Make pylint happy

* Fix dropped async_mock

* Simplify CSV row schema
2021-01-13 12:11:20 +01:00
Santobert 10bc05df00 Fix neato battery sensor not ready (#44946)
* Fix neato battery sensor not ready

* Edit available attribute

* Remove unnecessary condition
2021-01-13 10:23:16 +01:00
J. Nick Koston ac60b34d17 Roomba cleanups (#45097)
* Roomba cleanups

Remove async_step_init backwards compat

Move urls to description_placeholders.

Fix typos

* fix test

* fix fallback to manual when roomba is in the wrong state
2021-01-13 07:27:25 +01:00
Allen Porter eebd0d333e Clear cached nest event images after expiration (#44956)
* Clear cached nest event images after expiration

* Don't share removal cleanup with alarm cleanup

Don't share code across these functions since it would require a dummy timestamp values that is unnecessary.

* Increase test coverage on sdm camera remove

* Update homeassistant/components/nest/camera_sdm.py

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

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-12 22:08:59 -08:00
Paulus Schoutsen 82746616fa Cloud: Add web socket API to pick default TTS language (#45064)
* Allow picking default TTS language

* Fix test

* Fix coroutine function

* Improve test coverage

* Remove stale import

* Clean up hass

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-13 00:05:30 +01:00
Paulus Schoutsen 4e71be852a Bump Z-Wave JS to 0.7.1 (#45080) 2021-01-12 16:18:06 +01:00
Charles Garwood be2aba6c52 Fix docstring in zwave_js switch test (#45076) 2021-01-12 14:29:12 +01:00
Joakim Sørensen 8ce32d67f9 Fallback to tag for any AfterShip tracking that have no checkpoints (#45053) 2021-01-12 10:33:14 +01:00
Paulus Schoutsen e83ced6737 Add name to ignored entries (#45051)
* Add name to ignored entries

* Fix test
2021-01-12 09:26:20 +01:00
Charles Garwood f312b87a3f Add switch platform to zwave_js (#45046) 2021-01-11 18:40:39 -05:00
Philip Allgaier cad2fa89ed Default input_datetime to current date (#45052) 2021-01-11 23:45:58 +01:00
Philip Allgaier bade98624d Fix tests for input_datetime (#45055) 2021-01-11 23:13:16 +01:00
J. Nick Koston 13cdf0ba63 Cleanups for somfy_mylink (#45026)
* Cleanups for somfy_mylink

* Use the target/unique_id to configure reverse

* Simplify options flow

* Various code review cleanups

* Deprecate YAML

* revert get change

* revert get change

* add note about empty response

* move CONF_DEFAULT_REVERSE out of loop

* Update homeassistant/components/somfy_mylink/config_flow.py

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

* Ensure we deepcopy options

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-11 10:10:02 -10:00
Paulus Schoutsen f19b72ea02 Drop awarecan from codeowners (#45049) 2021-01-11 17:58:59 +01:00
Simone Chemelli bc2c7b2d48 Add Shelly RGB devices management (#43993)
* Add support for RGB devices

* White value handling

* Fixed logic for some devices (ColorTemp, White, Kelvin limits)

* Code cleanup

* Moved func from utils to light

* Fix for DUO

* Added "Optional" to properties that need it

* Code more understandable

* Applied code review suggestions

* Applied code review suggestions

* Updated logic to always show all available options
2021-01-11 16:47:49 +01:00
Shay Levy e3f38942cc Add 100% tests coverage for Shelly cover and switch platforms (#45001) 2021-01-11 16:45:06 +01:00
Martin Hjelmare d270f9515b Add zwave_js init module tests (#45048)
* Add entry setup and unload test

* Test home assistant stop

* Test on connect and on disconnect

* Test client connect timeout

* Test ready node added

* Test non ready node added

* Test existing node not ready

* Test device registry state

* Add common test tools module

* Add existing ready node test

* Include init module in coverage calculation

* Clean docstrings
2021-01-11 16:05:11 +01:00
Erik Montnemery d60fc0de38 Add availability_mode "all" and "any" to MQTT entities (#44987)
* Add availability_mode "all" to MQTT entities

* Add availability mode any
2021-01-11 16:04:22 +01:00
J. Nick Koston 74e7f7c879 Update roomba config flow to walk users through pairing (#45037)
* Update roomba config flow to walk users though pairing

* Remove YAML support

* adjust tests

* increase cover

* pylint

* pylint
2021-01-11 14:46:54 +01:00
J. Nick Koston eb5f3b282b Mark YAML support for August deprecated (#45039) 2021-01-11 14:40:32 +01:00
J. Nick Koston 38a5f25b59 Fix vacuums that do not support start with homekit (#45030)
* Fix vacuums that do not support start with homekit

* fix tests
2021-01-11 14:38:42 +01:00
Allen Porter 65e3661f88 Repair flaky and broken stream tests in test_hls.py, and turn back on (#45025)
* Unmark tests as flaky (though still flaky)

This put tests into the broken state where they are flaky and do not yet pass

* Fix bug in test_hls_stream with incorrect path

* Enable and de-flake HLS stream tests

Background: Tests encode a fake video them start a stream to be decoded. Test
assert on the decoded segments, however there is a race with the stream worker
which can finish decoding first, and end the stream which ereases all buffers.

Breadown of fixes:
- Fix the race conditions by adding synchronization points right before the
  stream is finalized.
- Refactor StreamOutput.put so that a patch() can block the worker
  thread.  Previously, the put call would happen in the event loop which was
  not safe to block. This is a bit of a hack, but it is the simplist possible
  code change to add this synchronization and arguably provides slightly better
  separation of responsibilities from the worker anyway.
- Fix bugs in the tests that make them not pass, likely due to changes
  introduced while the tests were disabled
- Fix case where the HLS stream view recv() call returns None, indicating
  the worker finished while the request was waiting.

The tests were previously failing anywhere from 2-5% of the time on a lightly
loaded machine doing 1k iterations.  Now, have 0% flake rate.  Tested with:
$ py.test --count=1000 tests/components/strema/test_hls.py
2021-01-11 14:34:45 +01:00
Charles Garwood ed4e8cdbc5 Bump zwave-js-server-python to 0.7.0 (#45045) 2021-01-11 14:34:08 +01:00
J. Nick Koston af21893652 Remove safe mode from HomeKit (#45028)
Safe mode was added to work around a race condition where
the mdns announcment was sent too early and would cause
pairing to fail. Since this has been corrected in
HAP-python, there is no longer a need to have
safe mode.
2021-01-11 14:26:09 +01:00
Paulus Schoutsen e584902b8b Remove empty schema (#45044) 2021-01-11 14:25:09 +01:00
Stefan Agner 54064b4010 Increase timeout to avoid killing the core during shutdown (#45029)
Stopping the core goes through several stages, which can take up to
120s, 60s and 30s respectively. However, if shutdown is taking longer
than 60s overall, s6 isn't patient and kills the core:
Jan 10 23:56:58 homeassistant eb034fca9c7d[407]: s6-svwait: fatal: timed out
Jan 10 23:56:58 homeassistant eb034fca9c7d[407]: [s6-finish] sending all processes the TERM signal.
Jan 10 23:57:01 homeassistant eb034fca9c7d[407]: [s6-finish] sending all processes the KILL signal and exiting.

This is most of the time not a problem since shutdown is quicker than
that.

However, increasing the timeout is especialy useful to debug cases when
an event is hanging, since the core will point it out after its timeout
elapsed.

Set the timeout to 220s, which is all core timeouts plus 10s grace time.
2021-01-11 11:24:02 +01:00
Stefan Agner ab25c5a2bd Shutdown asyncio http server within 10 seconds (#45033)
If there are open requests, the http server waits up to 60 seconds.
However, some requests (such as the Reboot button) seems to keep a
connection open and needlessly slow down the reboot process:

```
Jan 11 00:52:54 homeassistant eb034fca9c7d[404]: 2021-01-11 00:52:54 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event homeassistant_stop[L]>
Jan 11 00:52:54 homeassistant eb034fca9c7d[404]: 2021-01-11 00:52:54 DEBUG (MainThread) [homeassistant.helpers.restore_state] Dumping states 111
Jan 11 00:52:54 homeassistant eb034fca9c7d[404]: 2021-01-11 00:52:54 DEBUG (MainThread) [homeassistant.helpers.restore_state] Dumping states
Jan 11 00:52:54 homeassistant eb034fca9c7d[404]: 2021-01-11 00:52:54 INFO (MainThread) [homeassistant.components.websocket_api.http.connection] [281473359593728] Connection closed by client
Jan 11 00:52:56 homeassistant eb034fca9c7d[404]: 2021-01-11 00:52:56 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [281473359593728] Disconnected
Jan 11 00:53:54 homeassistant eb034fca9c7d[404]: 2021-01-11 00:53:54 DEBUG (MainThread) [homeassistant.core] Waited 60 seconds for task: <Task pending name='Task-585' coro=<async_setup.<locals>.stop_server() running at /usr/src/homeassistant/homeassistant/components/http/__init__.py:228> wait_for=<_GatheringFuture pending cb=[<TaskWakeupMethWrapper object at 0xffff9f112f70>()]> cb=[_wait.<locals>._on_completion() at /usr/local/lib/python3.8/asyncio/tasks.py:518]>
...
```
2021-01-11 09:36:14 +01:00
Charles Garwood d68fdbc283 Add zwave_js integration (#45020)
* Run zwave_js scaffold (#44891)

* Add zwave_js basic connection to zwave server (#44904)

* add the basic connection to zwave server

* fix name

* Fix requirements

* Fix things

* Version bump dep to 0.1.2

* fix pylint

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Bump zwave-js-server-python to 0.2.0

* Use zwave js server version check instead of fetching full state (#44943)

* Use version check instead of fetching full state

* Fix tests

* Use 0.3.0

* Also catch aiohttp client errors

* Update docstring

* Lint

* Unignore zwave_js

* Add zwave_js entity discovery basics and sensor platform (#44927)

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

* Complete zwave_js typing (#44960)

* Type discovery

* Type init

* Type entity

* Type config flow

* Type sensor

* Require typing of zwave_js

* Complete zwave_js config flow test coverage (#44955)

* Correct zwave_js sensor device class (#44968)

* Fix zwave_js KeyError on entry setup timeout (#44966)

* Bump zwave-js-server-python to 0.5.0 (#44975)

* Remove stale callback signal from zwave_js (#44994)

* Add light platform to zwave_js integration (#44974)

* add light platform

* styling fix

* fix type hint

* Fix typing

* Update homeassistant/components/zwave_js/const.py

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

* Update homeassistant/components/zwave_js/entity.py

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

* Update homeassistant/components/zwave_js/entity.py

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

* Update homeassistant/components/zwave_js/entity.py

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

* Update homeassistant/components/zwave_js/entity.py

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

* Update homeassistant/components/zwave_js/entity.py

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

* color temp should be integer

* guard Nonetype error

* Update homeassistant/components/zwave_js/light.py

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

* Update homeassistant/components/zwave_js/light.py

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

* some fixes after merging

* add additional guards for None values

* adjustments for rgb lights

* Fix typing

* Fix black

* Bump zwave-js-server-python to 0.6.0

* guard value updated log

* remove value_id lookup as its no longer needed

* fiz sending white value

* Update homeassistant/components/zwave_js/light.py

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

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

* Add zwave_js test foundation (#44983)

* Exclude text files from codespell

* Add basic dump fixture

* Add test foundation

* Fix test after rebase

* Exclude jsonl files from codespell

* Rename fixture file type to jsonl

* Update fixture path

* Fix stale docstring

* Add controller state json fixture

* Add multisensor 6 state json fixture

* Update fixtures

* Remove basic dump fixture

* Fix fixtures after library bump

* Update codeowner

* Minor cleanup Z-Wave JS (#45021)

* Update zwave_js device_info (#45023)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-01-11 00:08:25 +01:00
Ryan Fleming 1402e7ae56 Use the camera UUID as the entity unique id (#44937) 2021-01-10 23:23:32 +01:00
Philip Allgaier 4de9f5194f Include current version in updater log output (#45022) 2021-01-10 21:37:55 +01:00
J. Nick Koston f5b389faa8 Warn users when their HomeKit configuration may be unstable (#44999) 2021-01-10 09:38:41 -10:00
J. Nick Koston 4b54694c5c Add config flow for somfy_mylink (#44977)
* Add config flow for somfy_mylink

* fix typo
2021-01-10 20:24:22 +01:00
J. Nick Koston b450d4c135 Improve unifi performance with many devices (#45006)
With 250 clients, there were about 18000 timers updated every
minute. To avoid this, we check which entities should be set
to not_home only once every second.
2021-01-10 20:12:21 +01:00
Olivier Cloirec 707a8e62f9 Add stop support to openzwave (mqtt) cover (#44622)
* feat: add stop to openzwave (mqtt) cover

* Fix isort and black linter

* Remove supported_features for cover.

As suggested by @MartinHjelmare, not needed anymore because base class
implementation is sufficient.

https://github.com/home-assistant/core/pull/44622#discussion_r549854542

* Make a simpler version depending on idempotency

qt-openzwave already implements idempotency, see:
https://github.com/OpenZWave/qt-openzwave/blob/77e414217f83fae89a8f41156db3783d562703b1/qt-openzwave/source/qtozwvalueidmodel.cpp#L180

We can use it and trigger button release anywhen.

* Clean up

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-10 18:05:52 +01:00
Allen Porter a73a82e381 Improve nest client error handling using newest library (#44998) 2021-01-09 17:52:30 -08:00
Josef Schlehofer f240106189 Upgrade requests to 2.25.1 (#44989) 2021-01-09 20:02:50 +01:00
Erik Montnemery 248802efd5 Add MQTT base entity (#44971) 2021-01-09 17:46:53 +01:00
Paulus Schoutsen 75615bd92a Merge pull request #44982 from home-assistant/rc
2021.1.1
2021-01-09 16:02:54 +01:00
Paulus Schoutsen 9524766b07 Bumped version to 2021.1.1 2021-01-09 14:31:14 +00:00
ehendrix23 21121b6e9b Bump pymyq to 2.0.13 (#44961) 2021-01-09 14:31:10 +00:00
Erik Montnemery 1b0e0996af Fix parameters when toggling light (#44950) 2021-01-09 14:31:09 +00:00
Sergio Oller 0e4c560f38 Disambiguate Supervisor HTTPUnauthorized on user/password validation (#44940)
* Disambiguate HTTPUnauthorized on user/password validation

The HA core API usually returns 401 when the request does not
have proper authentication tokens or they have expired.

However the user/password validation endpoint may also return
401 when the given user/password is invalid.

The supervisor is currently unable to distinguish both scenarios,
and it needs to.

See https://github.com/home-assistant/supervisor/issues/2408

* Return 404 if user& password are not found/valid

* Fix test for invalid user/password
2021-01-09 14:31:09 +00:00
J. Nick Koston 5dfe8e15e3 Fix wait_template incorrectly matching falsey values (#44938) 2021-01-09 14:31:07 +00:00
Matthias Alphart 5ef6f87ab9 Fix KNX cover state return open when unknown (#44926) 2021-01-09 14:31:07 +00:00
ehendrix23 eabe757e20 Bump pymyq to 2.0.13 (#44961) 2021-01-09 15:29:48 +01:00
Martin Hjelmare 8b72324ae6 Add zwave to ozw migration (#39081)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-09 15:23:03 +01:00
bchastain 982c42e746 Add pressure forecast to HA weather entity model (#44965) 2021-01-09 14:52:49 +01:00
Erik Montnemery 6dd6d9b368 Deduplicate MQTT entity discovery code (#44970) 2021-01-09 14:37:33 +01:00
Erik Montnemery 2d9eb25142 Fix parameters when toggling light (#44950) 2021-01-09 01:10:47 +01:00
Erik Montnemery b85efd343f Move MQTT entity helpers to separate file (#44838)
* Move MQTT entity helpers to separate file

* Fix imports

* Update MQTT number

* Review comments

* Fix formatting
2021-01-09 00:47:17 +01:00
Ville Skyttä 3a88a4120e Helpers type hint improvements (#44964) 2021-01-09 00:08:34 +01:00
Ville Skyttä 3569d92385 Remove script/test (#44967)
It's still referencing tox py36, which has been obsolete for over a
year.
2021-01-08 23:58:39 +01:00
Diogo Gomes e3c1281616 Add MQTT Number (non optimistic) (#44883)
* non optimistic

* test restored state

* ups

* review

* Ensure the entity is not in optimistic mode

Co-authored-by: Erik Montnemery <erik@montnemery.com>

Co-authored-by: Erik Montnemery <erik@montnemery.com>
2021-01-08 23:43:14 +01:00
Tobias Sauerwein 8fa62329a4 Fix Netatmo climate boost for valves (#44957) 2021-01-08 18:41:31 +01:00
Sergio Oller 905100a189 Disambiguate Supervisor HTTPUnauthorized on user/password validation (#44940)
* Disambiguate HTTPUnauthorized on user/password validation

The HA core API usually returns 401 when the request does not
have proper authentication tokens or they have expired.

However the user/password validation endpoint may also return
401 when the given user/password is invalid.

The supervisor is currently unable to distinguish both scenarios,
and it needs to.

See https://github.com/home-assistant/supervisor/issues/2408

* Return 404 if user& password are not found/valid

* Fix test for invalid user/password
2021-01-08 17:28:22 +01:00
J.P. Hutchins 793adb7f40 Add torrent id to Transmission events (#44187)
* Fire event after object update; clarify code across related methods

* Change var to torrent, clarity

* Add typehints,  _ prefix private attributes
2021-01-08 07:53:47 -08:00
Joakim Sørensen d99bc99d9b Prefix versions in system health (#44921)
* Prefix versions in system health

* Adjust test

* Update homeassistant/components/hassio/strings.json
2021-01-08 16:03:06 +01:00
Ottavio Campana 58195c64b7 Fix media renderers without volume control (#44874)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-08 14:15:54 +01:00
J. Nick Koston 7c93a11aba Fix wait_template incorrectly matching falsey values (#44938) 2021-01-08 13:07:50 +01:00
Fabian Affolter c457ea854c Upgrade youtube_dl to 2021.01.03 (#44942) 2021-01-08 11:59:30 +01:00
Fabian Affolter e134c17df2 Upgrade discord.py to 1.6.0 (#44941) 2021-01-08 11:53:46 +01:00
Matthias Alphart 30189fb5d5 Fix KNX cover state return open when unknown (#44926) 2021-01-08 11:50:02 +01:00
Michael Chisholm e35e460e69 Use parent_id to find cause of logbook events with new contexts (#44416)
* Use parent_id to find cause of events with new contexts

When looking up the causing event for logbook display, use the `parent_id`
of the current context if the current context just points back to the
current event.

This now shows in the logbook the cause of an event in the case that a
component has created a new context from an existing context and tied
them together via the `Context.parent_id`.

* Fix exception when parent event not available

* Use async_Log_entry to avoid jump into executor
2021-01-07 20:27:03 -10:00
Jamin Collins 3b184ad11c Implement support for additional ecobee hold modes (#40520)
* useEndTime2hour   - 2 hours
* useEndTime4hour   - 4 hours
* indefinite        - Until I change it

These changes have been tested with an ecobee3 lite running firmware
version 4.5.81.200

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2021-01-07 18:09:22 -10:00
J. Nick Koston c54a0f80af Update nexia to 0.9.5 (#44924) 2021-01-08 03:00:53 +01:00
Bouwe Westerdijk cb3b37a87a Correct Plugwise sensor scaling (#44344)
* Remove sensor-scaling, handled by the back-end

* Correct assert-values

* Update test-fixtures

* Revert "Correct assert-values"

This reverts commit f1a1891f73414f1b74482cf963cc800220b9196a.

* Adapt value to the updated userdata set

* Link to plugwise v0.8.5, update fixtures

* Correct test-values

* Fix typo
2021-01-08 02:44:00 +01:00
Ville Skyttä 20e2493f68 Improve device registry type hints (#44919)
* Fix async_get_or_create via_device type hint

* Specify collection element types
2021-01-08 02:38:57 +01:00
jjlawren 0426b211f6 Rewrite Plex tests to use mocked payloads (#44044) 2021-01-07 19:56:52 +01:00
Paulus Schoutsen caf14b78d1 Homekit has two types (#44879) 2021-01-07 08:44:34 -10:00
Ville Skyttä 2fb3be50ab Make DeviceRegistry.async_get_device connections arg optional (#44897)
* Make async_get_device connections Optional, default None

* Remove unnecessary async_get_device connections arg usages

Some of these were using an incorrect collection type, which didn't
cause issues mostly just due to luck.
2021-01-07 13:49:45 +01:00
emufan 751ac0b955 Bump pydaikin version to 2.4.1 (#44888) 2021-01-06 20:15:16 +01:00
Franck Nijhof 92431049e5 Revert "Revert "Bump pypck to 0.7.8"" (#44885)
This reverts commit 6de8824980.
2021-01-06 18:01:06 +01:00
Franck Nijhof 1a44a8a714 Merge branch 'master' into dev 2021-01-06 17:59:14 +01:00
Franck Nijhof bb1ebae5cb Merge pull request #44881 from home-assistant/rc 2021-01-06 17:37:48 +01:00
Ville Skyttä 03ffeb9a02 Fix notion bridge id update device registry identifier usage (#44872) 2021-01-06 09:18:04 -07:00
Franck Nijhof 57d119a7fe Revert "Bump pypck to 0.7.8" (#44884)
This reverts commit addafd517f.
2021-01-06 16:49:02 +01:00
Franck Nijhof 6de8824980 Revert "Bump pypck to 0.7.8" (#44884)
This reverts commit addafd517f.
2021-01-06 16:47:02 +01:00
Franck Nijhof 9c478e8de7 Bumped version to 2021.1.0 2021-01-06 16:03:04 +01:00
Allen Porter 560e3811a3 Generate nest images thumbnails from events (#44638)
* Capture nest still images from events

Use python google-nest-sdm API for fetching images.  Update home assistant
to use the google-nest-sdm API for fetching the image contents generated
by the server.  This uses the existing websession object for server fetches,
reducing the amount of new code and facilites unit testing using the existing
mechanism.

Simplify tests using the image fetch API rather than a snapshot API
2021-01-06 07:02:04 -08:00
treylok 9d03b56c5c Bump python-ecobee-api to 0.2.8 (#44866) 2021-01-06 14:26:32 +01:00
jjlawren b6d323b008 Fix Plex media summary attribute (#44863) 2021-01-06 14:26:28 +01:00
Martin Hjelmare 88eac0be85 Bump pytradfri to 7.0.6 (#44661) 2021-01-06 14:26:19 +01:00
jjlawren d3d66c2e27 Fix Plex media summary attribute (#44863) 2021-01-06 14:12:19 +01:00
dependabot[bot] 2e864ca435 Bump codecov/codecov-action from v1.2.0 to v1.2.1 (#44869)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.2.0 to v1.2.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.2.0...e156083f13aff6830c92fc5faa23505779fbf649)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-06 14:05:23 +01:00
Erik Montnemery 72e6d58a99 Bump pychromecast to 7.7.2 (#44871) 2021-01-06 13:41:21 +01:00
treylok 1c2f88c500 Bump python-ecobee-api to 0.2.8 (#44866) 2021-01-06 13:40:24 +01:00
Erik Montnemery 93ae65d704 Improve MQTT number test coverage (#44870) 2021-01-06 13:27:05 +01:00
JeromeHXP 1a65ab0b80 Address late review of ondilo_ico (#44837)
* Updates following comments in PR 44728

* Make all api calls in same thread context

* Set API as parameter to get_all_pools_data

* extract pools data retrieval function to api class
2021-01-06 12:36:39 +01:00
Erik Montnemery 02bfc68842 Support dynamic Google Cast groups (#44484)
* Re-add support for dynamic groups

* Add tests

* Add support for manufacturer

* Refactor support for dynamic groups

* Bump pychromecast to 7.7.0

* Bump pychromecast to 7.7.1

* Tweak tests

* Apply review suggestion
2021-01-06 09:23:18 +01:00
Diogo Gomes f18880686c Add MQTT Number (#44739)
* Initial Commit

* initial commit

* add discovery and tests

* increase coverage

* address review

* catchup with reality
2021-01-06 08:27:46 +01:00
Franck Nijhof c258c2653f Bumped version to 2021.1.0b3 2021-01-05 23:31:18 +01:00
Bram Kragten 587676f436 Update frontend to 20201229.1 (#44861) 2021-01-05 23:31:00 +01:00
Franck Nijhof 2ee50a4d54 Fix Canary doing I/O in event loop (#44854) 2021-01-05 23:30:55 +01:00
Finbarr Brady fdce5878c6 Bump openwebifpy version: 3.1.6 → 3.2.7 (#44847) 2021-01-05 23:30:51 +01:00
Paulus Schoutsen 0b8251d9a1 Make Alexa custom ID unique (#44839)
* Make Alexa custom ID unique

* Lint

* Lint
2021-01-05 23:30:48 +01:00
Andre Lengwenus e4a84bb1c3 Bump pypck to 0.7.8 (#44834) 2021-01-05 23:30:44 +01:00
TheJulianJES 9396d9db5f Implement color mode for ZHA light polling (#44829) 2021-01-05 23:30:41 +01:00
J. Nick Koston b3fda469cf Fix zeroconf outgoing dns compression corruption for large packets (#44828) 2021-01-05 23:30:37 +01:00
Bram Kragten cc57dd9534 Update frontend to 20201229.1 (#44861) 2021-01-05 23:27:35 +01:00
Paulus Schoutsen 009663602a Avoid Ps4 doing I/O during tests (#44845) 2021-01-05 21:12:14 +01:00
Franck Nijhof 34161f3ff6 Fix Canary doing I/O in event loop (#44854) 2021-01-05 20:55:17 +01:00
mvn23 35edc40537 Fix opentherm_gw firmware version in device registry (#44756) 2021-01-05 20:46:54 +01:00
Paulus Schoutsen f1c116831f Patch Shelly test setting up entry (#44842) 2021-01-05 18:35:54 +01:00
Paulus Schoutsen 69b5176730 Make Alexa custom ID unique (#44839)
* Make Alexa custom ID unique

* Lint

* Lint
2021-01-05 17:35:28 +01:00
Finbarr Brady 16e1046dbc Bump openwebifpy version: 3.1.6 → 3.2.7 (#44847) 2021-01-05 17:22:25 +01:00
Ville Skyttä 67eebce55a Better general/fallback error message and traceback for unknown config errors (#44655)
* Include error repr in config error message is str(error) yields nothing

* Log traceback for config errors we don't have a "friendly" formatter for
2021-01-05 13:55:38 +01:00
Hans Oischinger 853420d972 Add Vicare set mode service (#44563)
* vicare: add set_vicare_mode service

The set_vicare_mode service allows the user to set any of the possible heating
modes of their heating device. Not just the ones that were mapped to
home assistant climate modes.

* vicare: Undo async changes and add heating mode

Useless async changes were undone.

To be able to set the most relevant modes the set_vicare_mode
shall be able to also set the heating mode (without domestic
hot water)

* Extract kwarg and undo some more async changes

Currectly extract the service argument

Adapt according to review

* Lint fixes

* Replace kwargs with single arg

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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-05 12:12:31 +01:00
Shane Qi c654476e24 Add support for reordering Shopping List Items via Drag and Drop (#41585)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-05 11:24:30 +01:00
Andre Lengwenus addafd517f Bump pypck to 0.7.8 (#44834) 2021-01-05 10:01:34 +01:00
dependabot[bot] 106252ea21 Bump actions/upload-artifact from v2.2.1 to v2.2.2 (#44835)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v2.2.1 to v2.2.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.2.1...e448a9b857ee2131e752b06002bf0e093c65e571)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-05 09:51:48 +01:00
dependabot[bot] 0c85ed1385 Bump codecov/codecov-action from v1.1.1 to v1.2.0 (#44836)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.1.1 to v1.2.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.1.1...a92c414703a4bba586f6df7fcc885c9d0bdff772)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-05 09:51:19 +01:00
Allen Porter 6cd18971b1 Propose an integration quality for nest SDM integration (#44755)
* Propose the nest SDM integration is silver quality

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-01-04 19:45:33 -08:00
TheJulianJES 86154744e4 Implement color mode for ZHA light polling (#44829) 2021-01-04 21:57:05 -05:00
Ville Skyttä 65e56d03bf Complete device and entity registry type hints (#44406) 2021-01-05 02:03:16 +01:00
chpego d315ab2cf5 Bump caldav version to 0.7.1 (#44815)
* bump caldav version 0.7.1

* Update requirements_all.txt

* Update requirements_test_all.txt
2021-01-04 16:49:01 -07:00
Mike Keesey 60a1948ab0 Generate switches for harmony activities automatically (#42331)
* Adding switch code for harmony activities

* Working on-off

* Removing poll code for now

* Async updates for current activity

* Update our state based on events

* Notifications we got connected or disconnected

* Remove unncessary constructor arg

* Initial switch tests

* Additional tests for switch transitions

* Test transitions for availability

* Testing switch state changes

* Tests passing

* Final tests

* Updating manifest.

* Correctly mock the return value from a call to the library

* Adding new subscriber classes

* Update class name and location

* Got the refactor working locally.

* Tests passing

* Tracking state changes

* Remove write_to_config_file - this appears to never be read.

It was added far back in the past to account for a harmony library
change, but nothing ever reads that path.

Removing that side effect from tests is a pain - avoid the side effect
completely.

* Connection changes tested

* Clean up temporary code

* Update .coveragerc for harmony component

Specifically exclude untested files instead of the whole module

* Fix linting

* test sending activity change commands by id

* Improving coverage

* Testing channel change commands

* Splitting subscriber logic into it's own class

* Improve coverage and tighten up .coveragerc

* Test cleanups.

* re-add config file writing for harmony remote

* Create fixture for the mock harmonyclient

* Reduce duplication in subscription callbacks

* use async_run_job to call callbacks

* Adding some tests for async behaviors with subscribers.

* async_call_later for delay in marking remote unavailable

* Test disconnection handling in harmony remote

* Early exit if activity not specified

* Use connection state mixin

* Lint fix after rebase

* Fix isort

* super init for ConnectionStateMixin

* Adding @mkeesey to harmony CODEOWNERS
2021-01-04 13:21:14 -10:00
Maciej Bieniek 2e50c1be8e Add nearest method to get data for Airly integration (#44288)
* Add nearest method

* Add tests

* Move urls to consts

* Simplify config flow

* Fix tests

* Update tests

* Use in instead get

* Fix AirlyError message in tests

* Fix manual update entity tests

* Clean up tests

* Fix after rebase

* Increase test coverage

* Format the code

* Fix after rebase
2021-01-04 23:14:45 +01:00
Shay Levy 76537305e2 Add logbook and device trigger platforms to Shelly (#44020)
* Add logbook and device trigger platforms to Shelly

Add `logbook` platform for describing “shelly.click” event
Add `device_trigger` platform for adding automation based on click events:

Example of logbook event:
Shelly 'single' click event for Test I3 channel 3 was fired.
(Test I3 is the name of the device)

Example of automation triggers:
First button triple clicked
First button long clicked and then single clicked
First button double clicked
First button long clicked
First button single clicked and then long clicked
First button single clicked
Second button triple clicked
..
Second button single clicked

* Fix codespell

* Remove pylint added for debug

* Add tests

* Rebase

* Fix Rebase & Apply PR review suggestions

Fix tests after rebasing
Use `INPUTS_EVENTS_DICT` for input triggers
Apply PR suggestions
2021-01-04 23:10:42 +01:00
J. Nick Koston 773d95251e Fix zeroconf outgoing dns compression corruption for large packets (#44828) 2021-01-04 21:18:54 +01:00
Aaron Bach 7657a5c901 Move RainMachine services to entity services (#44139) 2021-01-04 20:01:14 +01:00
JeromeHXP cd756f20b1 Updated Ondilo translation files to remove title (#44824) 2021-01-04 19:57:25 +01:00
Joakim Sørensen 766f89f338 Adjust system info for lovelace with multiple dashboards (#44796) 2021-01-04 18:02:46 +01:00
JeromeHXP de780c6d35 Add Ondilo ico integration (#44728)
* First implementationof Ondilo component support

* Update manifest toadd pypi pkg dependency

* Update entities name and corrected refresh issue

* Changed percentage unit name

* Corrected merge issues

* Updated coveragerc

* cleaned up code and corrected config flow tests

* Code cleanup and added test for exisitng entry

* Changes following PR comments:
- Inherit CoordinatorEntity instead of Entity
- Merged pools blocking calls into one
- Renamed devices vars to sensors
- Check supported sensor types
- Stop relying on array index position for pools
- Stop relying on attribute position in dict for sensors

* Corrected unit test

* Reformat sensor type check
2021-01-04 08:09:01 -08:00
Allen Porter c92353088c Fix Fan support in nest climate by adding HVAC_MODE_FAN_ONLY support (#44203)
* Add HVAC_MODE_FAN_ONLY to nest climate

* Remove unreachable code

* Fix HVAC_FAV_ONLY bug; must also turn off hvac
2021-01-04 16:43:41 +01:00
Joakim Plate c1027cace6 Add device entry id to events (#44407)
* Add device entry id to events

* Only add device id if device is known for now
2021-01-04 15:31:10 +01:00
On Freund e9f7e67f4c Try to fix flaky Risco test (#44788) 2021-01-04 15:23:47 +01:00
Simone Chemelli 3a32e16f4d Add myself to codeowners for Shelly (#44814) 2021-01-04 14:14:09 +01:00
Simone Chemelli f07bf6a88e Cleanup timeouts values for Shelly (#44790)
* Updated timeouts

* Small cleanup

* Fix + small cleanup of test code
2021-01-04 14:04:40 +01:00
Franck Nijhof e4fcc9c692 Bumped version to 2021.1.0b2 2021-01-04 13:17:44 +01:00
Matthias Alphart 5f91f14a49 Fix knx.send service not accepting floats (#44802) 2021-01-04 13:17:02 +01:00
Bob Matcuk f42ce2b0d1 Fix bug with blink auth flow (#44769) 2021-01-04 13:16:59 +01:00
Pascal Vizeli 506fdc877a Update docker base image 2021.01.0 (#44761) 2021-01-04 13:16:55 +01:00
J. Nick Koston f33c1332b9 Add index to old_state_id column for postgres and older databases (#44757)
* Add index to old_state_id column for older databases

The schema was updated in #43610 but the index was not
added on migration.

* Handle postgresql missing ondelete

* create index first
2021-01-04 13:16:52 +01:00
Maciej Bieniek 8da79479d3 Change rest sensors update interval for Shelly Motion (#44692)
* Change rest sensors update interval for Shelly Motion

* Cleaning

* Fix typo

* Remove unnecessary parentheses
2021-01-04 13:16:46 +01:00
Paulus Schoutsen 92e354ca38 Guard unbound var for DSMR (#44673) 2021-01-04 13:16:40 +01:00
Maciej Bieniek f771d8ff14 Change rest sensors update interval for Shelly Motion (#44692)
* Change rest sensors update interval for Shelly Motion

* Cleaning

* Fix typo

* Remove unnecessary parentheses
2021-01-04 13:14:07 +01:00
Pascal Vizeli a7a4875f52 Add more debug details to running timeouts (#43644)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-01-04 13:10:39 +01:00
Kendell R 5b67030c26 Change WHITELIST to ALLOWLIST for websockets (#44766) 2021-01-04 12:40:03 +01:00
Bob Matcuk 0cff069c98 Fix bug with blink auth flow (#44769) 2021-01-04 12:33:34 +01:00
Erik Montnemery 34bd70aee6 Fix race when handling MQTT discovery messages (#44730)
* Fix race when handling MQTT discovery messages

* Lint

* retrigger checks
2021-01-04 12:28:17 +01:00
Ville Skyttä 43474762b2 Drop remaining Python < 3.8 support (#44743)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-01-04 11:47:29 +01:00
Daniel Shokouhi 134db3f710 Bump pyobihai (#44768) 2021-01-04 11:46:58 +01:00
J. Nick Koston 12af87bc6e Add index to old_state_id column for postgres and older databases (#44757)
* Add index to old_state_id column for older databases

The schema was updated in #43610 but the index was not
added on migration.

* Handle postgresql missing ondelete

* create index first
2021-01-04 10:51:44 +01:00
Greg Dowling 3c62c21991 Bumo pyroon version to 0.0.30 (#44800) 2021-01-04 10:31:13 +01:00
Oliver 805a9bcb97 Update to denonavr version 0.9.10 (#44791) 2021-01-04 09:59:08 +01:00
dependabot[bot] e9d2f583b6 Bump dessant/lock-threads from v2.0.1 to v2.0.3 (#44806)
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from v2.0.1 to v2.0.3.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dessant/lock-threads/compare/v2.0.1...486f7380c15596f92b724e4260e4981c68d6bde6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-04 09:43:43 +01:00
Matthias Alphart ad4804f38a Fix knx.send service not accepting floats (#44802) 2021-01-04 04:49:29 +01:00
Álvaro Fernández Rojas ec926105a0 Bump PyTado to 0.10.0 (#44770)
* Bump PyTado to v0.10.0

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Tado: switch to getDeviceInfo

This function has been introduced in version 0.10.0 of PyTado.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>

* Tado: update tests

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2021-01-04 03:53:15 +01:00
Matthias Alphart 2bd8ee34f4 Update xknx to 0.16.0 (#44749)
* update xknx to 0.16.0

* fix telegram in knx_event and knx.send service

* fix knx.send to not coerce floats to int

fixes #44792
also enables strings to be sent

* Revert "fix knx.send to not coerce floats to int"

This reverts commit ac40fb53f965b3c0e4a801f1a0557b49be884551.
2021-01-04 03:07:12 +01:00
migube 10b5912901 Reconnect mochad light on on/off command (#44507) 2021-01-03 20:01:05 +01:00
Will Hughes 1fc4284a29 Add service to lock/unlock Sure Petcare pet flaps (#44557)
* Add service to lock/unlock Sure Petcare pet flaps

Adds a service to the Sure Petcare pet flaps to allow lock, unlocking,
locking in and locking out pets using the pet flap

* Linting

* Changes from code review
2021-01-03 13:43:16 +01:00
dzukero 2ed7b90027 Add RFXtrx Rfy venetian blinds tilt control (#44309)
* Add tilt control for RFXtrx Rfy venetian blinds

* Update Rfy cover test

* Update the required version of pyRFXtrx

* Update required pyRFXtrx version to 0.26.1

* Revert "Update required pyRFXtrx version to 0.26.1"

This reverts commit d54f1645d5ec8bb67f64b0f9b1a1bb498f275739.

* Revert "Update the required version of pyRFXtrx"

This reverts commit ac36d6532623ee75347797f1c3ac0cc52aaa05fd.

* Update required version of pyRFXtrx to 0.26.1

* @dzukero
Update required version of pyRFXtrx to 0.26.1

* Make requested changes from review

* Fix isort

* Remove set tilt position support

* Remove set tilt position support per review
2021-01-03 12:59:22 +01:00
badguy99 cc21639f00 Fix Soma integration reload (#44750)
* fix async_unload_entry so that component reload works from GUI

* update to use asyncio based on review feedback
2021-01-02 18:38:45 -08:00
Joakim Plate 3de0610909 Support google assistant stopping for assumed state covers (#44266)
* Support stopping for assumed state covers

* Adjust black formatting
2021-01-02 23:01:20 +01:00
Álvaro Fernández Rojas 067f2d0098 Add tado zone binary sensors (#44576)
These should be binary sensors.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2021-01-02 13:35:59 +01:00
Pascal Vizeli 40cbe597be Update docker base image 2021.01.0 (#44761) 2021-01-02 12:07:52 +01:00
Will Hughes a6e474c7c9 Update surepy to v0.4.0 (#44556)
* Update surepy to v0.4.0

* Clarify pylint disable

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-01-02 03:52:33 +01:00
Allen Porter 321c0a87ae Resolve nest pub/sub subscriber token refresh issues (#44686) 2021-01-02 01:51:01 +01:00
Paulus Schoutsen a2ca08905f Guard unbound var for DSMR (#44673) 2021-01-02 01:34:10 +01:00
Franck Nijhof 508d33a220 Remove deprecated PTVSD integration (#44748) 2021-01-01 23:36:15 +01:00
Franck Nijhof 65cf2fcb6f Drop asynctest (#44746) 2021-01-01 22:31:56 +01:00
Franck Nijhof 79aad3f07b Bumped version to 2021.1.0b1 2021-01-01 21:14:18 +01:00
Robert Svensson e781e1b26c Bump H11 library to support non RFC line endings (#44735) 2021-01-01 21:13:43 +01:00
J. Nick Koston c60390a52a Fix templates for rest notify (#44724) 2021-01-01 21:13:40 +01:00
J. Nick Koston af1d46aa6c Fix rest notify GET without params configured (#44723) 2021-01-01 21:13:36 +01:00
J. Nick Koston 6d33f6a115 Fix script wait templates with now/utcnow (#44717) 2021-01-01 21:13:32 +01:00
Allen Porter 39b9821d29 Fix broken test test_auto_purge in recorder (#44687)
* Fix failing test due to edge-of-2021 bug

* Rewrite test_auto_purge to make the intent more clear
2021-01-01 21:13:28 +01:00
Alexei Chetroi 864546201e Bump up ZHA dependencies (#44680)
- zigpy == 0.29.0
- zigpy_deconz == 0.11.1
- zha-quirks == 0.0.51
2021-01-01 21:13:25 +01:00
Paulus Schoutsen 1c2e4226dc Zeroconf lowercase (#44675) 2021-01-01 21:13:21 +01:00
Allen Porter 34a6b4deb0 Fix legacy nest api binary_sensor initialization (#44674) 2021-01-01 21:13:18 +01:00
Paulus Schoutsen 2b1df2e63f Catch Shelly zeroconf types with uppercase too (#44672)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-01-01 21:13:14 +01:00
Diogo Gomes 610ee24bb1 always sync unit_of_measurement (#44670) 2021-01-01 21:13:10 +01:00
Daniel Lintott 3cd97398aa Bump zm-py version to 0.5.2 (#44658) 2021-01-01 21:13:07 +01:00
David F. Mulcahey 7d99a35547 Bump ZHA quirks version to 0.0.50 (#44650) 2021-01-01 21:13:04 +01:00
Mark Allanson 470fe887a5 Upgrade canary integration to use py-canary 0.5.1 (#44645)
Fixes #35569
2021-01-01 21:13:00 +01:00
Sian 1b26f6e8e0 Correct Dyson climate fan auto mode (#44569)
Co-authored-by: Justin Gauthier <justin@justin-tech.com>
2021-01-01 21:12:56 +01:00
Maciej Bieniek 8a689a0105 Add motion binary sensor (#44445) 2021-01-01 21:12:52 +01:00
Raman Gupta e65903822d Suppress vizio logging API call failures to prevent no-op logs (#44388) 2021-01-01 21:12:48 +01:00
J. Nick Koston 335aceedfb Update py-august to 0.25.2 to fix august token refreshes (#40109)
* Update py-august to 0.26.0 to fix august token refreshes

* bump version
2021-01-01 21:12:44 +01:00
Paulus Schoutsen 2b0556520b Catch Shelly zeroconf types with uppercase too (#44672)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-01-01 20:54:43 +01:00
Martin Hjelmare c7fa98211d Bump locationsharinglib to 4.1.5 (#44742) 2021-01-01 19:48:33 +01:00
J. Nick Koston 661eb0338a Fix templates for rest notify (#44724) 2021-01-01 19:28:20 +01:00
Robert Svensson c4fbfc25e3 Bump H11 library to support non RFC line endings (#44735) 2021-01-01 18:39:59 +01:00
Franck Nijhof 70d2c37131 Upgrade pre-commit to 2.9.3 (#44740) 2021-01-01 18:10:28 +01:00
Franck Nijhof 5c634ac8bb Upgrade debugpy to 1.2.1 (#44737) 2021-01-01 17:59:05 +01:00
Franck Nijhof ea10f96bf7 Upgrade sentry-sdk to 0.19.5 (#44738) 2021-01-01 17:58:13 +01:00
Daniel Perna e2e79aba4e Update pyhomematic to 0.1.71 (#44732) 2021-01-01 17:20:55 +01:00
Sian 52e1aad008 Correct Dyson climate fan auto mode (#44569)
Co-authored-by: Justin Gauthier <justin@justin-tech.com>
2021-01-01 16:06:36 +01:00
Chris 176415b045 Fix AccuWeather condition mapping (#44710) 2021-01-01 15:40:28 +01:00
Franck Nijhof 1f0a6b178e Fix Gree climate turn on/off (#44731) 2021-01-01 15:11:25 +01:00
Clifford Roche f0e96f739b Add turn_on and turn_off to gree climate component (#43207) 2021-01-01 13:58:38 +01:00
Fabian Affolter ddfc3d6d8e Upgrade volkszaehler to 0.2.1 (#44703) 2021-01-01 13:49:57 +01:00
Fabian Affolter 168b3ae6af Upgrade alpha-vantage to 2.3.1 (#44705) 2021-01-01 13:48:24 +01:00
Franck Nijhof 2f486543df Drop Python 3.7 support (#43805) 2021-01-01 13:47:01 +01:00
Raman Gupta b651f63ef0 Suppress vizio logging API call failures to prevent no-op logs (#44388) 2021-01-01 13:35:05 +01:00
Allen Porter 051f6c0e72 Increase test coverage for Nest SDM integration (#44718) 2021-01-01 13:31:18 +01:00
Fabian Affolter c4b11322c8 Updated certifi to >=2020.12.5 (#44701) 2021-01-01 13:07:07 +01:00
J. Nick Koston 2ef25e7414 Fix script wait templates with now/utcnow (#44717) 2021-01-01 13:03:34 +01:00
Franck Nijhof 7415dacec9 Add Python 3.9 to CI (#41373) 2021-01-01 12:51:27 +01:00
J. Nick Koston 94825b3e15 Do not restore unavailable state for august locks (#44722) 2021-01-01 12:44:53 +01:00
J. Nick Koston 681f76b99d Fix rest notify GET without params configured (#44723) 2021-01-01 12:40:08 +01:00
Fabian Affolter 5e0eea21d4 Upgrade pyowm to 3.1.1 (#44706) 2020-12-31 20:02:59 -10:00
Fabian Affolter 99eed915d6 Upgrade slixmpp to 1.6.0 (#44693) 2020-12-31 19:35:42 -10:00
Jason Cronquist 787027958d Use the async_call context in result of call_service (#44458) 2020-12-31 16:15:39 -10:00
hung2kgithub db6bd22fc9 Add Chinese (Hong Kong) to Google Cloud TTS (#44689) 2021-01-01 01:59:42 +01:00
Oncleben31 41ebfcdc9e Add device info to Météo-France (#44457) 2021-01-01 01:51:03 +01:00
Fabian Affolter 12b7b2098d Upgrade sqlalchemy to 1.3.22 (#44698) 2020-12-31 14:28:15 -10:00
Glenn Waters edee0682ba Bump upb-lib to 0.4.12 (#44721) 2021-01-01 00:48:44 +01:00
Fabian Affolter 61f137c7c6 Upgrade pytz to >=2020.5 (#44702) 2021-01-01 00:38:38 +01:00
Glenn Waters 74b480e9d4 Bump elkm1-lib to 0.8.10 (#44714) 2020-12-31 23:44:04 +01:00
Allen Porter f1dff973dc Fix broken test test_auto_purge in recorder (#44687)
* Fix failing test due to edge-of-2021 bug

* Rewrite test_auto_purge to make the intent more clear
2020-12-31 12:04:12 -10:00
Allen Porter fe9a254017 Fix legacy nest api binary_sensor initialization (#44674) 2020-12-31 19:22:24 +01:00
Ville Skyttä 1c8fbc7e6a Upgrade codespell to 2.0.0 (#44695)
* Upgrade codespell to 2.0.0

* Fix newly found spelling errors
2020-12-31 19:14:07 +01:00
Mike Keesey cdda5900e5 Upgrade pubnubsub-handler to 1.0.9 (#44542)
This resolves an error thrown on shutdown of the wink component
2020-12-31 18:48:36 +01:00
Alexei Chetroi 64dd748330 Bump up ZHA dependencies (#44680)
- zigpy == 0.29.0
- zigpy_deconz == 0.11.1
- zha-quirks == 0.0.51
2020-12-31 14:07:15 +01:00
Franck Nijhof 408da3600b Upgrade feedparser to 6.0.2 (#44683) 2020-12-31 12:00:43 +01:00
Martin Hjelmare 4bde0640d6 Bump pytradfri to 7.0.6 (#44661) 2020-12-31 01:18:58 +01:00
Mark Allanson 1428c403ba Upgrade canary integration to use py-canary 0.5.1 (#44645)
Fixes #35569
2020-12-31 01:16:53 +01:00
Paulus Schoutsen c7bf7b32a2 Zeroconf lowercase (#44675) 2020-12-31 01:06:26 +01:00
Diogo Gomes b290a8b5a1 always sync unit_of_measurement (#44670) 2020-12-31 00:39:14 +01:00
Maciej Bieniek 687f90e164 Add motion binary sensor (#44445) 2020-12-31 00:02:56 +01:00
Ville Skyttä da66a4e933 Device automation config error message improvements (#44656)
Refs #44654, #44655
2020-12-31 00:02:14 +01:00
J. Nick Koston e2964ca878 Update py-august to 0.25.2 to fix august token refreshes (#40109)
* Update py-august to 0.26.0 to fix august token refreshes

* bump version
2020-12-30 23:10:42 +01:00
Franck Nijhof b1bb0d12c9 Upgrade vsure to 1.6.1 (#44657) 2020-12-30 23:06:30 +01:00
J.P. Krauss e37bb51320 Add AirNow Integration (#40091) 2020-12-30 20:25:57 +01:00
Daniel Lintott 15a4e1e1b3 Bump zm-py version to 0.5.2 (#44658) 2020-12-30 10:15:27 -08:00
Fabian Affolter 2e62e0661b Upgrade colorlog to 4.6.2 (#44652) 2020-12-30 17:12:51 +01:00
zewelor 16ddbb95f4 Add yeelight service to enable disable music mode (#44533)
* Add service to enable / disable music mode

* Black reformat

* Update test

* Fix tests

* Revert consts cleanup

* Use entity method as service call

* Use ATTR for service call

* Sort

* Add tests

* Fix isort

* Fix print

* Black
2020-12-30 17:00:28 +01:00
David F. Mulcahey a6c83cc46a Bump ZHA quirks version to 0.0.50 (#44650) 2020-12-30 09:11:08 -05:00
Fabian Affolter e2e07cf42e Upgrade sendgrid to 6.4.8 (#44646) 2020-12-30 13:33:13 +01:00
Franck Nijhof 4b057101c5 Bumped version to 2021.2.0dev0 (#44647) 2020-12-30 13:27:12 +01:00
1780 changed files with 53505 additions and 14148 deletions
+26 -16
View File
@@ -29,6 +29,8 @@ omit =
homeassistant/components/agent_dvr/camera.py
homeassistant/components/agent_dvr/const.py
homeassistant/components/agent_dvr/helpers.py
homeassistant/components/airnow/__init__.py
homeassistant/components/airnow/sensor.py
homeassistant/components/airvisual/__init__.py
homeassistant/components/airvisual/air_quality.py
homeassistant/components/airvisual/sensor.py
@@ -142,7 +144,7 @@ omit =
homeassistant/components/co2signal/*
homeassistant/components/coinbase/*
homeassistant/components/comed_hourly_pricing/sensor.py
homeassistant/components/comfoconnect/*
homeassistant/components/comfoconnect/fan.py
homeassistant/components/concord232/alarm_control_panel.py
homeassistant/components/concord232/binary_sensor.py
homeassistant/components/control4/__init__.py
@@ -213,7 +215,11 @@ omit =
homeassistant/components/ecobee/notify.py
homeassistant/components/ecobee/sensor.py
homeassistant/components/ecobee/weather.py
homeassistant/components/econet/*
homeassistant/components/econet/__init__.py
homeassistant/components/econet/binary_sensor.py
homeassistant/components/econet/const.py
homeassistant/components/econet/sensor.py
homeassistant/components/econet/water_heater.py
homeassistant/components/ecovacs/*
homeassistant/components/edl21/*
homeassistant/components/eddystone_temperature/sensor.py
@@ -298,8 +304,8 @@ omit =
homeassistant/components/folder_watcher/*
homeassistant/components/foobot/sensor.py
homeassistant/components/fortios/device_tracker.py
homeassistant/components/foscam/__init__.py
homeassistant/components/foscam/camera.py
homeassistant/components/foscam/const.py
homeassistant/components/foursquare/*
homeassistant/components/free_mobile/notify.py
homeassistant/components/freebox/__init__.py
@@ -308,6 +314,9 @@ omit =
homeassistant/components/freebox/sensor.py
homeassistant/components/freebox/switch.py
homeassistant/components/fritz/device_tracker.py
homeassistant/components/fritzbox_callmonitor/__init__.py
homeassistant/components/fritzbox_callmonitor/const.py
homeassistant/components/fritzbox_callmonitor/base.py
homeassistant/components/fritzbox_callmonitor/sensor.py
homeassistant/components/fritzbox_netmonitor/sensor.py
homeassistant/components/fronius/sensor.py
@@ -356,7 +365,10 @@ omit =
homeassistant/components/hangouts/hangouts_bot.py
homeassistant/components/hangouts/hangups_utils.py
homeassistant/components/harman_kardon_avr/media_player.py
homeassistant/components/harmony/*
homeassistant/components/harmony/const.py
homeassistant/components/harmony/data.py
homeassistant/components/harmony/remote.py
homeassistant/components/harmony/util.py
homeassistant/components/haveibeenpwned/sensor.py
homeassistant/components/hdmi_cec/*
homeassistant/components/heatmiser/climate.py
@@ -578,13 +590,7 @@ omit =
homeassistant/components/neato/vacuum.py
homeassistant/components/nederlandse_spoorwegen/sensor.py
homeassistant/components/nello/lock.py
homeassistant/components/nest/__init__.py
homeassistant/components/nest/api.py
homeassistant/components/nest/binary_sensor.py
homeassistant/components/nest/camera.py
homeassistant/components/nest/climate.py
homeassistant/components/nest/legacy/*
homeassistant/components/nest/sensor.py
homeassistant/components/netatmo/__init__.py
homeassistant/components/netatmo/api.py
homeassistant/components/netatmo/camera.py
@@ -629,6 +635,11 @@ omit =
homeassistant/components/omnilogic/__init__.py
homeassistant/components/omnilogic/common.py
homeassistant/components/omnilogic/sensor.py
homeassistant/components/ondilo_ico/__init__.py
homeassistant/components/ondilo_ico/api.py
homeassistant/components/ondilo_ico/const.py
homeassistant/components/ondilo_ico/oauth_impl.py
homeassistant/components/ondilo_ico/sensor.py
homeassistant/components/onkyo/media_player.py
homeassistant/components/onvif/__init__.py
homeassistant/components/onvif/base.py
@@ -690,8 +701,6 @@ omit =
homeassistant/components/pjlink/media_player.py
homeassistant/components/plaato/*
homeassistant/components/plex/media_player.py
homeassistant/components/plex/models.py
homeassistant/components/plex/sensor.py
homeassistant/components/plum_lightpad/light.py
homeassistant/components/pocketcasts/sensor.py
homeassistant/components/point/*
@@ -706,7 +715,6 @@ omit =
homeassistant/components/prowl/notify.py
homeassistant/components/proxmoxve/*
homeassistant/components/proxy/camera.py
homeassistant/components/ptvsd/*
homeassistant/components/pulseaudio_loopback/switch.py
homeassistant/components/pushbullet/notify.py
homeassistant/components/pushbullet/sensor.py
@@ -790,11 +798,9 @@ omit =
homeassistant/components/shodan/sensor.py
homeassistant/components/shelly/__init__.py
homeassistant/components/shelly/binary_sensor.py
homeassistant/components/shelly/cover.py
homeassistant/components/shelly/entity.py
homeassistant/components/shelly/light.py
homeassistant/components/shelly/sensor.py
homeassistant/components/shelly/switch.py
homeassistant/components/shelly/utils.py
homeassistant/components/sht31/sensor.py
homeassistant/components/sigfox/sensor.py
@@ -837,7 +843,8 @@ omit =
homeassistant/components/soma/cover.py
homeassistant/components/soma/sensor.py
homeassistant/components/somfy/*
homeassistant/components/somfy_mylink/*
homeassistant/components/somfy_mylink/__init__.py
homeassistant/components/somfy_mylink/cover.py
homeassistant/components/sonos/*
homeassistant/components/sony_projector/switch.py
homeassistant/components/spc/*
@@ -1090,6 +1097,9 @@ omit =
homeassistant/components/zoneminder/*
homeassistant/components/supla/*
homeassistant/components/zwave/util.py
homeassistant/components/zwave_js/discovery.py
homeassistant/components/zwave_js/light.py
homeassistant/components/zwave_js/sensor.py
[report]
# Regexes for lines to exclude from consideration
+20 -23
View File
@@ -11,7 +11,7 @@ on:
env:
CACHE_VERSION: 1
DEFAULT_PYTHON: 3.7
DEFAULT_PYTHON: 3.8
PRE_COMMIT_HOME: ~/.cache/pre-commit
jobs:
@@ -521,13 +521,12 @@ jobs:
needs: prepare-tests
strategy:
matrix:
python-version: [3.7]
python-version: [3.8]
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -585,13 +584,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8]
python-version: [3.8, 3.9]
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -605,10 +603,13 @@ jobs:
${{ env.CACHE_VERSION}}-${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('requirements_test.txt') }}-${{ hashFiles('requirements_all.txt') }}
${{ env.CACHE_VERSION}}-${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('requirements_test.txt') }}
${{ env.CACHE_VERSION}}-${{ runner.os }}-venv-${{ matrix.python-version }}-
- name:
Create full Python ${{ matrix.python-version }} virtual environment
- name: Create full Python ${{ matrix.python-version }} virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
# Temporary addition of cmake, needed to build some Python 3.9 packages
apt-get update
apt-get -y install cmake
python -m venv venv
. venv/bin/activate
pip install -U "pip<20.3" setuptools wheel
@@ -622,13 +623,12 @@ jobs:
needs: prepare-tests
strategy:
matrix:
python-version: [3.7]
python-version: [3.8]
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -657,13 +657,12 @@ jobs:
needs: prepare-tests
strategy:
matrix:
python-version: [3.7]
python-version: [3.8]
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -692,15 +691,14 @@ jobs:
strategy:
matrix:
group: [1, 2, 3, 4]
python-version: [3.7, 3.8]
python-version: [3.8, 3.9]
name: >-
Run tests Python ${{ matrix.python-version }} (group ${{ matrix.group }})
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -741,7 +739,7 @@ jobs:
-p no:sugar \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.2.1
uses: actions/upload-artifact@v2.2.2
with:
name: coverage-${{ matrix.python-version }}-group${{ matrix.group }}
path: .coverage
@@ -755,13 +753,12 @@ jobs:
needs: pytest
strategy:
matrix:
python-version: [3.7]
python-version: [3.8]
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2
- name:
Restore full Python ${{ matrix.python-version }} virtual environment
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2
with:
@@ -785,4 +782,4 @@ jobs:
coverage report --fail-under=94
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.1.1
uses: codecov/codecov-action@v1.2.1
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2.0.1
- uses: dessant/lock-threads@v2.0.3
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: "30"
+44 -25
View File
@@ -4,23 +4,27 @@ name: Stale
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
# The 90 day stale policy
# Used for: Everything (unless 30 day policy below beats it)
- name: 90 days stale policy
uses: actions/stale@v3.0.14
# Used for:
# - Issues & PRs
# - No PRs marked as no-stale
# - No issues marked as no-stale or help-wanted
- name: 90 days stale issues & PRs policy
uses: actions/stale@v3.0.15
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90
days-before-close: 7
operations-per-run: 25
operations-per-run: 150
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "no-stale,Help%20wanted,help-wanted"
exempt-issue-labels: "no-stale,help-wanted"
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
high number of incoming GitHub notifications, we have to clean some
@@ -43,22 +47,48 @@ jobs:
Thank you for your contributions.
# The 30 day stale policy
# The 30 day stale policy for PRS
# Used for:
# - Issues that are pending more information (incomplete issues)
# - PRs that are not marked as new-integration
- name: 30 days stale policy
uses: actions/stale@v3.0.14
# - PRs
# - No PRs marked as no-stale or new-integrations
# - No issues (-1)
- name: 30 days stale PRs policy
uses: actions/stale@v3.0.15
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# PRs have a CLA signed label, we can misuse it to apply this policy
only-labels: "cla-signed,needs-more-information"
days-before-stale: 30
days-before-close: 7
operations-per-run: 5
days-before-issue-close: -1
operations-per-run: 50
remove-stale-when-updated: true
stale-pr-label: "stale"
# Exempt new integrations, these often take more time.
# They will automatically be handled by the 90 day version above.
exempt-pr-labels: "no-stale,new-integration"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
# The 30 day stale policy for issues
# Used for:
# - Issues that are pending more information (incomplete issues)
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@v3.0.15
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
only-labels: "needs-more-information"
days-before-stale: 14
days-before-close: 7
days-before-pr-close: -1
operations-per-run: 50
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "no-stale,Help%20wanted,help-wanted"
exempt-issue-labels: "no-stale,help-wanted"
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
high number of incoming GitHub notifications, we have to clean some
@@ -71,14 +101,3 @@ jobs:
This issue has now been marked as stale and will be closed if no
further activity occurs. Thank you for your contributions.
stale-pr-label: "stale"
# Exempt new integrations, these often take more time.
# They will automatically be handled by the 90 day version above.
exempt-pr-labels: "no-stale,new-integration"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
+4 -3
View File
@@ -3,7 +3,7 @@ repos:
rev: v2.7.2
hooks:
- id: pyupgrade
args: [--py37-plus]
args: [--py38-plus]
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
@@ -13,14 +13,15 @@ repos:
- --quiet
files: ^((homeassistant|script|tests)/.+)?[^/]+\.py$
- repo: https://github.com/codespell-project/codespell
rev: v1.17.1
rev: v2.0.0
hooks:
- id: codespell
args:
- --ignore-words-list=hass,alot,datas,dof,dur,farenheit,hist,iff,ines,ist,lightsensor,mut,nd,pres,referer,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort
- --ignore-words-list=hass,alot,datas,dof,dur,ether,farenheit,hist,iff,ines,ist,lightsensor,mut,nd,pres,referer,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort
- --skip="./.*,*.csv,*.json"
- --quiet-level=2
exclude_types: [csv, json]
exclude: ^tests/fixtures/
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
hooks:
+1 -1
View File
@@ -4,7 +4,7 @@ build:
image: latest
python:
version: 3.7
version: 3.8
setup_py_install: true
requirements_file: requirements_docs.txt
+1 -1
View File
@@ -5,5 +5,5 @@
// https://github.com/microsoft/vscode-python/issues/14067
"python.testing.pytestArgs": ["--no-cov"],
// https://code.visualstudio.com/docs/python/testing#_pytest-configuration-settings
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": false
}
+13 -7
View File
@@ -26,6 +26,7 @@ homeassistant/components/adguard/* @frenck
homeassistant/components/advantage_air/* @Bre77
homeassistant/components/agent_dvr/* @ispysoftware
homeassistant/components/airly/* @bieniu
homeassistant/components/airnow/* @asymworks
homeassistant/components/airvisual/* @bachya
homeassistant/components/alarmdecoder/* @ajschmidt8
homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy
@@ -55,7 +56,6 @@ homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automation/* @home-assistant/core
homeassistant/components/avea/* @pattyland
homeassistant/components/awair/* @ahayworth @danielsjf
homeassistant/components/aws/* @awarecan
homeassistant/components/axis/* @Kane610
homeassistant/components/azure_devops/* @timmo001
homeassistant/components/azure_event_hub/* @eavanvalkenburg
@@ -107,6 +107,7 @@ homeassistant/components/derivative/* @afaucogney
homeassistant/components/device_automation/* @home-assistant/core
homeassistant/components/devolo_home_control/* @2Fake @Shutgun
homeassistant/components/dexcom/* @gagebenne
homeassistant/components/dhcp/* @bdraco
homeassistant/components/digital_ocean/* @fabaff
homeassistant/components/directv/* @ctalkington
homeassistant/components/discogs/* @thibmaek
@@ -119,6 +120,7 @@ homeassistant/components/dweet/* @fabaff
homeassistant/components/dynalite/* @ziv1234
homeassistant/components/eafm/* @Jc2k
homeassistant/components/ecobee/* @marthoc
homeassistant/components/econet/* @vangorra @w1ll1am23
homeassistant/components/ecovacs/* @OverloadUT
homeassistant/components/edl21/* @mtdcr
homeassistant/components/egardia/* @jeroenterheerdt
@@ -179,7 +181,7 @@ homeassistant/components/griddy/* @bdraco
homeassistant/components/group/* @home-assistant/core
homeassistant/components/growatt_server/* @indykoning
homeassistant/components/guardian/* @bachya
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
homeassistant/components/hassio/* @home-assistant/supervisor
homeassistant/components/hdmi_cec/* @newAM
homeassistant/components/heatmiser/* @andylockran
@@ -201,6 +203,7 @@ homeassistant/components/http/* @home-assistant/core
homeassistant/components/huawei_lte/* @scop @fphammerle
homeassistant/components/huawei_router/* @abmantis
homeassistant/components/hue/* @balloob @frenck
homeassistant/components/huisbaasje/* @denniss17
homeassistant/components/humidifier/* @home-assistant/core @Shulyaka
homeassistant/components/hunterdouglas_powerview/* @bdraco
homeassistant/components/hvv_departures/* @vigonotion
@@ -256,7 +259,7 @@ homeassistant/components/luci/* @mzdrale
homeassistant/components/luftdaten/* @fabaff
homeassistant/components/lupusec/* @majuss
homeassistant/components/lutron/* @JonGilmore
homeassistant/components/lutron_caseta/* @swails
homeassistant/components/lutron_caseta/* @swails @bdraco
homeassistant/components/mastodon/* @fabaff
homeassistant/components/matrix/* @tinloaf
homeassistant/components/mcp23017/* @jardiamj
@@ -289,10 +292,10 @@ homeassistant/components/neato/* @dshokouhi @Santobert
homeassistant/components/nederlandse_spoorwegen/* @YarmoM
homeassistant/components/nello/* @pschmitt
homeassistant/components/ness_alarm/* @nickw444
homeassistant/components/nest/* @awarecan @allenporter
homeassistant/components/nest/* @allenporter
homeassistant/components/netatmo/* @cgtobi
homeassistant/components/netdata/* @fabaff
homeassistant/components/nexia/* @ryannazaretian @bdraco
homeassistant/components/nexia/* @bdraco
homeassistant/components/nextbus/* @vividboarder
homeassistant/components/nextcloud/* @meichthys
homeassistant/components/nightscout/* @marciogranzotto
@@ -318,6 +321,7 @@ homeassistant/components/ohmconnect/* @robbiet480
homeassistant/components/ombi/* @larssont
homeassistant/components/omnilogic/* @oliver84 @djtimca @gentoosu
homeassistant/components/onboarding/* @home-assistant/core
homeassistant/components/ondilo_ico/* @JeromeHXP
homeassistant/components/onewire/* @garbled1 @epenet
homeassistant/components/onvif/* @hunterjm
homeassistant/components/openerz/* @misialq
@@ -351,7 +355,6 @@ homeassistant/components/progettihwsw/* @ardaseremet
homeassistant/components/prometheus/* @knyar
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe
homeassistant/components/ps4/* @ktnrg45
homeassistant/components/ptvsd/* @swamp-ig
homeassistant/components/push/* @dgomes
homeassistant/components/pvoutput/* @fabaff
homeassistant/components/pvpc_hourly_pricing/* @azogue
@@ -362,6 +365,7 @@ homeassistant/components/quantum_gateway/* @cisasteelersfan
homeassistant/components/qvr_pro/* @oblogic7
homeassistant/components/qwikswitch/* @kellerza
homeassistant/components/rachio/* @bdraco
homeassistant/components/radiotherm/* @vinnyfuria
homeassistant/components/rainbird/* @konikvranik
homeassistant/components/raincloud/* @vanstinator
homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
@@ -396,7 +400,7 @@ homeassistant/components/seven_segments/* @fabaff
homeassistant/components/seventeentrack/* @bachya
homeassistant/components/sharkiq/* @ajmarks
homeassistant/components/shell_command/* @home-assistant/core
homeassistant/components/shelly/* @balloob @bieniu @thecode
homeassistant/components/shelly/* @balloob @bieniu @thecode @chemelli74
homeassistant/components/shiftr/* @fabaff
homeassistant/components/shodan/* @fabaff
homeassistant/components/sighthound/* @robmarkcole
@@ -420,6 +424,7 @@ homeassistant/components/solarlog/* @Ernst79
homeassistant/components/solax/* @squishykid
homeassistant/components/soma/* @ratsept
homeassistant/components/somfy/* @tetienne
homeassistant/components/somfy_mylink/* @bdraco
homeassistant/components/sonarr/* @ctalkington
homeassistant/components/songpal/* @rytilahti @shenxn
homeassistant/components/sonos/* @cgtobi
@@ -535,6 +540,7 @@ homeassistant/components/zodiac/* @JulienTant
homeassistant/components/zone/* @home-assistant/core
homeassistant/components/zoneminder/* @rohankapoorcom
homeassistant/components/zwave/* @home-assistant/z-wave
homeassistant/components/zwave_js/* @home-assistant/z-wave
# Individual files
homeassistant/components/demo/weather @fabaff
+2 -1
View File
@@ -1,8 +1,9 @@
ARG BUILD_FROM
FROM ${BUILD_FROM}
# Synchronize with homeassistant/core.py:async_stop
ENV \
S6_SERVICES_GRACETIME=60000
S6_SERVICES_GRACETIME=220000
WORKDIR /usr/src
+1 -5
View File
@@ -14,8 +14,6 @@ pr:
resources:
containers:
- container: 37
image: homeassistant/ci-azure:3.7
- container: 38
image: homeassistant/ci-azure:3.8
repositories:
@@ -25,7 +23,7 @@ resources:
endpoint: "home-assistant"
variables:
- name: PythonMain
value: "37"
value: "38"
- name: versionHadolint
value: "v1.17.6"
@@ -150,8 +148,6 @@ stages:
strategy:
maxParallel: 3
matrix:
Python37:
python.container: "37"
Python38:
python.container: "38"
container: $[ variables['python.container'] ]
+2 -2
View File
@@ -60,9 +60,9 @@ stages:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
displayName: 'Use Python 3.7'
displayName: 'Use Python 3.8'
inputs:
versionSpec: '3.7'
versionSpec: '3.8'
- script: pip install twine wheel
displayName: 'Install tools'
- script: python setup.py sdist bdist_wheel
+2 -2
View File
@@ -30,9 +30,9 @@ jobs:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
displayName: 'Use Python 3.7'
displayName: 'Use Python 3.8'
inputs:
versionSpec: '3.7'
versionSpec: '3.8'
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
+5 -5
View File
@@ -1,11 +1,11 @@
{
"image": "homeassistant/{arch}-homeassistant",
"build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:2020.11.2",
"armhf": "homeassistant/armhf-homeassistant-base:2020.11.2",
"armv7": "homeassistant/armv7-homeassistant-base:2020.11.2",
"amd64": "homeassistant/amd64-homeassistant-base:2020.11.2",
"i386": "homeassistant/i386-homeassistant-base:2020.11.2"
"aarch64": "homeassistant/aarch64-homeassistant-base:2021.01.1",
"armhf": "homeassistant/armhf-homeassistant-base:2021.01.1",
"armv7": "homeassistant/armv7-homeassistant-base:2021.01.1",
"amd64": "homeassistant/amd64-homeassistant-base:2021.01.1",
"i386": "homeassistant/i386-homeassistant-base:2021.01.1"
},
"labels": {
"io.hass.type": "core"
+10 -9
View File
@@ -48,7 +48,7 @@ COOLDOWN_TIME = 60
MAX_LOAD_CONCURRENTLY = 6
DEBUGGER_INTEGRATIONS = {"debugpy", "ptvsd"}
DEBUGGER_INTEGRATIONS = {"debugpy"}
CORE_INTEGRATIONS = ("homeassistant", "persistent_notification")
LOGGING_INTEGRATIONS = {
# Set log levels
@@ -307,12 +307,10 @@ def async_enable_logging(
sys.excepthook = lambda *args: logging.getLogger(None).exception(
"Uncaught exception", exc_info=args # type: ignore
)
if sys.version_info[:2] >= (3, 8):
threading.excepthook = lambda args: logging.getLogger(None).exception(
"Uncaught thread exception",
exc_info=(args.exc_type, args.exc_value, args.exc_traceback),
)
threading.excepthook = lambda args: logging.getLogger(None).exception(
"Uncaught thread exception",
exc_info=(args.exc_type, args.exc_value, args.exc_traceback), # type: ignore[arg-type]
)
# Log errors to a file if we have write access to file or config dir
if log_file is None:
@@ -383,7 +381,7 @@ def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
async def _async_log_pending_setups(
domains: Set[str], setup_started: Dict[str, datetime]
hass: core.HomeAssistant, 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:
@@ -395,6 +393,7 @@ async def _async_log_pending_setups(
"Waiting on integrations to complete setup: %s",
", ".join(remaining),
)
_LOGGER.debug("Running timeout Zones: %s", hass.timeout.zones)
async def async_setup_multi_components(
@@ -408,7 +407,9 @@ async def async_setup_multi_components(
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))
log_task = asyncio.create_task(
_async_log_pending_setups(hass, domains, setup_started)
)
await asyncio.wait(futures.values())
log_task.cancel()
errors = [domain for domain in domains if futures[domain].exception()]
@@ -100,13 +100,13 @@ class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator):
self.accuweather = AccuWeather(api_key, session, location_key=self.location_key)
# Enabling the forecast download increases the number of requests per data
# update, we use 32 minutes for current condition only and 64 minutes for
# update, we use 40 minutes for current condition only and 80 minutes for
# current condition and forecast as update interval to not exceed allowed number
# of requests. We have 50 requests allowed per day, so we use 45 and leave 5 as
# of requests. We have 50 requests allowed per day, so we use 36 and leave 14 as
# a reserve for restarting HA.
update_interval = (
timedelta(minutes=64) if self.forecast else timedelta(minutes=32)
)
update_interval = timedelta(minutes=40)
if self.forecast:
update_interval *= 2
_LOGGER.debug("Data will be update every %s", update_interval)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
@@ -52,12 +52,12 @@ CONDITION_CLASSES = {
ATTR_CONDITION_HAIL: [25],
ATTR_CONDITION_LIGHTNING: [15],
ATTR_CONDITION_LIGHTNING_RAINY: [16, 17, 41, 42],
ATTR_CONDITION_PARTLYCLOUDY: [4, 6, 35, 36],
ATTR_CONDITION_PARTLYCLOUDY: [3, 4, 6, 35, 36],
ATTR_CONDITION_POURING: [18],
ATTR_CONDITION_RAINY: [12, 13, 14, 26, 39, 40],
ATTR_CONDITION_SNOWY: [19, 20, 21, 22, 23, 43, 44],
ATTR_CONDITION_SNOWY_RAINY: [29],
ATTR_CONDITION_SUNNY: [1, 2, 3, 5],
ATTR_CONDITION_SUNNY: [1, 2, 5],
ATTR_CONDITION_WINDY: [32],
}
@@ -25,7 +25,7 @@
"step": {
"user": {
"title": "AccuWeather Options",
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 64 minutes instead of every 32 minutes.",
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes.",
"data": {
"forecast": "Weather forecast"
}
+1 -3
View File
@@ -26,9 +26,7 @@ class AcmedaBase(entity.Entity):
ent_registry.async_remove(self.entity_id)
dev_registry = await get_dev_reg(self.hass)
device = dev_registry.async_get_device(
identifiers={(DOMAIN, self.unique_id)}, connections=set()
)
device = dev_registry.async_get_device(identifiers={(DOMAIN, self.unique_id)})
if device is not None:
dev_registry.async_update_device(
device.id, remove_config_entry_id=self.registry_entry.config_entry_id
+1 -3
View File
@@ -32,9 +32,7 @@ async def update_devices(hass, config_entry, api):
for api_item in api.values():
# Update Device name
device = dev_registry.async_get_device(
identifiers={(DOMAIN, api_item.id)}, connections=set()
)
device = dev_registry.async_get_device(identifiers={(DOMAIN, api_item.id)})
if device is not None:
dev_registry.async_update_device(
device.id,
+2 -2
View File
@@ -181,8 +181,8 @@ class AfterShipSensor(Entity):
track["tracking_number"] if track["title"] is None else track["title"]
)
last_checkpoint = (
"Shipment pending"
if track["tag"] == "Pending"
f"Shipment {track['tag'].lower()}"
if not track["checkpoints"]
else track["checkpoints"][-1]
)
status_counts[status] = status_counts.get(status, 0) + 1
+20 -3
View File
@@ -20,6 +20,7 @@ from .const import (
ATTR_API_CAQI,
ATTR_API_CAQI_DESCRIPTION,
ATTR_API_CAQI_LEVEL,
CONF_USE_NEAREST,
DOMAIN,
MAX_REQUESTS_PER_DAY,
NO_AIRLY_SENSORS,
@@ -53,6 +54,7 @@ async def async_setup_entry(hass, config_entry):
api_key = config_entry.data[CONF_API_KEY]
latitude = config_entry.data[CONF_LATITUDE]
longitude = config_entry.data[CONF_LONGITUDE]
use_nearest = config_entry.data.get(CONF_USE_NEAREST, False)
# For backwards compat, set unique ID
if config_entry.unique_id is None:
@@ -67,7 +69,7 @@ async def async_setup_entry(hass, config_entry):
)
coordinator = AirlyDataUpdateCoordinator(
hass, websession, api_key, latitude, longitude, update_interval
hass, websession, api_key, latitude, longitude, update_interval, use_nearest
)
await coordinator.async_refresh()
@@ -107,21 +109,36 @@ async def async_unload_entry(hass, config_entry):
class AirlyDataUpdateCoordinator(DataUpdateCoordinator):
"""Define an object to hold Airly data."""
def __init__(self, hass, session, api_key, latitude, longitude, update_interval):
def __init__(
self,
hass,
session,
api_key,
latitude,
longitude,
update_interval,
use_nearest,
):
"""Initialize."""
self.latitude = latitude
self.longitude = longitude
self.airly = Airly(api_key, session)
self.use_nearest = use_nearest
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def _async_update_data(self):
"""Update data via library."""
data = {}
with async_timeout.timeout(20):
if self.use_nearest:
measurements = self.airly.create_measurements_session_nearest(
self.latitude, self.longitude, max_distance_km=5
)
else:
measurements = self.airly.create_measurements_session_point(
self.latitude, self.longitude
)
with async_timeout.timeout(20):
try:
await measurements.update()
except (AirlyError, ClientConnectorError) as error:
+14 -7
View File
@@ -87,13 +87,13 @@ class AirlyAirQuality(CoordinatorEntity, AirQualityEntity):
@round_state
def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level."""
return self.coordinator.data[ATTR_API_PM25]
return self.coordinator.data.get(ATTR_API_PM25)
@property
@round_state
def particulate_matter_10(self):
"""Return the particulate matter 10 level."""
return self.coordinator.data[ATTR_API_PM10]
return self.coordinator.data.get(ATTR_API_PM10)
@property
def attribution(self):
@@ -120,12 +120,19 @@ class AirlyAirQuality(CoordinatorEntity, AirQualityEntity):
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
attrs = {
LABEL_AQI_DESCRIPTION: self.coordinator.data[ATTR_API_CAQI_DESCRIPTION],
LABEL_ADVICE: self.coordinator.data[ATTR_API_ADVICE],
LABEL_AQI_LEVEL: self.coordinator.data[ATTR_API_CAQI_LEVEL],
LABEL_PM_2_5_LIMIT: self.coordinator.data[ATTR_API_PM25_LIMIT],
LABEL_PM_2_5_PERCENT: round(self.coordinator.data[ATTR_API_PM25_PERCENT]),
LABEL_PM_10_LIMIT: self.coordinator.data[ATTR_API_PM10_LIMIT],
LABEL_PM_10_PERCENT: round(self.coordinator.data[ATTR_API_PM10_PERCENT]),
}
if ATTR_API_PM25 in self.coordinator.data:
attrs[LABEL_PM_2_5_LIMIT] = self.coordinator.data[ATTR_API_PM25_LIMIT]
attrs[LABEL_PM_2_5_PERCENT] = round(
self.coordinator.data[ATTR_API_PM25_PERCENT]
)
if ATTR_API_PM10 in self.coordinator.data:
attrs[LABEL_PM_10_LIMIT] = self.coordinator.data[ATTR_API_PM10_LIMIT]
attrs[LABEL_PM_10_PERCENT] = round(
self.coordinator.data[ATTR_API_PM10_PERCENT]
)
return attrs
+33 -14
View File
@@ -10,12 +10,17 @@ from homeassistant.const import (
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_NAME,
HTTP_NOT_FOUND,
HTTP_UNAUTHORIZED,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from .const import DOMAIN, NO_AIRLY_SENSORS # pylint:disable=unused-import
from .const import ( # pylint:disable=unused-import
CONF_USE_NEAREST,
DOMAIN,
NO_AIRLY_SENSORS,
)
class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@@ -27,6 +32,7 @@ class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
errors = {}
use_nearest = False
websession = async_get_clientsession(self.hass)
@@ -36,23 +42,32 @@ class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
)
self._abort_if_unique_id_configured()
try:
location_valid = await test_location(
location_point_valid = await test_location(
websession,
user_input["api_key"],
user_input["latitude"],
user_input["longitude"],
)
if not location_point_valid:
await test_location(
websession,
user_input["api_key"],
user_input["latitude"],
user_input["longitude"],
use_nearest=True,
)
except AirlyError as err:
if err.status_code == HTTP_UNAUTHORIZED:
errors["base"] = "invalid_api_key"
else:
if not location_valid:
if err.status_code == HTTP_NOT_FOUND:
errors["base"] = "wrong_location"
if not errors:
return self.async_create_entry(
title=user_input[CONF_NAME], data=user_input
)
else:
if not location_point_valid:
use_nearest = True
return self.async_create_entry(
title=user_input[CONF_NAME],
data={**user_input, CONF_USE_NEAREST: use_nearest},
)
return self.async_show_form(
step_id="user",
@@ -74,13 +89,17 @@ class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
)
async def test_location(client, api_key, latitude, longitude):
async def test_location(client, api_key, latitude, longitude, use_nearest=False):
"""Return true if location is valid."""
airly = Airly(api_key, client)
measurements = airly.create_measurements_session_point(
latitude=latitude, longitude=longitude
)
if use_nearest:
measurements = airly.create_measurements_session_nearest(
latitude=latitude, longitude=longitude, max_distance_km=5
)
else:
measurements = airly.create_measurements_session_point(
latitude=latitude, longitude=longitude
)
with async_timeout.timeout(10):
await measurements.update()
+1
View File
@@ -13,6 +13,7 @@ ATTR_API_PM25_LIMIT = "PM25_LIMIT"
ATTR_API_PM25_PERCENT = "PM25_PERCENT"
ATTR_API_PRESSURE = "PRESSURE"
ATTR_API_TEMPERATURE = "TEMPERATURE"
CONF_USE_NEAREST = "use_nearest"
DEFAULT_NAME = "Airly"
DOMAIN = "airly"
MANUFACTURER = "Airly sp. z o.o."
+3 -1
View File
@@ -67,7 +67,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensors = []
for sensor in SENSOR_TYPES:
sensors.append(AirlySensor(coordinator, name, sensor))
# When we use the nearest method, we are not sure which sensors are available
if coordinator.data.get(sensor):
sensors.append(AirlySensor(coordinator, name, sensor))
async_add_entities(sensors, False)
+161
View File
@@ -0,0 +1,161 @@
"""The AirNow integration."""
import asyncio
import datetime
import logging
from aiohttp.client_exceptions import ClientConnectorError
from pyairnow import WebServiceAPI
from pyairnow.conv import aqi_to_concentration
from pyairnow.errors import AirNowError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
from homeassistant.core import 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 (
ATTR_API_AQI,
ATTR_API_AQI_DESCRIPTION,
ATTR_API_AQI_LEVEL,
ATTR_API_AQI_PARAM,
ATTR_API_CAT_DESCRIPTION,
ATTR_API_CAT_LEVEL,
ATTR_API_CATEGORY,
ATTR_API_PM25,
ATTR_API_POLLUTANT,
ATTR_API_REPORT_DATE,
ATTR_API_REPORT_HOUR,
ATTR_API_STATE,
ATTR_API_STATION,
ATTR_API_STATION_LATITUDE,
ATTR_API_STATION_LONGITUDE,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"]
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the AirNow component."""
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up AirNow from a config entry."""
api_key = entry.data[CONF_API_KEY]
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
distance = entry.data[CONF_RADIUS]
# Reports are published hourly but update twice per hour
update_interval = datetime.timedelta(minutes=30)
# Setup the Coordinator
session = async_get_clientsession(hass)
coordinator = AirNowDataUpdateCoordinator(
hass, session, api_key, latitude, longitude, distance, update_interval
)
# Sync with Coordinator
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
# Store Entity and Initialize Platforms
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = coordinator
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
class AirNowDataUpdateCoordinator(DataUpdateCoordinator):
"""Define an object to hold Airly data."""
def __init__(
self, hass, session, api_key, latitude, longitude, distance, update_interval
):
"""Initialize."""
self.latitude = latitude
self.longitude = longitude
self.distance = distance
self.airnow = WebServiceAPI(api_key, session=session)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def _async_update_data(self):
"""Update data via library."""
data = {}
try:
obs = await self.airnow.observations.latLong(
self.latitude,
self.longitude,
distance=self.distance,
)
except (AirNowError, ClientConnectorError) as error:
raise UpdateFailed(error) from error
if not obs:
raise UpdateFailed("No data was returned from AirNow")
max_aqi = 0
max_aqi_level = 0
max_aqi_desc = ""
max_aqi_poll = ""
for obv in obs:
# Convert AQIs to Concentration
pollutant = obv[ATTR_API_AQI_PARAM]
concentration = aqi_to_concentration(obv[ATTR_API_AQI], pollutant)
data[obv[ATTR_API_AQI_PARAM]] = concentration
# Overall AQI is the max of all pollutant AQIs
if obv[ATTR_API_AQI] > max_aqi:
max_aqi = obv[ATTR_API_AQI]
max_aqi_level = obv[ATTR_API_CATEGORY][ATTR_API_CAT_LEVEL]
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
max_aqi_poll = pollutant
# Copy other data from PM2.5 Value
if obv[ATTR_API_AQI_PARAM] == ATTR_API_PM25:
# Copy Report Details
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
# Copy Station Details
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
# Store Overall AQI
data[ATTR_API_AQI] = max_aqi
data[ATTR_API_AQI_LEVEL] = max_aqi_level
data[ATTR_API_AQI_DESCRIPTION] = max_aqi_desc
data[ATTR_API_POLLUTANT] = max_aqi_poll
return data
@@ -0,0 +1,110 @@
"""Config flow for AirNow integration."""
import logging
from pyairnow import WebServiceAPI
from pyairnow.errors import AirNowError, InvalidKeyError
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
async def validate_input(hass: core.HomeAssistant, data):
"""
Validate the user input allows us to connect.
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
session = async_get_clientsession(hass)
client = WebServiceAPI(data[CONF_API_KEY], session=session)
lat = data[CONF_LATITUDE]
lng = data[CONF_LONGITUDE]
distance = data[CONF_RADIUS]
# Check that the provided latitude/longitude provide a response
try:
test_data = await client.observations.latLong(lat, lng, distance=distance)
except InvalidKeyError as exc:
raise InvalidAuth from exc
except AirNowError as exc:
raise CannotConnect from exc
if not test_data:
raise InvalidLocation
# Validation Succeeded
return True
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for AirNow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
# Set a unique id based on latitude/longitude
await self.async_set_unique_id(
f"{user_input[CONF_LATITUDE]}-{user_input[CONF_LONGITUDE]}"
)
self._abort_if_unique_id_configured()
try:
# Validate inputs
await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except InvalidLocation:
errors["base"] = "invalid_location"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
# Create Entry
return self.async_create_entry(
title=f"AirNow Sensor at {user_input[CONF_LATITUDE]}, {user_input[CONF_LONGITUDE]}",
data=user_input,
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_API_KEY): str,
vol.Optional(
CONF_LATITUDE, default=self.hass.config.latitude
): cv.latitude,
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(CONF_RADIUS, default=150): int,
}
),
errors=errors,
)
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""
class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""
class InvalidLocation(exceptions.HomeAssistantError):
"""Error to indicate the location is invalid."""
+21
View File
@@ -0,0 +1,21 @@
"""Constants for the AirNow integration."""
ATTR_API_AQI = "AQI"
ATTR_API_AQI_LEVEL = "Category.Number"
ATTR_API_AQI_DESCRIPTION = "Category.Name"
ATTR_API_AQI_PARAM = "ParameterName"
ATTR_API_CATEGORY = "Category"
ATTR_API_CAT_LEVEL = "Number"
ATTR_API_CAT_DESCRIPTION = "Name"
ATTR_API_O3 = "O3"
ATTR_API_PM25 = "PM2.5"
ATTR_API_POLLUTANT = "Pollutant"
ATTR_API_REPORT_DATE = "HourObserved"
ATTR_API_REPORT_HOUR = "DateObserved"
ATTR_API_STATE = "StateCode"
ATTR_API_STATION = "ReportingArea"
ATTR_API_STATION_LATITUDE = "Latitude"
ATTR_API_STATION_LONGITUDE = "Longitude"
DEFAULT_NAME = "AirNow"
DOMAIN = "airnow"
SENSOR_AQI_ATTR_DESCR = "description"
SENSOR_AQI_ATTR_LEVEL = "level"
@@ -0,0 +1,12 @@
{
"domain": "airnow",
"name": "AirNow",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airnow",
"requirements": [
"pyairnow==1.1.0"
],
"codeowners": [
"@asymworks"
]
}
+118
View File
@@ -0,0 +1,118 @@
"""Support for the AirNow sensor service."""
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_DEVICE_CLASS,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ATTR_API_AQI,
ATTR_API_AQI_DESCRIPTION,
ATTR_API_AQI_LEVEL,
ATTR_API_O3,
ATTR_API_PM25,
DOMAIN,
SENSOR_AQI_ATTR_DESCR,
SENSOR_AQI_ATTR_LEVEL,
)
ATTRIBUTION = "Data provided by AirNow"
ATTR_ICON = "icon"
ATTR_LABEL = "label"
ATTR_UNIT = "unit"
PARALLEL_UPDATES = 1
SENSOR_TYPES = {
ATTR_API_AQI: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_LABEL: ATTR_API_AQI,
ATTR_UNIT: "aqi",
},
ATTR_API_PM25: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_LABEL: ATTR_API_PM25,
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
},
ATTR_API_O3: {
ATTR_DEVICE_CLASS: None,
ATTR_ICON: "mdi:blur",
ATTR_LABEL: ATTR_API_O3,
ATTR_UNIT: CONCENTRATION_PARTS_PER_MILLION,
},
}
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up AirNow sensor entities based on a config entry."""
coordinator = hass.data[DOMAIN][config_entry.entry_id]
sensors = []
for sensor in SENSOR_TYPES:
sensors.append(AirNowSensor(coordinator, sensor))
async_add_entities(sensors, False)
class AirNowSensor(CoordinatorEntity):
"""Define an AirNow sensor."""
def __init__(self, coordinator, kind):
"""Initialize."""
super().__init__(coordinator)
self.kind = kind
self._device_class = None
self._state = None
self._icon = None
self._unit_of_measurement = None
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
@property
def name(self):
"""Return the name."""
return f"AirNow {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
@property
def state(self):
"""Return the state."""
self._state = self.coordinator.data[self.kind]
return self._state
@property
def device_state_attributes(self):
"""Return the state attributes."""
if self.kind == ATTR_API_AQI:
self._attrs[SENSOR_AQI_ATTR_DESCR] = self.coordinator.data[
ATTR_API_AQI_DESCRIPTION
]
self._attrs[SENSOR_AQI_ATTR_LEVEL] = self.coordinator.data[
ATTR_API_AQI_LEVEL
]
return self._attrs
@property
def icon(self):
"""Return the icon."""
self._icon = SENSOR_TYPES[self.kind][ATTR_ICON]
return self._icon
@property
def device_class(self):
"""Return the device_class."""
return SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
@property
def unique_id(self):
"""Return a unique_id for this entity."""
return f"{self.coordinator.latitude}-{self.coordinator.longitude}-{self.kind.lower()}"
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return SENSOR_TYPES[self.kind][ATTR_UNIT]
@@ -0,0 +1,26 @@
{
"title": "AirNow",
"config": {
"step": {
"user": {
"title": "AirNow",
"description": "Set up AirNow air quality integration. To generate API key go to https://docs.airnowapi.org/account/request/",
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"radius": "Station Radius (miles; optional)"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_location": "No results found for that location",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}
@@ -0,0 +1,27 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_location": "No results found for that location",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"user": {
"data": {
"api_key": "API Key",
"latitude": "Latitude",
"longitude": "Longitude",
"name": "Name of the Entity",
"radius": "Station Radius (miles; optional)"
},
"description": "Set up AirNow air quality integration. To generate API key go to https://docs.airnowapi.org/account/request/",
"title": "AirNow"
}
}
},
"title": "AirNow"
}
+17 -26
View File
@@ -58,25 +58,6 @@ NODE_PRO_SENSORS = [
(SENSOR_KIND_TEMPERATURE, "Temperature", DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS),
]
POLLUTANT_LEVEL_MAPPING = [
{"label": "Good", "icon": "mdi:emoticon-excited", "minimum": 0, "maximum": 50},
{"label": "Moderate", "icon": "mdi:emoticon-happy", "minimum": 51, "maximum": 100},
{
"label": "Unhealthy for sensitive groups",
"icon": "mdi:emoticon-neutral",
"minimum": 101,
"maximum": 150,
},
{"label": "Unhealthy", "icon": "mdi:emoticon-sad", "minimum": 151, "maximum": 200},
{
"label": "Very Unhealthy",
"icon": "mdi:emoticon-dead",
"minimum": 201,
"maximum": 300,
},
{"label": "Hazardous", "icon": "mdi:biohazard", "minimum": 301, "maximum": 10000},
]
POLLUTANT_MAPPING = {
"co": {"label": "Carbon Monoxide", "unit": CONCENTRATION_PARTS_PER_MILLION},
"n2": {"label": "Nitrogen Dioxide", "unit": CONCENTRATION_PARTS_PER_BILLION},
@@ -87,6 +68,22 @@ POLLUTANT_MAPPING = {
}
@callback
def async_get_pollutant_level_info(value):
"""Return a verbal pollutant level (and associated icon) for a numeric value."""
if 0 <= value <= 50:
return ("Good", "mdi:emoticon-excited")
if 51 <= value <= 100:
return ("Moderate", "mdi:emoticon-happy")
if 101 <= value <= 150:
return ("Unhealthy for sensitive groups", "mdi:emoticon-neutral")
if 151 <= value <= 200:
return ("Unhealthy", "mdi:emoticon-sad")
if 201 <= value <= 300:
return ("Very Unhealthy", "mdi:emoticon-dead")
return ("Hazardous", "mdi:biohazard")
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up AirVisual sensors based on a config entry."""
coordinator = hass.data[DOMAIN][DATA_COORDINATOR][config_entry.entry_id]
@@ -171,13 +168,7 @@ class AirVisualGeographySensor(AirVisualEntity):
if self._kind == SENSOR_KIND_LEVEL:
aqi = data[f"aqi{self._locale}"]
[level] = [
i
for i in POLLUTANT_LEVEL_MAPPING
if i["minimum"] <= aqi <= i["maximum"]
]
self._state = level["label"]
self._icon = level["icon"]
self._state, self._icon = async_get_pollutant_level_info(aqi)
elif self._kind == SENSOR_KIND_AQI:
self._state = data[f"aqi{self._locale}"]
elif self._kind == SENSOR_KIND_POLLUTANT:
@@ -23,7 +23,6 @@ from homeassistant.const import (
STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMING,
STATE_ALARM_DISARMED,
STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
@@ -143,13 +142,10 @@ async def async_attach_trigger(
from_state = STATE_ALARM_DISARMED
to_state = STATE_ALARM_ARMING
elif config[CONF_TYPE] == "armed_home":
from_state = STATE_ALARM_PENDING or STATE_ALARM_ARMING
to_state = STATE_ALARM_ARMED_HOME
elif config[CONF_TYPE] == "armed_away":
from_state = STATE_ALARM_PENDING or STATE_ALARM_ARMING
to_state = STATE_ALARM_ARMED_AWAY
elif config[CONF_TYPE] == "armed_night":
from_state = STATE_ALARM_PENDING or STATE_ALARM_ARMING
to_state = STATE_ALARM_ARMED_NIGHT
state_config = {
+5
View File
@@ -45,6 +45,11 @@ class AbstractConfig(ABC):
"""Return if proactive mode is enabled."""
return self._unsub_proactive_report is not None
@callback
@abstractmethod
def user_identifier(self):
"""Return an identifier for the user that represents this config."""
async def async_enable_proactive_mode(self):
"""Enable proactive mode."""
if self._unsub_proactive_report is None:
+1 -1
View File
@@ -329,7 +329,7 @@ class AlexaEntity:
"manufacturer": "Home Assistant",
"model": self.entity.domain,
"softwareVersion": __version__,
"customIdentifier": self.entity_id,
"customIdentifier": f"{self.config.user_identifier()}-{self.entity_id}",
},
}
@@ -53,6 +53,11 @@ class AlexaConfig(AbstractConfig):
"""Return config locale."""
return self._config.get(CONF_LOCALE)
@core.callback
def user_identifier(self):
"""Return an identifier for the user that represents this config."""
return ""
def should_expose(self, entity_id):
"""If an entity should be exposed."""
return self._config[CONF_FILTER](entity_id)
+45 -19
View File
@@ -2,15 +2,18 @@
import asyncio
import json
import logging
from typing import Optional
import aiohttp
import async_timeout
from homeassistant.const import HTTP_ACCEPTED, MATCH_ALL, STATE_ON
from homeassistant.core import State
from homeassistant.helpers.significant_change import create_checker
import homeassistant.util.dt as dt_util
from .const import API_CHANGE, Cause
from .entities import ENTITY_ADAPTERS, generate_alexa_id
from .const import API_CHANGE, DOMAIN, Cause
from .entities import ENTITY_ADAPTERS, AlexaEntity, generate_alexa_id
from .messages import AlexaResponse
_LOGGER = logging.getLogger(__name__)
@@ -25,7 +28,13 @@ async def async_enable_proactive_mode(hass, smart_home_config):
# Validate we can get access token.
await smart_home_config.async_get_access_token()
async def async_entity_state_listener(changed_entity, old_state, new_state):
checker = await create_checker(hass, DOMAIN)
async def async_entity_state_listener(
changed_entity: str,
old_state: Optional[State],
new_state: Optional[State],
):
if not hass.is_running:
return
@@ -39,24 +48,43 @@ async def async_enable_proactive_mode(hass, smart_home_config):
_LOGGER.debug("Not exposing %s because filtered by config", changed_entity)
return
alexa_changed_entity = ENTITY_ADAPTERS[new_state.domain](
alexa_changed_entity: AlexaEntity = ENTITY_ADAPTERS[new_state.domain](
hass, smart_home_config, new_state
)
# Determine how entity should be reported on
should_report = False
should_doorbell = False
for interface in alexa_changed_entity.interfaces():
if interface.properties_proactively_reported():
await async_send_changereport_message(
hass, smart_home_config, alexa_changed_entity
)
return
if not should_report and interface.properties_proactively_reported():
should_report = True
if (
interface.name() == "Alexa.DoorbellEventSource"
and new_state.state == STATE_ON
):
await async_send_doorbell_event_message(
hass, smart_home_config, alexa_changed_entity
)
return
should_doorbell = True
break
if not should_report and not should_doorbell:
return
if not checker.async_is_significant_change(new_state):
return
if should_doorbell:
should_report = False
if should_report:
await async_send_changereport_message(
hass, smart_home_config, alexa_changed_entity
)
elif should_doorbell:
await async_send_doorbell_event_message(
hass, smart_home_config, alexa_changed_entity
)
return hass.helpers.event.async_track_state_change(
MATCH_ALL, async_entity_state_listener
@@ -76,13 +104,11 @@ async def async_send_changereport_message(
endpoint = alexa_entity.alexa_id()
# this sends all the properties of the Alexa Entity, whether they have
# changed or not. this should be improved, and properties that have not
# changed should be moved to the 'context' object
properties = list(alexa_entity.serialize_properties())
payload = {
API_CHANGE: {"cause": {"type": Cause.APP_INTERACTION}, "properties": properties}
API_CHANGE: {
"cause": {"type": Cause.APP_INTERACTION},
"properties": list(alexa_entity.serialize_properties()),
}
}
message = AlexaResponse(name="ChangeReport", namespace="Alexa", payload=payload)
@@ -2,6 +2,6 @@
"domain": "alpha_vantage",
"name": "Alpha Vantage",
"documentation": "https://www.home-assistant.io/integrations/alpha_vantage",
"requirements": ["alpha_vantage==2.2.0"],
"requirements": ["alpha_vantage==2.3.1"],
"codeowners": ["@fabaff"]
}
@@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
"requirements": [
"pyatv==0.7.5"
"pyatv==0.7.6"
],
"zeroconf": [
"_mediaremotetv._tcp.local.",
@@ -108,8 +108,6 @@ async def _run_client(hass, client, interval):
await asyncio.sleep(interval)
except asyncio.TimeoutError:
continue
except asyncio.CancelledError:
raise
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception, aborting arcam client")
return
+16 -11
View File
@@ -43,17 +43,22 @@ _LOGGER = logging.getLogger(__name__)
TWO_FA_REVALIDATE = "verify_configurator"
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_LOGIN_METHOD): vol.In(LOGIN_METHODS),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_INSTALL_ID): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}
)
},
vol.All(
cv.deprecated(DOMAIN),
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_LOGIN_METHOD): vol.In(LOGIN_METHODS),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_INSTALL_ID): cv.string,
vol.Optional(
CONF_TIMEOUT, default=DEFAULT_TIMEOUT
): cv.positive_int,
}
)
},
),
extra=vol.ALLOW_EXTRA,
)
+10 -9
View File
@@ -11,7 +11,7 @@ from .subscriber import AugustSubscriberMixin
_LOGGER = logging.getLogger(__name__)
ACTIVITY_STREAM_FETCH_LIMIT = 10
ACTIVITY_CATCH_UP_FETCH_LIMIT = 1000
ACTIVITY_CATCH_UP_FETCH_LIMIT = 2500
class ActivityStream(AugustSubscriberMixin):
@@ -102,11 +102,14 @@ class ActivityStream(AugustSubscriberMixin):
def _process_newer_device_activities(self, activities):
updated_device_ids = set()
for activity in activities:
self._latest_activities_by_id_type.setdefault(activity.device_id, {})
device_id = activity.device_id
activity_type = activity.activity_type
lastest_activity = self._latest_activities_by_id_type[
activity.device_id
].get(activity.activity_type)
self._latest_activities_by_id_type.setdefault(device_id, {})
lastest_activity = self._latest_activities_by_id_type[device_id].get(
activity_type
)
# Ignore activities that are older than the latest one
if (
@@ -115,10 +118,8 @@ class ActivityStream(AugustSubscriberMixin):
):
continue
self._latest_activities_by_id_type[activity.device_id][
activity.activity_type
] = activity
self._latest_activities_by_id_type[device_id][activity_type] = activity
updated_device_ids.add(activity.device_id)
updated_device_ids.add(device_id)
return updated_device_ids
@@ -2,8 +2,12 @@
"domain": "august",
"name": "August",
"documentation": "https://www.home-assistant.io/integrations/august",
"requirements": ["py-august==0.25.0"],
"requirements": ["py-august==0.25.2"],
"dependencies": ["configurator"],
"codeowners": ["@bdraco"],
"dhcp": [
{"hostname":"connect","macaddress":"D86162*"},
{"hostname":"connect","macaddress":"B8B7F1*"}
],
"config_flow": true
}
+3 -3
View File
@@ -4,7 +4,7 @@ import logging
from august.activity import ActivityType
from homeassistant.components.sensor import DEVICE_CLASS_BATTERY
from homeassistant.const import ATTR_ENTITY_PICTURE, PERCENTAGE
from homeassistant.const import ATTR_ENTITY_PICTURE, PERCENTAGE, STATE_UNAVAILABLE
from homeassistant.core import callback
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_registry import async_get_registry
@@ -157,8 +157,8 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, Entity):
self._device_id, [ActivityType.LOCK_OPERATION]
)
self._available = True
if lock_activity is not None:
self._available = True
self._state = lock_activity.operated_by
self._operated_remote = lock_activity.operated_remote
self._operated_keypad = lock_activity.operated_keypad
@@ -193,7 +193,7 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, Entity):
await super().async_added_to_hass()
last_state = await self.async_get_last_state()
if not last_state:
if not last_state or last_state.state == STATE_UNAVAILABLE:
return
self._state = last_state.state
@@ -404,6 +404,12 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
await self.action_script.async_run(
variables, trigger_context, started_action
)
except (vol.Invalid, HomeAssistantError) as err:
self._logger.error(
"Error while executing automation %s: %s",
self.entity_id,
err,
)
except Exception: # pylint: disable=broad-except
self._logger.exception("While executing automation %s", self.entity_id)
@@ -462,8 +468,8 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
) -> Optional[Callable[[], None]]:
"""Set up the triggers."""
def log_cb(level, msg):
self._logger.log(level, "%s %s", msg, self._name)
def log_cb(level, msg, **kwargs):
self._logger.log(level, "%s %s", msg, self._name, **kwargs)
return await async_initialize_triggers(
cast(HomeAssistant, self.hass),
+1 -1
View File
@@ -3,5 +3,5 @@
"name": "Amazon Web Services (AWS)",
"documentation": "https://www.home-assistant.io/integrations/aws",
"requirements": ["aiobotocore==0.11.1"],
"codeowners": ["@awarecan"]
"codeowners": []
}
+24 -8
View File
@@ -2,7 +2,10 @@
import logging
from homeassistant.const import CONF_DEVICE, EVENT_HOMEASSISTANT_STOP
from homeassistant.const import CONF_DEVICE, CONF_MAC, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.entity_registry import async_migrate_entries
from .const import DOMAIN as AXIS_DOMAIN
from .device import AxisNetworkDevice
@@ -24,12 +27,6 @@ async def async_setup_entry(hass, config_entry):
if not await device.async_setup():
return False
# 0.104 introduced config entry unique id, this makes upgrading possible
if config_entry.unique_id is None:
hass.config_entries.async_update_entry(
config_entry, unique_id=device.api.vapix.serial_number
)
hass.data[AXIS_DOMAIN][config_entry.unique_id] = device
await device.async_update_device_registry()
@@ -52,9 +49,28 @@ async def async_migrate_entry(hass, config_entry):
# Flatten configuration but keep old data if user rollbacks HASS prior to 0.106
if config_entry.version == 1:
config_entry.data = {**config_entry.data, **config_entry.data[CONF_DEVICE]}
config_entry.unique_id = config_entry.data[CONF_MAC]
config_entry.version = 2
# Normalise MAC address of device which also affects entity unique IDs
if config_entry.version == 2:
old_unique_id = config_entry.unique_id
new_unique_id = format_mac(old_unique_id)
@callback
def update_unique_id(entity_entry):
"""Update unique ID of entity entry."""
return {
"new_unique_id": entity_entry.unique_id.replace(
old_unique_id, new_unique_id
)
}
await async_migrate_entries(hass, config_entry.entry_id, update_unique_id)
config_entry.unique_id = new_unique_id
config_entry.version = 3
_LOGGER.info("Migration to version %s successful", config_entry.version)
return True
+2 -2
View File
@@ -30,7 +30,7 @@ class AxisEntityBase(Entity):
@property
def device_info(self):
"""Return a device description for device registry."""
return {"identifiers": {(AXIS_DOMAIN, self.device.serial)}}
return {"identifiers": {(AXIS_DOMAIN, self.device.unique_id)}}
@callback
def update_callback(self, no_delay=None):
@@ -73,4 +73,4 @@ class AxisEventBase(AxisEntityBase):
@property
def unique_id(self):
"""Return a unique identifier for this device."""
return f"{self.device.serial}-{self.event.topic}-{self.event.id}"
return f"{self.device.unique_id}-{self.event.topic}-{self.event.id}"
@@ -7,10 +7,12 @@ from axis.event_stream import (
CLASS_LIGHT,
CLASS_MOTION,
CLASS_OUTPUT,
CLASS_PTZ,
CLASS_SOUND,
FenceGuard,
LoiteringGuard,
MotionGuard,
ObjectAnalytics,
Vmd4,
)
@@ -46,7 +48,7 @@ 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 and not (
if event.CLASS not in (CLASS_OUTPUT, CLASS_PTZ) and not (
event.CLASS == CLASS_LIGHT and event.TYPE == "Light"
):
async_add_entities([AxisBinarySensor(event, device)])
@@ -101,7 +103,7 @@ class AxisBinarySensor(AxisEventBase, BinarySensorEntity):
"""Return the name of the event."""
if (
self.event.CLASS == CLASS_INPUT
and self.event.id
and self.event.id in self.device.api.vapix.ports
and self.device.api.vapix.ports[self.event.id].name
):
return (
@@ -114,6 +116,7 @@ class AxisBinarySensor(AxisEventBase, BinarySensorEntity):
(FenceGuard, self.device.api.vapix.fence_guard),
(LoiteringGuard, self.device.api.vapix.loitering_guard),
(MotionGuard, self.device.api.vapix.motion_guard),
(ObjectAnalytics, self.device.api.vapix.object_analytics),
(Vmd4, self.device.api.vapix.vmd4),
):
if (
+40 -22
View File
@@ -1,5 +1,7 @@
"""Support for Axis camera streaming."""
from urllib.parse import urlencode
from homeassistant.components.camera import SUPPORT_STREAM
from homeassistant.components.mjpeg.camera import (
CONF_MJPEG_URL,
@@ -11,14 +13,13 @@ from homeassistant.const import (
CONF_AUTHENTICATION,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_USERNAME,
HTTP_DIGEST_AUTHENTICATION,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .axis_base import AxisEntityBase
from .const import DEFAULT_STREAM_PROFILE, DOMAIN as AXIS_DOMAIN
from .const import DEFAULT_STREAM_PROFILE, DEFAULT_VIDEO_SOURCE, DOMAIN as AXIS_DOMAIN
async def async_setup_entry(hass, config_entry, async_add_entities):
@@ -41,9 +42,9 @@ class AxisCamera(AxisEntityBase, MjpegCamera):
AxisEntityBase.__init__(self, device)
config = {
CONF_NAME: device.config_entry.data[CONF_NAME],
CONF_USERNAME: device.config_entry.data[CONF_USERNAME],
CONF_PASSWORD: device.config_entry.data[CONF_PASSWORD],
CONF_NAME: device.name,
CONF_USERNAME: device.username,
CONF_PASSWORD: device.password,
CONF_MJPEG_URL: self.mjpeg_source,
CONF_STILL_IMAGE_URL: self.image_source,
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
@@ -61,38 +62,55 @@ class AxisCamera(AxisEntityBase, MjpegCamera):
await super().async_added_to_hass()
@property
def supported_features(self):
def supported_features(self) -> int:
"""Return supported features."""
return SUPPORT_STREAM
def _new_address(self):
def _new_address(self) -> None:
"""Set new device address for video stream."""
self._mjpeg_url = self.mjpeg_source
self._still_image_url = self.image_source
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return a unique identifier for this device."""
return f"{self.device.serial}-camera"
return f"{self.device.unique_id}-camera"
@property
def image_source(self):
def image_source(self) -> str:
"""Return still image URL for device."""
return f"http://{self.device.host}:{self.device.config_entry.data[CONF_PORT]}/axis-cgi/jpg/image.cgi"
options = self.generate_options(skip_stream_profile=True)
return f"http://{self.device.host}:{self.device.port}/axis-cgi/jpg/image.cgi{options}"
@property
def mjpeg_source(self):
def mjpeg_source(self) -> str:
"""Return mjpeg URL for device."""
options = ""
if self.device.option_stream_profile != DEFAULT_STREAM_PROFILE:
options = f"?&streamprofile={self.device.option_stream_profile}"
options = self.generate_options()
return f"http://{self.device.host}:{self.device.port}/axis-cgi/mjpg/video.cgi{options}"
return f"http://{self.device.host}:{self.device.config_entry.data[CONF_PORT]}/axis-cgi/mjpg/video.cgi{options}"
async def stream_source(self):
async def stream_source(self) -> str:
"""Return the stream source."""
options = ""
if self.device.option_stream_profile != DEFAULT_STREAM_PROFILE:
options = f"&streamprofile={self.device.option_stream_profile}"
options = self.generate_options(add_video_codec_h264=True)
return f"rtsp://{self.device.username}:{self.device.password}@{self.device.host}/axis-media/media.amp{options}"
return f"rtsp://{self.device.config_entry.data[CONF_USERNAME]}:{self.device.config_entry.data[CONF_PASSWORD]}@{self.device.host}/axis-media/media.amp?videocodec=h264{options}"
def generate_options(
self, skip_stream_profile: bool = False, add_video_codec_h264: bool = False
) -> str:
"""Generate options for video stream."""
options_dict = {}
if add_video_codec_h264:
options_dict["videocodec"] = "h264"
if (
not skip_stream_profile
and self.device.option_stream_profile != DEFAULT_STREAM_PROFILE
):
options_dict["streamprofile"] = self.device.option_stream_profile
if self.device.option_video_source != DEFAULT_VIDEO_SOURCE:
options_dict["camera"] = self.device.option_video_source
if not options_dict:
return ""
return f"?{urlencode(options_dict)}"
+108 -38
View File
@@ -1,10 +1,12 @@
"""Config flow to configure Axis devices."""
from ipaddress import ip_address
from urllib.parse import urlsplit
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS
from homeassistant.config_entries import SOURCE_IGNORE
from homeassistant.const import (
CONF_HOST,
@@ -15,34 +17,28 @@ from homeassistant.const import (
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.util.network import is_link_local
from .const import (
CONF_MODEL,
CONF_STREAM_PROFILE,
CONF_VIDEO_SOURCE,
DEFAULT_STREAM_PROFILE,
DEFAULT_VIDEO_SOURCE,
DOMAIN as AXIS_DOMAIN,
)
from .device import get_device
from .errors import AuthenticationRequired, CannotConnect
AXIS_OUI = {"00408C", "ACCC8E", "B8A44F"}
CONFIG_FILE = "axis.conf"
EVENT_TYPES = ["motion", "vmd3", "pir", "sound", "daynight", "tampering", "input"]
PLATFORMS = ["camera"]
AXIS_INCLUDE = EVENT_TYPES + PLATFORMS
AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f"}
DEFAULT_PORT = 80
class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
"""Handle a Axis config flow."""
VERSION = 2
VERSION = 3
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
@staticmethod
@@ -56,6 +52,7 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
self.device_config = {}
self.discovery_schema = {}
self.import_schema = {}
self.serial = None
async def async_step_user(self, user_input=None):
"""Handle a Axis config flow start.
@@ -74,12 +71,15 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
password=user_input[CONF_PASSWORD],
)
await self.async_set_unique_id(device.vapix.serial_number)
self.serial = device.vapix.serial_number
await self.async_set_unique_id(format_mac(self.serial))
self._abort_if_unique_id_configured(
updates={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
CONF_USERNAME: user_input[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD],
}
)
@@ -88,7 +88,6 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
CONF_PORT: user_input[CONF_PORT],
CONF_USERNAME: user_input[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_MAC: device.vapix.serial_number,
CONF_MODEL: device.vapix.product_number,
}
@@ -134,39 +133,88 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN):
self.device_config[CONF_NAME] = name
title = f"{model} - {self.device_config[CONF_MAC]}"
title = f"{model} - {self.serial}"
return self.async_create_entry(title=title, data=self.device_config)
async def async_step_zeroconf(self, discovery_info):
"""Prepare configuration for a discovered Axis device."""
serial_number = discovery_info["properties"]["macaddress"]
async def async_step_reauth(self, device_config: dict):
"""Trigger a reauthentication flow."""
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {
CONF_NAME: device_config[CONF_NAME],
CONF_HOST: device_config[CONF_HOST],
}
if serial_number[:6] not in AXIS_OUI:
self.discovery_schema = {
vol.Required(CONF_HOST, default=device_config[CONF_HOST]): str,
vol.Required(CONF_USERNAME, default=device_config[CONF_USERNAME]): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=device_config[CONF_PORT]): int,
}
return await self.async_step_user()
async def async_step_dhcp(self, discovery_info: dict):
"""Prepare configuration for a DHCP discovered Axis device."""
return await self._process_discovered_device(
{
CONF_HOST: discovery_info[IP_ADDRESS],
CONF_MAC: format_mac(discovery_info.get(MAC_ADDRESS)),
CONF_NAME: discovery_info.get(HOSTNAME),
CONF_PORT: DEFAULT_PORT,
}
)
async def async_step_ssdp(self, discovery_info: dict):
"""Prepare configuration for a SSDP discovered Axis device."""
url = urlsplit(discovery_info["presentationURL"])
return await self._process_discovered_device(
{
CONF_HOST: url.hostname,
CONF_MAC: format_mac(discovery_info["serialNumber"]),
CONF_NAME: f"{discovery_info['friendlyName']}",
CONF_PORT: url.port,
}
)
async def async_step_zeroconf(self, discovery_info: dict):
"""Prepare configuration for a Zeroconf discovered Axis device."""
return await self._process_discovered_device(
{
CONF_HOST: discovery_info[CONF_HOST],
CONF_MAC: format_mac(discovery_info["properties"]["macaddress"]),
CONF_NAME: discovery_info["name"].split(".", 1)[0],
CONF_PORT: discovery_info[CONF_PORT],
}
)
async def _process_discovered_device(self, device: dict):
"""Prepare configuration for a discovered Axis device."""
if device[CONF_MAC][:8] not in AXIS_OUI:
return self.async_abort(reason="not_axis_device")
if is_link_local(ip_address(discovery_info[CONF_HOST])):
if is_link_local(ip_address(device[CONF_HOST])):
return self.async_abort(reason="link_local_address")
await self.async_set_unique_id(serial_number)
await self.async_set_unique_id(device[CONF_MAC])
self._abort_if_unique_id_configured(
updates={
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
CONF_HOST: device[CONF_HOST],
CONF_PORT: device[CONF_PORT],
}
)
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {
CONF_NAME: discovery_info["hostname"][:-7],
CONF_HOST: discovery_info[CONF_HOST],
CONF_NAME: device[CONF_NAME],
CONF_HOST: device[CONF_HOST],
}
self.discovery_schema = {
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str,
vol.Required(CONF_HOST, default=device[CONF_HOST]): str,
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=discovery_info[CONF_PORT]): int,
vol.Required(CONF_PORT, default=device[CONF_PORT]): int,
}
return await self.async_step_user()
@@ -187,22 +235,44 @@ class AxisOptionsFlowHandler(config_entries.OptionsFlow):
return await self.async_step_configure_stream()
async def async_step_configure_stream(self, user_input=None):
"""Manage the Axis device options."""
"""Manage the Axis device stream options."""
if user_input is not None:
self.options.update(user_input)
return self.async_create_entry(title="", data=self.options)
profiles = [DEFAULT_STREAM_PROFILE]
for profile in self.device.api.vapix.streaming_profiles:
profiles.append(profile.name)
schema = {}
vapix = self.device.api.vapix
# Stream profiles
if vapix.params.stream_profiles_max_groups > 0:
stream_profiles = [DEFAULT_STREAM_PROFILE]
for profile in vapix.streaming_profiles:
stream_profiles.append(profile.name)
schema[
vol.Optional(
CONF_STREAM_PROFILE, default=self.device.option_stream_profile
)
] = vol.In(stream_profiles)
# Video sources
if vapix.params.image_nbrofviews > 0:
await vapix.params.update_image()
video_sources = {DEFAULT_VIDEO_SOURCE: DEFAULT_VIDEO_SOURCE}
for idx, video_source in vapix.params.image_sources.items():
if not video_source["Enabled"]:
continue
video_sources[idx + 1] = video_source["Name"]
schema[
vol.Optional(CONF_VIDEO_SOURCE, default=self.device.option_video_source)
] = vol.In(video_sources)
return self.async_show_form(
step_id="configure_stream",
data_schema=vol.Schema(
{
vol.Optional(
CONF_STREAM_PROFILE, default=self.device.option_stream_profile
): vol.In(profiles)
}
),
step_id="configure_stream", data_schema=vol.Schema(schema)
)
+2
View File
@@ -15,9 +15,11 @@ ATTR_MANUFACTURER = "Axis Communications AB"
CONF_EVENTS = "events"
CONF_MODEL = "model"
CONF_STREAM_PROFILE = "stream_profile"
CONF_VIDEO_SOURCE = "video_source"
DEFAULT_EVENTS = True
DEFAULT_STREAM_PROFILE = "No stream profile"
DEFAULT_TRIGGER_TIME = 0
DEFAULT_VIDEO_SOURCE = "No video source"
PLATFORMS = [BINARY_SENSOR_DOMAIN, CAMERA_DOMAIN, LIGHT_DOMAIN, SWITCH_DOMAIN]
+50 -22
View File
@@ -13,6 +13,7 @@ from axis.streammanager import SIGNAL_PLAYING, STATE_STOPPED
from homeassistant.components import mqtt
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
from homeassistant.components.mqtt.models import Message
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
@@ -25,6 +26,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.setup import async_when_setup
from .const import (
@@ -32,9 +34,11 @@ from .const import (
CONF_EVENTS,
CONF_MODEL,
CONF_STREAM_PROFILE,
CONF_VIDEO_SOURCE,
DEFAULT_EVENTS,
DEFAULT_STREAM_PROFILE,
DEFAULT_TRIGGER_TIME,
DEFAULT_VIDEO_SOURCE,
DOMAIN as AXIS_DOMAIN,
LOGGER,
PLATFORMS,
@@ -59,9 +63,24 @@ class AxisNetworkDevice:
@property
def host(self):
"""Return the host of this device."""
"""Return the host address of this device."""
return self.config_entry.data[CONF_HOST]
@property
def port(self):
"""Return the HTTP port of this device."""
return self.config_entry.data[CONF_PORT]
@property
def username(self):
"""Return the username of this device."""
return self.config_entry.data[CONF_USERNAME]
@property
def password(self):
"""Return the password of this device."""
return self.config_entry.data[CONF_PASSWORD]
@property
def model(self):
"""Return the model of this device."""
@@ -73,8 +92,8 @@ class AxisNetworkDevice:
return self.config_entry.data[CONF_NAME]
@property
def serial(self):
"""Return the serial number of this device."""
def unique_id(self):
"""Return the unique ID (serial number) of this device."""
return self.config_entry.unique_id
# Options
@@ -96,22 +115,27 @@ class AxisNetworkDevice:
"""Config entry option defining minimum number of seconds to keep trigger high."""
return self.config_entry.options.get(CONF_TRIGGER_TIME, DEFAULT_TRIGGER_TIME)
@property
def option_video_source(self):
"""Config entry option defining what video source camera platform should use."""
return self.config_entry.options.get(CONF_VIDEO_SOURCE, DEFAULT_VIDEO_SOURCE)
# Signals
@property
def signal_reachable(self):
"""Device specific event to signal a change in connection status."""
return f"axis_reachable_{self.serial}"
return f"axis_reachable_{self.unique_id}"
@property
def signal_new_event(self):
"""Device specific event to signal new device event available."""
return f"axis_new_event_{self.serial}"
return f"axis_new_event_{self.unique_id}"
@property
def signal_new_address(self):
"""Device specific event to signal a change in device address."""
return f"axis_new_address_{self.serial}"
return f"axis_new_address_{self.unique_id}"
# Callbacks
@@ -150,8 +174,8 @@ class AxisNetworkDevice:
device_registry = await self.hass.helpers.device_registry.async_get_registry()
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
connections={(CONNECTION_NETWORK_MAC, self.serial)},
identifiers={(AXIS_DOMAIN, self.serial)},
connections={(CONNECTION_NETWORK_MAC, self.unique_id)},
identifiers={(AXIS_DOMAIN, self.unique_id)},
manufacturer=ATTR_MANUFACTURER,
model=f"{self.model} {self.product_type}",
name=self.name,
@@ -168,7 +192,9 @@ class AxisNetworkDevice:
if status.get("data", {}).get("status", {}).get("state") == "active":
self.listeners.append(
await mqtt.async_subscribe(hass, f"{self.serial}/#", self.mqtt_message)
await mqtt.async_subscribe(
hass, f"{self.api.vapix.serial_number}/#", self.mqtt_message
)
)
@callback
@@ -177,7 +203,7 @@ class AxisNetworkDevice:
self.disconnect_from_stream()
event = mqtt_json_to_event(message.payload)
self.api.event.process_event(event)
self.api.event.update([event])
# Setup and teardown methods
@@ -186,17 +212,23 @@ class AxisNetworkDevice:
try:
self.api = await get_device(
self.hass,
host=self.config_entry.data[CONF_HOST],
port=self.config_entry.data[CONF_PORT],
username=self.config_entry.data[CONF_USERNAME],
password=self.config_entry.data[CONF_PASSWORD],
host=self.host,
port=self.port,
username=self.username,
password=self.password,
)
except CannotConnect as err:
raise ConfigEntryNotReady from err
except Exception: # pylint: disable=broad-except
LOGGER.error("Unknown error connecting with Axis device on %s", self.host)
except AuthenticationRequired:
self.hass.async_create_task(
self.hass.config_entries.flow.async_init(
AXIS_DOMAIN,
context={"source": SOURCE_REAUTH},
data=self.config_entry.data,
)
)
return False
self.fw_version = self.api.vapix.firmware_version
@@ -239,12 +271,10 @@ class AxisNetworkDevice:
async def shutdown(self, event):
"""Stop the event stream."""
self.disconnect_from_stream()
await self.api.vapix.close()
async def async_reset(self):
"""Reset this device to default state."""
self.disconnect_from_stream()
await self.api.vapix.close()
unload_ok = all(
await asyncio.gather(
@@ -267,9 +297,10 @@ class AxisNetworkDevice:
async def get_device(hass, host, port, username, password):
"""Create a Axis device."""
session = get_async_client(hass, verify_ssl=False)
device = axis.AxisDevice(
Configuration(host, port=port, username=username, password=password)
Configuration(session, host, port=port, username=username, password=password)
)
try:
@@ -280,15 +311,12 @@ async def get_device(hass, host, port, username, password):
except axis.Unauthorized as err:
LOGGER.warning("Connected to device at %s but not registered.", host)
await device.vapix.close()
raise AuthenticationRequired from err
except (asyncio.TimeoutError, axis.RequestError) as err:
LOGGER.error("Error connecting to the Axis device at %s", host)
await device.vapix.close()
raise CannotConnect from err
except axis.AxisException as err:
LOGGER.exception("Unknown Axis communication error occurred")
await device.vapix.close()
raise AuthenticationRequired from err
+4 -1
View File
@@ -18,7 +18,10 @@ 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:
if (
device.api.vapix.light_control is None
or len(device.api.vapix.light_control) == 0
):
return
@callback
+13 -2
View File
@@ -3,12 +3,23 @@
"name": "Axis",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/axis",
"requirements": ["axis==41"],
"requirements": ["axis==43"],
"dhcp": [
{ "hostname": "axis-00408c*", "macaddress": "00408C*" },
{ "hostname": "axis-accc8e*", "macaddress": "ACCC8E*" },
{ "hostname": "axis-b8a44f*", "macaddress": "B8A44F*" }
],
"ssdp": [
{
"manufacturer": "AXIS"
}
],
"zeroconf": [
{ "type": "_axis-video._tcp.local.", "macaddress": "00408C*" },
{ "type": "_axis-video._tcp.local.", "macaddress": "ACCC8E*" },
{ "type": "_axis-video._tcp.local.", "macaddress": "B8A44F*" }
],
"after_dependencies": ["mqtt"],
"codeowners": ["@Kane610"]
"codeowners": ["@Kane610"],
"quality_scale": "platinum"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"config": {
"flow_title": "Axis device: {name} ({host})",
"flow_title": "{name} ({host})",
"step": {
"user": {
"title": "Set up Axis device",
@@ -36,6 +36,7 @@ def _send_blink_2fa_pin(auth, pin):
"""Send 2FA pin to blink servers."""
blink = Blink()
blink.auth = auth
blink.setup_login_ids()
blink.setup_urls()
return auth.send_auth_key(blink, pin)
@@ -169,7 +169,7 @@ def setup_scanner(hass, config, see, discovery_info=None):
):
handle = None
try:
adapter.start(reset_on_start=True)
adapter.start(reset_on_start=False)
_LOGGER.debug("Reading battery for Bluetooth LE device %s", mac)
bt_device = adapter.connect(mac)
# Try to get the handle; it will raise a BLEError exception if not available
@@ -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.13"],
"requirements": ["bimmer_connected==0.7.14"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true
}
+1 -3
View File
@@ -57,9 +57,7 @@ class BroadlinkDevice:
Triggered when the device is renamed on the frontend.
"""
device_registry = await dr.async_get_registry(hass)
device_entry = device_registry.async_get_device(
{(DOMAIN, entry.unique_id)}, set()
)
device_entry = device_registry.async_get_device({(DOMAIN, entry.unique_id)})
device_registry.async_update_device(device_entry.id, name=entry.title)
await hass.config_entries.async_reload(entry.entry_id)
+64 -1
View File
@@ -23,7 +23,10 @@ from homeassistant.components.remote import (
ATTR_DEVICE,
ATTR_NUM_REPEATS,
DEFAULT_DELAY_SECS,
DOMAIN as RM_DOMAIN,
PLATFORM_SCHEMA,
SERVICE_DELETE_COMMAND,
SUPPORT_DELETE_COMMAND,
SUPPORT_LEARN_COMMAND,
RemoteEntity,
)
@@ -48,6 +51,8 @@ COMMAND_TYPES = [COMMAND_TYPE_IR, COMMAND_TYPE_RF]
CODE_STORAGE_VERSION = 1
FLAG_STORAGE_VERSION = 1
CODE_SAVE_DELAY = 15
FLAG_SAVE_DELAY = 15
COMMAND_SCHEMA = vol.Schema(
@@ -74,6 +79,10 @@ SERVICE_LEARN_SCHEMA = COMMAND_SCHEMA.extend(
}
)
SERVICE_DELETE_SCHEMA = COMMAND_SCHEMA.extend(
{vol.Required(ATTR_DEVICE): vol.All(cv.string, vol.Length(min=1))}
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_HOST): cv.string}, extra=vol.ALLOW_EXTRA
)
@@ -149,7 +158,7 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity):
@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_LEARN_COMMAND
return SUPPORT_LEARN_COMMAND | SUPPORT_DELETE_COMMAND
@property
def device_info(self):
@@ -196,6 +205,11 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity):
except ValueError as err:
raise ValueError("Invalid code") from err
@callback
def get_codes(self):
"""Return a dictionary of codes."""
return self._codes
@callback
def get_flags(self):
"""Return a dictionary of toggle flags.
@@ -434,3 +448,52 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity):
self.hass.components.persistent_notification.async_dismiss(
notification_id="learn_command"
)
async def async_delete_command(self, **kwargs):
"""Delete a list of commands from a remote."""
kwargs = SERVICE_DELETE_SCHEMA(kwargs)
commands = kwargs[ATTR_COMMAND]
device = kwargs[ATTR_DEVICE]
service = f"{RM_DOMAIN}.{SERVICE_DELETE_COMMAND}"
if not self._state:
_LOGGER.warning(
"%s canceled: %s entity is turned off",
service,
self.entity_id,
)
return
try:
codes = self._codes[device]
except KeyError as err:
err_msg = f"Device not found: {repr(device)}"
_LOGGER.error("Failed to call %s. %s", service, err_msg)
raise ValueError(err_msg) from err
cmds_not_found = []
for command in commands:
try:
del codes[command]
except KeyError:
cmds_not_found.append(command)
if cmds_not_found:
if len(cmds_not_found) == 1:
err_msg = f"Command not found: {repr(cmds_not_found[0])}"
else:
err_msg = f"Commands not found: {repr(cmds_not_found)}"
if len(cmds_not_found) == len(commands):
_LOGGER.error("Failed to call %s. %s", service, err_msg)
raise ValueError(err_msg)
_LOGGER.error("Error during %s. %s", service, err_msg)
# Clean up
if not codes:
del self._codes[device]
if self._flags.pop(device, None) is not None:
self._flag_storage.async_delay_save(self.get_flags, FLAG_SAVE_DELAY)
self._code_storage.async_delay_save(self.get_codes, CODE_SAVE_DELAY)
@@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/brother",
"codeowners": ["@bieniu"],
"requirements": ["brother==0.1.20"],
"zeroconf": [{"type": "_printer._tcp.local.", "name":"Brother*"}],
"zeroconf": [{ "type": "_printer._tcp.local.", "name": "brother*" }],
"config_flow": true,
"quality_scale": "platinum"
}
@@ -2,6 +2,6 @@
"domain": "caldav",
"name": "CalDAV",
"documentation": "https://www.home-assistant.io/integrations/caldav",
"requirements": ["caldav==0.6.1"],
"requirements": ["caldav==0.7.1"],
"codeowners": []
}
+4 -1
View File
@@ -127,11 +127,14 @@ class CanaryCamera(CoordinatorEntity, Camera):
async def async_camera_image(self):
"""Return a still image response from the camera."""
await self.hass.async_add_executor_job(self.renew_live_stream_session)
live_stream_url = await self.hass.async_add_executor_job(
getattr, self._live_stream_session, "live_stream_url"
)
ffmpeg = ImageFrame(self._ffmpeg.binary)
image = await asyncio.shield(
ffmpeg.get_image(
self._live_stream_session.live_stream_url,
live_stream_url,
output_format=IMAGE_JPEG,
extra_cmd=self._ffmpeg_arguments,
)
@@ -2,7 +2,7 @@
"domain": "canary",
"name": "Canary",
"documentation": "https://www.home-assistant.io/integrations/canary",
"requirements": ["py-canary==0.5.0"],
"requirements": ["py-canary==0.5.1"],
"dependencies": ["ffmpeg"],
"codeowners": [],
"config_flow": true
@@ -25,6 +25,7 @@ def discover_chromecast(hass: HomeAssistant, info: ChromecastInfo):
_LOGGER.error("Discovered chromecast without uuid %s", info)
return
info = info.fill_out_missing_chromecast_info()
if info.uuid in hass.data[KNOWN_CHROMECAST_INFO_KEY]:
_LOGGER.debug("Discovered update for known chromecast %s", info)
else:
+81 -8
View File
@@ -1,7 +1,8 @@
"""Helpers to deal with Cast devices."""
from typing import Optional, Tuple
from typing import Optional
import attr
from pychromecast import dial
from pychromecast.const import CAST_MANUFACTURERS
from .const import DEFAULT_PORT
@@ -20,8 +21,10 @@ class ChromecastInfo:
uuid: Optional[str] = attr.ib(
converter=attr.converters.optional(str), default=None
) # always convert UUID to string if not None
_manufacturer = attr.ib(type=Optional[str], default=None)
model_name: str = attr.ib(default="")
friendly_name: Optional[str] = attr.ib(default=None)
is_dynamic_group = attr.ib(type=Optional[bool], default=None)
@property
def is_audio_group(self) -> bool:
@@ -29,17 +32,84 @@ class ChromecastInfo:
return self.port != DEFAULT_PORT
@property
def host_port(self) -> Tuple[str, int]:
"""Return the host+port tuple."""
return self.host, self.port
def is_information_complete(self) -> bool:
"""Return if all information is filled out."""
want_dynamic_group = self.is_audio_group
have_dynamic_group = self.is_dynamic_group is not None
have_all_except_dynamic_group = all(
attr.astuple(
self,
filter=attr.filters.exclude(
attr.fields(ChromecastInfo).is_dynamic_group
),
)
)
return have_all_except_dynamic_group and (
not want_dynamic_group or have_dynamic_group
)
@property
def manufacturer(self) -> str:
"""Return the manufacturer."""
if self._manufacturer:
return self._manufacturer
if not self.model_name:
return None
return CAST_MANUFACTURERS.get(self.model_name.lower(), "Google Inc.")
def fill_out_missing_chromecast_info(self) -> "ChromecastInfo":
"""Return a new ChromecastInfo object with missing attributes filled in.
Uses blocking HTTP / HTTPS.
"""
if self.is_information_complete:
# We have all information, no need to check HTTP API.
return self
# Fill out missing group information via HTTP API.
if self.is_audio_group:
is_dynamic_group = False
http_group_status = None
if self.uuid:
http_group_status = dial.get_multizone_status(
self.host,
services=self.services,
zconf=ChromeCastZeroconf.get_zeroconf(),
)
if http_group_status is not None:
is_dynamic_group = any(
str(g.uuid) == self.uuid
for g in http_group_status.dynamic_groups
)
return ChromecastInfo(
services=self.services,
host=self.host,
port=self.port,
uuid=self.uuid,
friendly_name=self.friendly_name,
model_name=self.model_name,
is_dynamic_group=is_dynamic_group,
)
# Fill out some missing information (friendly_name, uuid) via HTTP dial.
http_device_status = dial.get_device_status(
self.host, services=self.services, zconf=ChromeCastZeroconf.get_zeroconf()
)
if http_device_status is None:
# HTTP dial didn't give us any new information.
return self
return ChromecastInfo(
services=self.services,
host=self.host,
port=self.port,
uuid=(self.uuid or http_device_status.uuid),
friendly_name=(self.friendly_name or http_device_status.friendly_name),
manufacturer=(self.manufacturer or http_device_status.manufacturer),
model_name=(self.model_name or http_device_status.model_name),
)
class ChromeCastZeroconf:
"""Class to hold a zeroconf instance."""
@@ -65,19 +135,22 @@ class CastStatusListener:
potentially arrive. This class allows invalidating past chromecast objects.
"""
def __init__(self, cast_device, chromecast, mz_mgr):
def __init__(self, cast_device, chromecast, mz_mgr, mz_only=False):
"""Initialize the status listener."""
self._cast_device = cast_device
self._uuid = chromecast.uuid
self._valid = True
self._mz_mgr = mz_mgr
if cast_device._cast_info.is_audio_group:
self._mz_mgr.add_multizone(chromecast)
if mz_only:
return
chromecast.register_status_listener(self)
chromecast.socket_client.media_controller.register_status_listener(self)
chromecast.register_connection_listener(self)
if cast_device._cast_info.is_audio_group:
self._mz_mgr.add_multizone(chromecast)
else:
if not cast_device._cast_info.is_audio_group:
self._mz_mgr.register_listener(chromecast.uuid, self)
def new_cast_status(self, cast_status):
+1 -1
View File
@@ -3,7 +3,7 @@
"name": "Google Cast",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/cast",
"requirements": ["pychromecast==7.6.0"],
"requirements": ["pychromecast==8.0.0"],
"after_dependencies": ["cloud", "http", "media_source", "plex", "tts", "zeroconf"],
"zeroconf": ["_googlecast._tcp.local."],
"codeowners": ["@emontnemery"]
+146 -15
View File
@@ -63,6 +63,7 @@ from .const import (
DOMAIN as CAST_DOMAIN,
KNOWN_CHROMECAST_INFO_KEY,
SIGNAL_CAST_DISCOVERED,
SIGNAL_CAST_REMOVED,
SIGNAL_HASS_CAST_SHOW_VIEW,
)
from .discovery import setup_internal_discovery
@@ -115,6 +116,13 @@ def _async_create_cast_device(hass: HomeAssistantType, info: ChromecastInfo):
return None
# -> New cast device
added_casts.add(info.uuid)
if info.is_dynamic_group:
# This is a dynamic group, do not add it but connect to the service.
group = DynamicCastGroup(hass, info)
group.async_setup()
return None
return CastDevice(info)
@@ -206,8 +214,9 @@ class CastDevice(MediaPlayerEntity):
self.hass, SIGNAL_CAST_DISCOVERED, self._async_cast_discovered
)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._async_stop)
self.async_set_cast_info(self._cast_info)
self.hass.async_create_task(
async_create_catching_coro(self.async_set_cast_info(self._cast_info))
async_create_catching_coro(self.async_connect_to_chromecast())
)
self._cast_view_remove_handler = async_dispatcher_connect(
@@ -228,15 +237,13 @@ class CastDevice(MediaPlayerEntity):
self._cast_view_remove_handler()
self._cast_view_remove_handler = None
async def async_set_cast_info(self, cast_info):
"""Set the cast information and set up the chromecast object."""
def async_set_cast_info(self, cast_info):
"""Set the cast information."""
self._cast_info = cast_info
if self._chromecast is not None:
# Only setup the chromecast once, added elements to services
# will automatically be picked up.
return
async def async_connect_to_chromecast(self):
"""Set up the chromecast object."""
_LOGGER.debug(
"[%s %s] Connecting to cast device by service %s",
@@ -248,9 +255,9 @@ class CastDevice(MediaPlayerEntity):
pychromecast.get_chromecast_from_service,
(
self.services,
cast_info.uuid,
cast_info.model_name,
cast_info.friendly_name,
self._cast_info.uuid,
self._cast_info.model_name,
self._cast_info.friendly_name,
None,
None,
),
@@ -777,16 +784,12 @@ class CastDevice(MediaPlayerEntity):
async def _async_cast_discovered(self, discover: ChromecastInfo):
"""Handle discovery of new Chromecast."""
if self._cast_info.uuid is None:
# We can't handle empty UUIDs
return
if self._cast_info.uuid != discover.uuid:
# Discovered is not our device.
return
_LOGGER.debug("Discovered chromecast with same UUID: %s", discover)
await self.async_set_cast_info(discover)
self.async_set_cast_info(discover)
async def _async_stop(self, event):
"""Disconnect socket on Home Assistant stop."""
@@ -808,3 +811,131 @@ class CastDevice(MediaPlayerEntity):
self._chromecast.register_handler(controller)
self._hass_cast_controller.show_lovelace_view(view_path, url_path)
class DynamicCastGroup:
"""Representation of a Cast device on the network - for dynamic cast groups."""
def __init__(self, hass, cast_info: ChromecastInfo):
"""Initialize the cast device."""
self.hass = hass
self._cast_info = cast_info
self.services = cast_info.services
self._chromecast: Optional[pychromecast.Chromecast] = None
self.mz_mgr = None
self._status_listener: Optional[CastStatusListener] = None
self._add_remove_handler = None
self._del_remove_handler = None
def async_setup(self):
"""Create chromecast object."""
self._add_remove_handler = async_dispatcher_connect(
self.hass, SIGNAL_CAST_DISCOVERED, self._async_cast_discovered
)
self._del_remove_handler = async_dispatcher_connect(
self.hass, SIGNAL_CAST_REMOVED, self._async_cast_removed
)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._async_stop)
self.async_set_cast_info(self._cast_info)
self.hass.async_create_task(
async_create_catching_coro(self.async_connect_to_chromecast())
)
async def async_tear_down(self) -> None:
"""Disconnect Chromecast object."""
await self._async_disconnect()
if self._cast_info.uuid is not None:
# Remove the entity from the added casts so that it can dynamically
# be re-added again.
self.hass.data[ADDED_CAST_DEVICES_KEY].remove(self._cast_info.uuid)
if self._add_remove_handler:
self._add_remove_handler()
self._add_remove_handler = None
if self._del_remove_handler:
self._del_remove_handler()
self._del_remove_handler = None
def async_set_cast_info(self, cast_info):
"""Set the cast information and set up the chromecast object."""
self._cast_info = cast_info
async def async_connect_to_chromecast(self):
"""Set the cast information and set up the chromecast object."""
_LOGGER.debug(
"[%s %s] Connecting to cast device by service %s",
"Dynamic group",
self._cast_info.friendly_name,
self.services,
)
chromecast = await self.hass.async_add_executor_job(
pychromecast.get_chromecast_from_service,
(
self.services,
self._cast_info.uuid,
self._cast_info.model_name,
self._cast_info.friendly_name,
None,
None,
),
ChromeCastZeroconf.get_zeroconf(),
)
self._chromecast = chromecast
if CAST_MULTIZONE_MANAGER_KEY not in self.hass.data:
self.hass.data[CAST_MULTIZONE_MANAGER_KEY] = MultizoneManager()
self.mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY]
self._status_listener = CastStatusListener(self, chromecast, self.mz_mgr, True)
self._chromecast.start()
async def _async_disconnect(self):
"""Disconnect Chromecast object if it is set."""
if self._chromecast is None:
# Can't disconnect if not connected.
return
_LOGGER.debug(
"[%s %s] Disconnecting from chromecast socket",
"Dynamic group",
self._cast_info.friendly_name,
)
await self.hass.async_add_executor_job(self._chromecast.disconnect)
self._invalidate()
def _invalidate(self):
"""Invalidate some attributes."""
self._chromecast = None
self.mz_mgr = None
if self._status_listener is not None:
self._status_listener.invalidate()
self._status_listener = None
async def _async_cast_discovered(self, discover: ChromecastInfo):
"""Handle discovery of new Chromecast."""
if self._cast_info.uuid != discover.uuid:
# Discovered is not our device.
return
_LOGGER.debug("Discovered dynamic group with same UUID: %s", discover)
self.async_set_cast_info(discover)
async def _async_cast_removed(self, discover: ChromecastInfo):
"""Handle removal of Chromecast."""
if self._cast_info.uuid != discover.uuid:
# Removed is not our device.
return
if not discover.services:
# Clean up the dynamic group
_LOGGER.debug("Clean up dynamic group: %s", discover)
await self.async_tear_down()
async def _async_stop(self, event):
"""Disconnect socket on Home Assistant stop."""
await self._async_disconnect()
+16 -3
View File
@@ -5,7 +5,7 @@ import logging
import aiohttp
import async_timeout
from hass_nabucasa import cloud_api
from hass_nabucasa import Cloud, cloud_api
from homeassistant.components.alexa import (
config as alexa_config,
@@ -14,7 +14,7 @@ from homeassistant.components.alexa import (
state_report as alexa_state_report,
)
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, HTTP_BAD_REQUEST
from homeassistant.core import callback, split_entity_id
from homeassistant.core import HomeAssistant, callback, split_entity_id
from homeassistant.helpers import entity_registry
from homeassistant.helpers.event import async_call_later
from homeassistant.util.dt import utcnow
@@ -32,10 +32,18 @@ SYNC_DELAY = 1
class AlexaConfig(alexa_config.AbstractConfig):
"""Alexa Configuration."""
def __init__(self, hass, config, prefs: CloudPreferences, cloud):
def __init__(
self,
hass: HomeAssistant,
config: dict,
cloud_user: str,
prefs: CloudPreferences,
cloud: Cloud,
):
"""Initialize the Alexa config."""
super().__init__(hass)
self._config = config
self._cloud_user = cloud_user
self._prefs = prefs
self._cloud = cloud
self._token = None
@@ -85,6 +93,11 @@ class AlexaConfig(alexa_config.AbstractConfig):
"""Return entity config."""
return self._config.get(CONF_ENTITY_CONFIG) or {}
@callback
def user_identifier(self):
"""Return an identifier for the user that represents this config."""
return self._cloud_user
def should_expose(self, entity_id):
"""If an entity should be exposed."""
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
+10 -6
View File
@@ -79,13 +79,15 @@ class CloudClient(Interface):
"""Return true if we want start a remote connection."""
return self._prefs.remote_enabled
@property
def alexa_config(self) -> alexa_config.AlexaConfig:
async def get_alexa_config(self) -> alexa_config.AlexaConfig:
"""Return Alexa config."""
if self._alexa_config is None:
assert self.cloud is not None
cloud_user = await self._prefs.get_cloud_user()
self._alexa_config = alexa_config.AlexaConfig(
self._hass, self.alexa_user_config, self._prefs, self.cloud
self._hass, self.alexa_user_config, cloud_user, self._prefs, self.cloud
)
return self._alexa_config
@@ -110,8 +112,9 @@ class CloudClient(Interface):
async def enable_alexa(_):
"""Enable Alexa."""
aconf = await self.get_alexa_config()
try:
await self.alexa_config.async_enable_proactive_mode()
await aconf.async_enable_proactive_mode()
except aiohttp.ClientError as err: # If no internet available yet
if self._hass.is_running:
logging.getLogger(__package__).warning(
@@ -133,7 +136,7 @@ class CloudClient(Interface):
tasks = []
if self.alexa_config.enabled and self.alexa_config.should_report_state:
if self._prefs.alexa_enabled and self._prefs.alexa_report_state:
tasks.append(enable_alexa)
if self._prefs.google_enabled:
@@ -164,9 +167,10 @@ class CloudClient(Interface):
async def async_alexa_message(self, payload: Dict[Any, Any]) -> Dict[Any, Any]:
"""Process cloud alexa message to client."""
cloud_user = await self._prefs.get_cloud_user()
aconfig = await self.get_alexa_config()
return await alexa_sh.async_handle_message(
self._hass,
self.alexa_config,
aconfig,
payload,
context=Context(user_id=cloud_user),
enabled=self._prefs.alexa_enabled,
+2
View File
@@ -20,6 +20,8 @@ PREF_GOOGLE_LOCAL_WEBHOOK_ID = "google_local_webhook_id"
PREF_USERNAME = "username"
PREF_ALEXA_DEFAULT_EXPOSE = "alexa_default_expose"
PREF_GOOGLE_DEFAULT_EXPOSE = "google_default_expose"
PREF_TTS_DEFAULT_VOICE = "tts_default_voice"
DEFAULT_TTS_DEFAULT_VOICE = ("en-US", "female")
DEFAULT_DISABLE_2FA = False
DEFAULT_ALEXA_REPORT_STATE = False
DEFAULT_GOOGLE_REPORT_STATE = False
@@ -28,7 +28,7 @@ _LOGGER = logging.getLogger(__name__)
class CloudGoogleConfig(AbstractConfig):
"""HA Cloud Configuration for Google Assistant."""
def __init__(self, hass, config, cloud_user, prefs: CloudPreferences, cloud):
def __init__(self, hass, config, cloud_user: str, prefs: CloudPreferences, cloud):
"""Initialize the Google config."""
super().__init__(hass)
self._config = config
+20 -3
View File
@@ -8,6 +8,7 @@ import async_timeout
import attr
from hass_nabucasa import Cloud, auth, thingtalk
from hass_nabucasa.const import STATE_DISCONNECTED
from hass_nabucasa.voice import MAP_VOICE
import voluptuous as vol
from homeassistant.components import websocket_api
@@ -37,6 +38,7 @@ from .const import (
PREF_GOOGLE_DEFAULT_EXPOSE,
PREF_GOOGLE_REPORT_STATE,
PREF_GOOGLE_SECURE_DEVICES_PIN,
PREF_TTS_DEFAULT_VOICE,
REQUEST_TIMEOUT,
InvalidTrustedNetworks,
InvalidTrustedProxies,
@@ -115,6 +117,7 @@ async def async_setup(hass):
async_register_command(alexa_sync)
async_register_command(thingtalk_convert)
async_register_command(tts_info)
hass.http.register_view(GoogleActionsSyncView)
hass.http.register_view(CloudLoginView)
@@ -385,6 +388,9 @@ async def websocket_subscription(hass, connection, msg):
vol.Optional(PREF_ALEXA_DEFAULT_EXPOSE): [str],
vol.Optional(PREF_GOOGLE_DEFAULT_EXPOSE): [str],
vol.Optional(PREF_GOOGLE_SECURE_DEVICES_PIN): vol.Any(None, str),
vol.Optional(PREF_TTS_DEFAULT_VOICE): vol.All(
vol.Coerce(tuple), vol.In(MAP_VOICE)
),
}
)
async def websocket_update_prefs(hass, connection, msg):
@@ -397,9 +403,10 @@ async def websocket_update_prefs(hass, connection, msg):
# If we turn alexa linking on, validate that we can fetch access token
if changes.get(PREF_ALEXA_REPORT_STATE):
alexa_config = await cloud.client.get_alexa_config()
try:
with async_timeout.timeout(10):
await cloud.client.alexa_config.async_get_access_token()
await alexa_config.async_get_access_token()
except asyncio.TimeoutError:
connection.send_error(
msg["id"], "alexa_timeout", "Timeout validating Alexa access token."
@@ -555,7 +562,8 @@ async def google_assistant_update(hass, connection, msg):
async def alexa_list(hass, connection, msg):
"""List all alexa entities."""
cloud = hass.data[DOMAIN]
entities = alexa_entities.async_get_entities(hass, cloud.client.alexa_config)
alexa_config = await cloud.client.get_alexa_config()
entities = alexa_entities.async_get_entities(hass, alexa_config)
result = []
@@ -603,10 +611,11 @@ async def alexa_update(hass, connection, msg):
async def alexa_sync(hass, connection, msg):
"""Sync with Alexa."""
cloud = hass.data[DOMAIN]
alexa_config = await cloud.client.get_alexa_config()
with async_timeout.timeout(10):
try:
success = await cloud.client.alexa_config.async_sync_entities()
success = await alexa_config.async_sync_entities()
except alexa_errors.NoTokenAvailable:
connection.send_error(
msg["id"],
@@ -634,3 +643,11 @@ async def thingtalk_convert(hass, connection, msg):
)
except thingtalk.ThingTalkConversionError as err:
connection.send_error(msg["id"], ws_const.ERR_UNKNOWN_ERROR, str(err))
@websocket_api.websocket_command({"type": "cloud/tts/info"})
def tts_info(hass, connection, msg):
"""Fetch available tts info."""
connection.send_result(
msg["id"], {"languages": [(lang, gender.value) for lang, gender in MAP_VOICE]}
)
+1 -1
View File
@@ -2,7 +2,7 @@
"domain": "cloud",
"name": "Home Assistant Cloud",
"documentation": "https://www.home-assistant.io/integrations/cloud",
"requirements": ["hass-nabucasa==0.39.0"],
"requirements": ["hass-nabucasa==0.41.0"],
"dependencies": ["http", "webhook", "alexa"],
"after_dependencies": ["google_assistant"],
"codeowners": ["@home-assistant/cloud"]
+10
View File
@@ -12,6 +12,7 @@ from .const import (
DEFAULT_ALEXA_REPORT_STATE,
DEFAULT_EXPOSED_DOMAINS,
DEFAULT_GOOGLE_REPORT_STATE,
DEFAULT_TTS_DEFAULT_VOICE,
DOMAIN,
PREF_ALEXA_DEFAULT_EXPOSE,
PREF_ALEXA_ENTITY_CONFIGS,
@@ -30,6 +31,7 @@ from .const import (
PREF_GOOGLE_SECURE_DEVICES_PIN,
PREF_OVERRIDE_NAME,
PREF_SHOULD_EXPOSE,
PREF_TTS_DEFAULT_VOICE,
PREF_USERNAME,
InvalidTrustedNetworks,
InvalidTrustedProxies,
@@ -86,6 +88,7 @@ class CloudPreferences:
google_report_state=UNDEFINED,
alexa_default_expose=UNDEFINED,
google_default_expose=UNDEFINED,
tts_default_voice=UNDEFINED,
):
"""Update user preferences."""
prefs = {**self._prefs}
@@ -103,6 +106,7 @@ class CloudPreferences:
(PREF_GOOGLE_REPORT_STATE, google_report_state),
(PREF_ALEXA_DEFAULT_EXPOSE, alexa_default_expose),
(PREF_GOOGLE_DEFAULT_EXPOSE, google_default_expose),
(PREF_TTS_DEFAULT_VOICE, tts_default_voice),
):
if value is not UNDEFINED:
prefs[key] = value
@@ -203,6 +207,7 @@ class CloudPreferences:
PREF_GOOGLE_ENTITY_CONFIGS: self.google_entity_configs,
PREF_GOOGLE_REPORT_STATE: self.google_report_state,
PREF_GOOGLE_SECURE_DEVICES_PIN: self.google_secure_devices_pin,
PREF_TTS_DEFAULT_VOICE: self.tts_default_voice,
}
@property
@@ -279,6 +284,11 @@ class CloudPreferences:
"""Return the published cloud webhooks."""
return self._prefs.get(PREF_CLOUDHOOKS, {})
@property
def tts_default_voice(self):
"""Return the default TTS voice."""
return self._prefs.get(PREF_TTS_DEFAULT_VOICE, DEFAULT_TTS_DEFAULT_VOICE)
async def get_cloud_user(self) -> str:
"""Return ID from Home Assistant Cloud system user."""
user = await self._load_cloud_user()
+18 -7
View File
@@ -12,13 +12,14 @@ CONF_GENDER = "gender"
SUPPORT_LANGUAGES = list({key[0] for key in MAP_VOICE})
DEFAULT_LANG = "en-US"
DEFAULT_GENDER = "female"
def validate_lang(value):
"""Validate chosen gender or language."""
lang = value[CONF_LANG]
lang = value.get(CONF_LANG)
if lang is None:
return value
gender = value.get(CONF_GENDER)
if gender is None:
@@ -35,7 +36,7 @@ def validate_lang(value):
PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_LANG, default=DEFAULT_LANG): str,
vol.Optional(CONF_LANG): str,
vol.Optional(CONF_GENDER): str,
}
),
@@ -48,8 +49,8 @@ async def async_get_engine(hass, config, discovery_info=None):
cloud: Cloud = hass.data[DOMAIN]
if discovery_info is not None:
language = DEFAULT_LANG
gender = DEFAULT_GENDER
language = None
gender = None
else:
language = config[CONF_LANG]
gender = config[CONF_GENDER]
@@ -67,6 +68,16 @@ class CloudProvider(Provider):
self._language = language
self._gender = gender
if self._language is not None:
return
self._language, self._gender = cloud.client.prefs.tts_default_voice
cloud.client.prefs.async_listen_updates(self._sync_prefs)
async def _sync_prefs(self, prefs):
"""Sync preferences."""
self._language, self._gender = prefs.tts_default_voice
@property
def default_language(self):
"""Return the default language."""
@@ -2,6 +2,6 @@
"domain": "comfoconnect",
"name": "Zehnder ComfoAir Q",
"documentation": "https://www.home-assistant.io/integrations/comfoconnect",
"requirements": ["pycomfoconnect==0.3"],
"requirements": ["pycomfoconnect==0.4"],
"codeowners": ["@michaelarnauts"]
}
@@ -3,6 +3,7 @@ import logging
from pycomfoconnect import (
SENSOR_BYPASS_STATE,
SENSOR_CURRENT_RMOT,
SENSOR_DAYS_TO_REPLACE_FILTER,
SENSOR_FAN_EXHAUST_DUTY,
SENSOR_FAN_EXHAUST_FLOW,
@@ -15,6 +16,9 @@ from pycomfoconnect import (
SENSOR_HUMIDITY_OUTDOOR,
SENSOR_HUMIDITY_SUPPLY,
SENSOR_POWER_CURRENT,
SENSOR_POWER_TOTAL,
SENSOR_PREHEATER_POWER_CURRENT,
SENSOR_PREHEATER_POWER_TOTAL,
SENSOR_TEMPERATURE_EXHAUST,
SENSOR_TEMPERATURE_EXTRACT,
SENSOR_TEMPERATURE_OUTDOOR,
@@ -26,9 +30,11 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
ATTR_DEVICE_CLASS,
CONF_RESOURCES,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
ENERGY_KILO_WATT_HOUR,
PERCENTAGE,
POWER_WATT,
TEMP_CELSIUS,
@@ -45,6 +51,7 @@ ATTR_AIR_FLOW_EXHAUST = "air_flow_exhaust"
ATTR_AIR_FLOW_SUPPLY = "air_flow_supply"
ATTR_BYPASS_STATE = "bypass_state"
ATTR_CURRENT_HUMIDITY = "current_humidity"
ATTR_CURRENT_RMOT = "current_rmot"
ATTR_CURRENT_TEMPERATURE = "current_temperature"
ATTR_DAYS_TO_REPLACE_FILTER = "days_to_replace_filter"
ATTR_EXHAUST_FAN_DUTY = "exhaust_fan_duty"
@@ -54,6 +61,9 @@ ATTR_EXHAUST_TEMPERATURE = "exhaust_temperature"
ATTR_OUTSIDE_HUMIDITY = "outside_humidity"
ATTR_OUTSIDE_TEMPERATURE = "outside_temperature"
ATTR_POWER_CURRENT = "power_usage"
ATTR_POWER_TOTAL = "power_total"
ATTR_PREHEATER_POWER_CURRENT = "preheater_power_usage"
ATTR_PREHEATER_POWER_TOTAL = "preheater_power_total"
ATTR_SUPPLY_FAN_DUTY = "supply_fan_duty"
ATTR_SUPPLY_FAN_SPEED = "supply_fan_speed"
ATTR_SUPPLY_HUMIDITY = "supply_humidity"
@@ -72,7 +82,7 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Inside Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ICON: None,
ATTR_ID: SENSOR_TEMPERATURE_EXTRACT,
ATTR_MULTIPLIER: 0.1,
},
@@ -80,14 +90,22 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Inside Humidity",
ATTR_UNIT: PERCENTAGE,
ATTR_ICON: "mdi:water-percent",
ATTR_ICON: None,
ATTR_ID: SENSOR_HUMIDITY_EXTRACT,
},
ATTR_CURRENT_RMOT: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Current RMOT",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: None,
ATTR_ID: SENSOR_CURRENT_RMOT,
ATTR_MULTIPLIER: 0.1,
},
ATTR_OUTSIDE_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Outside Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ICON: None,
ATTR_ID: SENSOR_TEMPERATURE_OUTDOOR,
ATTR_MULTIPLIER: 0.1,
},
@@ -95,14 +113,14 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Outside Humidity",
ATTR_UNIT: PERCENTAGE,
ATTR_ICON: "mdi:water-percent",
ATTR_ICON: None,
ATTR_ID: SENSOR_HUMIDITY_OUTDOOR,
},
ATTR_SUPPLY_TEMPERATURE: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Supply Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ICON: None,
ATTR_ID: SENSOR_TEMPERATURE_SUPPLY,
ATTR_MULTIPLIER: 0.1,
},
@@ -110,7 +128,7 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Supply Humidity",
ATTR_UNIT: PERCENTAGE,
ATTR_ICON: "mdi:water-percent",
ATTR_ICON: None,
ATTR_ID: SENSOR_HUMIDITY_SUPPLY,
},
ATTR_SUPPLY_FAN_SPEED: {
@@ -145,7 +163,7 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_LABEL: "Exhaust Temperature",
ATTR_UNIT: TEMP_CELSIUS,
ATTR_ICON: "mdi:thermometer",
ATTR_ICON: None,
ATTR_ID: SENSOR_TEMPERATURE_EXHAUST,
ATTR_MULTIPLIER: 0.1,
},
@@ -153,7 +171,7 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
ATTR_LABEL: "Exhaust Humidity",
ATTR_UNIT: PERCENTAGE,
ATTR_ICON: "mdi:water-percent",
ATTR_ICON: None,
ATTR_ID: SENSOR_HUMIDITY_EXHAUST,
},
ATTR_AIR_FLOW_SUPPLY: {
@@ -188,9 +206,30 @@ SENSOR_TYPES = {
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
ATTR_LABEL: "Power usage",
ATTR_UNIT: POWER_WATT,
ATTR_ICON: "mdi:flash",
ATTR_ICON: None,
ATTR_ID: SENSOR_POWER_CURRENT,
},
ATTR_POWER_TOTAL: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
ATTR_LABEL: "Power total",
ATTR_UNIT: ENERGY_KILO_WATT_HOUR,
ATTR_ICON: None,
ATTR_ID: SENSOR_POWER_TOTAL,
},
ATTR_PREHEATER_POWER_CURRENT: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_POWER,
ATTR_LABEL: "Preheater power usage",
ATTR_UNIT: POWER_WATT,
ATTR_ICON: None,
ATTR_ID: SENSOR_PREHEATER_POWER_CURRENT,
},
ATTR_PREHEATER_POWER_TOTAL: {
ATTR_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
ATTR_LABEL: "Preheater power total",
ATTR_UNIT: ENERGY_KILO_WATT_HOUR,
ATTR_ICON: None,
ATTR_ID: SENSOR_PREHEATER_POWER_TOTAL,
},
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@@ -318,7 +318,9 @@ async def config_entry_update(hass, connection, msg):
@websocket_api.require_admin
@websocket_api.async_response
@websocket_api.websocket_command({"type": "config_entries/ignore_flow", "flow_id": str})
@websocket_api.websocket_command(
{"type": "config_entries/ignore_flow", "flow_id": str, "title": str}
)
async def ignore_config_flow(hass, connection, msg):
"""Ignore a config flow."""
flow = next(
@@ -345,7 +347,7 @@ async def ignore_config_flow(hass, connection, msg):
await hass.config_entries.flow.async_init(
flow["handler"],
context={"source": config_entries.SOURCE_IGNORE},
data={"unique_id": flow["context"]["unique_id"]},
data={"unique_id": flow["context"]["unique_id"], "title": msg["title"]},
)
connection.send_result(msg["id"])
@@ -102,7 +102,9 @@ async def websocket_update_entity(hass, connection, msg):
if hass.states.get(msg["new_entity_id"]) is not None:
connection.send_message(
websocket_api.error_message(
msg["id"], "invalid_info", "Entity is already registered"
msg["id"],
"invalid_info",
"Entity with this ID is already registered",
)
)
return
@@ -3,7 +3,7 @@
"name": "Daikin AC",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/daikin",
"requirements": ["pydaikin==2.4.0"],
"requirements": ["pydaikin==2.4.1"],
"codeowners": ["@fredrike"],
"zeroconf": ["_dkapi._tcp.local."],
"quality_scale": "platinum"
@@ -2,7 +2,7 @@
"domain": "debugpy",
"name": "Remote Python Debugger",
"documentation": "https://www.home-assistant.io/integrations/debugpy",
"requirements": ["debugpy==1.2.0"],
"requirements": ["debugpy==1.2.1"],
"codeowners": ["@frenck"],
"quality_scale": "internal"
}
@@ -174,6 +174,18 @@ class DeconzFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=self.bridge_id, data=self.deconz_config)
async def async_step_reauth(self, config: dict):
"""Trigger a reauthentication flow."""
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {CONF_HOST: config[CONF_HOST]}
self.deconz_config = {
CONF_HOST: config[CONF_HOST],
CONF_PORT: config[CONF_PORT],
}
return await self.async_step_link()
async def async_step_ssdp(self, discovery_info):
"""Handle a discovered deCONZ bridge."""
if (
@@ -75,6 +75,7 @@ CONF_SIDE_6 = "side_6"
HUE_DIMMER_REMOTE_MODEL_GEN1 = "RWL020"
HUE_DIMMER_REMOTE_MODEL_GEN2 = "RWL021"
HUE_DIMMER_REMOTE_MODEL_GEN3 = "RWL022"
HUE_DIMMER_REMOTE = {
(CONF_SHORT_PRESS, CONF_TURN_ON): {CONF_EVENT: 1000},
(CONF_SHORT_RELEASE, CONF_TURN_ON): {CONF_EVENT: 1002},
@@ -362,6 +363,7 @@ AQARA_OPPLE_6_BUTTONS = {
REMOTES = {
HUE_DIMMER_REMOTE_MODEL_GEN1: HUE_DIMMER_REMOTE,
HUE_DIMMER_REMOTE_MODEL_GEN2: HUE_DIMMER_REMOTE,
HUE_DIMMER_REMOTE_MODEL_GEN3: HUE_DIMMER_REMOTE,
HUE_BUTTON_REMOTE_MODEL: HUE_BUTTON_REMOTE,
HUE_TAP_REMOTE_MODEL: HUE_TAP_REMOTE,
FRIENDS_OF_HUE_SWITCH_MODEL: FRIENDS_OF_HUE_SWITCH,
@@ -417,7 +419,15 @@ async def async_validate_trigger_config(hass, config):
or device.model not in REMOTES
or trigger not in REMOTES[device.model]
):
raise InvalidDeviceAutomationConfig
if not device:
raise InvalidDeviceAutomationConfig(
f"deCONZ trigger {trigger} device with id "
f"{config[CONF_DEVICE_ID]} not found"
)
raise InvalidDeviceAutomationConfig(
f"deCONZ trigger {trigger} is not valid for device "
f"{device} ({config[CONF_DEVICE_ID]})"
)
return config
+12 -5
View File
@@ -4,6 +4,7 @@ import asyncio
import async_timeout
from pydeconz import DeconzSession, errors
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryNotReady
@@ -19,7 +20,7 @@ from .const import (
DEFAULT_ALLOW_CLIP_SENSOR,
DEFAULT_ALLOW_DECONZ_GROUPS,
DEFAULT_ALLOW_NEW_DEVICES,
DOMAIN,
DOMAIN as DECONZ_DOMAIN,
LOGGER,
NEW_GROUP,
NEW_LIGHT,
@@ -34,7 +35,7 @@ from .errors import AuthenticationRequired, CannotConnect
@callback
def get_gateway_from_config_entry(hass, config_entry):
"""Return gateway with a matching bridge id."""
return hass.data[DOMAIN][config_entry.unique_id]
return hass.data[DECONZ_DOMAIN][config_entry.unique_id]
class DeconzGateway:
@@ -152,7 +153,7 @@ class DeconzGateway:
# Gateway service
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
identifiers={(DOMAIN, self.api.config.bridgeid)},
identifiers={(DECONZ_DOMAIN, self.api.config.bridgeid)},
manufacturer="Dresden Elektronik",
model=self.api.config.modelid,
name=self.api.config.name,
@@ -173,8 +174,14 @@ class DeconzGateway:
except CannotConnect as err:
raise ConfigEntryNotReady from err
except Exception as err: # pylint: disable=broad-except
LOGGER.error("Error connecting with deCONZ gateway: %s", err, exc_info=True)
except AuthenticationRequired:
self.hass.async_create_task(
self.hass.config_entries.flow.async_init(
DECONZ_DOMAIN,
context={"source": SOURCE_REAUTH},
data=self.config_entry.data,
)
)
return False
for component in SUPPORTED_PLATFORMS:
+17 -7
View File
@@ -1,4 +1,7 @@
"""Support for deCONZ lights."""
from pydeconz.light import Light
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
@@ -105,21 +108,22 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
super().__init__(device, gateway)
self._features = 0
self.update_features(self._device)
if self._device.brightness is not None:
def update_features(self, device):
"""Calculate supported features of device."""
if device.brightness is not None:
self._features |= SUPPORT_BRIGHTNESS
self._features |= SUPPORT_FLASH
self._features |= SUPPORT_TRANSITION
if self._device.ct is not None:
if device.ct is not None:
self._features |= SUPPORT_COLOR_TEMP
if self._device.xy is not None or (
self._device.hue is not None and self._device.sat is not None
):
if device.xy is not None or (device.hue is not None and device.sat is not None):
self._features |= SUPPORT_COLOR
if self._device.effect is not None:
if device.effect is not None:
self._features |= SUPPORT_EFFECT
@property
@@ -146,7 +150,8 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
if self._device.colormode in ("xy", "hs"):
if self._device.xy:
return color_util.color_xy_to_hs(*self._device.xy)
return (self._device.hue / 65535 * 360, self._device.sat / 255 * 100)
if self._device.hue and self._device.sat:
return (self._device.hue / 65535 * 360, self._device.sat / 255 * 100)
return None
@property
@@ -250,6 +255,11 @@ class DeconzGroup(DeconzBaseLight):
super().__init__(device, gateway)
for light_id in device.lights:
light = gateway.api.lights[light_id]
if light.ZHATYPE == Light.ZHATYPE:
self.update_features(light)
@property
def unique_id(self):
"""Return a unique identifier for this device."""
+143
View File
@@ -0,0 +1,143 @@
"""Describe deCONZ logbook events."""
from typing import Callable, Optional
from homeassistant.const import ATTR_DEVICE_ID, CONF_EVENT
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.event import Event
from .const import DOMAIN as DECONZ_DOMAIN
from .deconz_event import CONF_DECONZ_EVENT, DeconzEvent
from .device_trigger import (
CONF_BOTH_BUTTONS,
CONF_BOTTOM_BUTTONS,
CONF_BUTTON_1,
CONF_BUTTON_2,
CONF_BUTTON_3,
CONF_BUTTON_4,
CONF_CLOSE,
CONF_DIM_DOWN,
CONF_DIM_UP,
CONF_DOUBLE_PRESS,
CONF_DOUBLE_TAP,
CONF_LEFT,
CONF_LONG_PRESS,
CONF_LONG_RELEASE,
CONF_MOVE,
CONF_OPEN,
CONF_QUADRUPLE_PRESS,
CONF_QUINTUPLE_PRESS,
CONF_RIGHT,
CONF_ROTATE_FROM_SIDE_1,
CONF_ROTATE_FROM_SIDE_2,
CONF_ROTATE_FROM_SIDE_3,
CONF_ROTATE_FROM_SIDE_4,
CONF_ROTATE_FROM_SIDE_5,
CONF_ROTATE_FROM_SIDE_6,
CONF_ROTATED,
CONF_ROTATED_FAST,
CONF_ROTATION_STOPPED,
CONF_SHAKE,
CONF_SHORT_PRESS,
CONF_SHORT_RELEASE,
CONF_SIDE_1,
CONF_SIDE_2,
CONF_SIDE_3,
CONF_SIDE_4,
CONF_SIDE_5,
CONF_SIDE_6,
CONF_TOP_BUTTONS,
CONF_TRIPLE_PRESS,
CONF_TURN_OFF,
CONF_TURN_ON,
REMOTES,
_get_deconz_event_from_device_id,
)
ACTIONS = {
CONF_SHORT_PRESS: "Short press",
CONF_SHORT_RELEASE: "Short release",
CONF_LONG_PRESS: "Long press",
CONF_LONG_RELEASE: "Long release",
CONF_DOUBLE_PRESS: "Double press",
CONF_TRIPLE_PRESS: "Triple press",
CONF_QUADRUPLE_PRESS: "Quadruple press",
CONF_QUINTUPLE_PRESS: "Quintuple press",
CONF_ROTATED: "Rotated",
CONF_ROTATED_FAST: "Rotated fast",
CONF_ROTATION_STOPPED: "Rotated stopped",
CONF_MOVE: "Move",
CONF_DOUBLE_TAP: "Double tap",
CONF_SHAKE: "Shake",
CONF_ROTATE_FROM_SIDE_1: "Rotate from side 1",
CONF_ROTATE_FROM_SIDE_2: "Rotate from side 2",
CONF_ROTATE_FROM_SIDE_3: "Rotate from side 3",
CONF_ROTATE_FROM_SIDE_4: "Rotate from side 4",
CONF_ROTATE_FROM_SIDE_5: "Rotate from side 5",
CONF_ROTATE_FROM_SIDE_6: "Rotate from side 6",
}
INTERFACES = {
CONF_TURN_ON: "Turn on",
CONF_TURN_OFF: "Turn off",
CONF_DIM_UP: "Dim up",
CONF_DIM_DOWN: "Dim down",
CONF_LEFT: "Left",
CONF_RIGHT: "Right",
CONF_OPEN: "Open",
CONF_CLOSE: "Close",
CONF_BOTH_BUTTONS: "Both buttons",
CONF_TOP_BUTTONS: "Top buttons",
CONF_BOTTOM_BUTTONS: "Bottom buttons",
CONF_BUTTON_1: "Button 1",
CONF_BUTTON_2: "Button 2",
CONF_BUTTON_3: "Button 3",
CONF_BUTTON_4: "Button 4",
CONF_SIDE_1: "Side 1",
CONF_SIDE_2: "Side 2",
CONF_SIDE_3: "Side 3",
CONF_SIDE_4: "Side 4",
CONF_SIDE_5: "Side 5",
CONF_SIDE_6: "Side 6",
}
def _get_device_event_description(modelid: str, event: str) -> tuple:
"""Get device event description."""
device_event_descriptions: dict = REMOTES[modelid]
for event_type_tuple, event_dict in device_event_descriptions.items():
if event == event_dict[CONF_EVENT]:
return event_type_tuple
@callback
def async_describe_events(
hass: HomeAssistant,
async_describe_event: Callable[[str, str, Callable[[Event], dict]], None],
) -> None:
"""Describe logbook events."""
@callback
def async_describe_deconz_event(event: Event) -> dict:
"""Describe deCONZ logbook event."""
deconz_event: Optional[DeconzEvent] = _get_deconz_event_from_device_id(
hass, event.data[ATTR_DEVICE_ID]
)
if deconz_event.device.modelid not in REMOTES:
return {
"name": f"{deconz_event.device.name}",
"message": f"fired event '{event.data[CONF_EVENT]}'.",
}
action, interface = _get_device_event_description(
deconz_event.device.modelid, event.data[CONF_EVENT]
)
return {
"name": f"{deconz_event.device.name}",
"message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired.",
}
async_describe_event(DECONZ_DOMAIN, CONF_DECONZ_EVENT, async_describe_deconz_event)
@@ -6,6 +6,7 @@
"automation",
"cloud",
"counter",
"dhcp",
"frontend",
"history",
"input_boolean",
+16
View File
@@ -3,7 +3,11 @@ from homeassistant.components.cover import (
ATTR_POSITION,
ATTR_TILT_POSITION,
SUPPORT_CLOSE,
SUPPORT_CLOSE_TILT,
SUPPORT_OPEN,
SUPPORT_OPEN_TILT,
SUPPORT_SET_TILT_POSITION,
SUPPORT_STOP_TILT,
CoverEntity,
)
from homeassistant.core import callback
@@ -26,6 +30,18 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
device_class="garage",
supported_features=(SUPPORT_OPEN | SUPPORT_CLOSE),
),
DemoCover(
hass,
"cover_5",
"Pergola Roof",
tilt_position=60,
supported_features=(
SUPPORT_OPEN_TILT
| SUPPORT_STOP_TILT
| SUPPORT_CLOSE_TILT
| SUPPORT_SET_TILT_POSITION
),
),
]
)
@@ -3,7 +3,7 @@
"name": "Denon AVR Network Receivers",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/denonavr",
"requirements": ["denonavr==0.9.9", "getmac==0.8.2"],
"requirements": ["denonavr==0.9.10", "getmac==0.8.2"],
"codeowners": ["@scarface-4711", "@starkillerOG"],
"ssdp": [
{
@@ -13,7 +13,7 @@ from homeassistant.const import (
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from .const import ATTR_SOURCE_TYPE, DOMAIN, LOGGER
from .const import ATTR_HOST_NAME, ATTR_IP, ATTR_MAC, ATTR_SOURCE_TYPE, DOMAIN, LOGGER
async def async_setup_entry(hass, entry):
@@ -130,6 +130,21 @@ class TrackerEntity(BaseTrackerEntity):
class ScannerEntity(BaseTrackerEntity):
"""Represent a tracked device that is on a scanned network."""
@property
def ip_address(self) -> str:
"""Return the primary ip address of the device."""
return None
@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return None
@property
def hostname(self) -> str:
"""Return hostname of the device."""
return None
@property
def state(self):
"""Return the state of the device."""
@@ -141,3 +156,17 @@ class ScannerEntity(BaseTrackerEntity):
def is_connected(self):
"""Return true if the device is connected to the network."""
raise NotImplementedError
@property
def state_attributes(self):
"""Return the device state attributes."""
attr = {}
attr.update(super().state_attributes)
if self.ip_address is not None:
attr[ATTR_IP] = self.ip_address
if self.mac_address is not None:
attr[ATTR_MAC] = self.mac_address
if self.hostname is not None:
attr[ATTR_HOST_NAME] = self.hostname
return attr
@@ -34,3 +34,4 @@ ATTR_LOCATION_NAME = "location_name"
ATTR_MAC = "mac"
ATTR_SOURCE_TYPE = "source_type"
ATTR_CONSIDER_HOME = "consider_home"
ATTR_IP = "ip"
+285
View File
@@ -0,0 +1,285 @@
"""The dhcp integration."""
from abc import abstractmethod
import fnmatch
from ipaddress import ip_address as make_ip_address
import logging
import os
import threading
from scapy.config import conf
from scapy.error import Scapy_Exception
from scapy.layers.dhcp import DHCP
from scapy.layers.l2 import Ether
from scapy.sendrecv import AsyncSniffer
from homeassistant.components.device_tracker.const import (
ATTR_HOST_NAME,
ATTR_IP,
ATTR_MAC,
ATTR_SOURCE_TYPE,
DOMAIN as DEVICE_TRACKER_DOMAIN,
SOURCE_TYPE_ROUTER,
)
from homeassistant.const import (
EVENT_HOMEASSISTANT_STARTED,
EVENT_HOMEASSISTANT_STOP,
STATE_HOME,
)
from homeassistant.core import Event, HomeAssistant, State, callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.event import async_track_state_added_domain
from homeassistant.loader import async_get_dhcp
from homeassistant.util.network import is_link_local
from .const import DOMAIN
FILTER = "udp and (port 67 or 68)"
REQUESTED_ADDR = "requested_addr"
MESSAGE_TYPE = "message-type"
HOSTNAME = "hostname"
MAC_ADDRESS = "macaddress"
IP_ADDRESS = "ip"
DHCP_REQUEST = 3
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up the dhcp component."""
async def _initialize(_):
address_data = {}
integration_matchers = await async_get_dhcp(hass)
watchers = []
for cls in (DHCPWatcher, DeviceTrackerWatcher):
watcher = cls(hass, address_data, integration_matchers)
await watcher.async_start()
watchers.append(watcher)
async def _async_stop(*_):
for watcher in watchers:
await watcher.async_stop()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _initialize)
return True
class WatcherBase:
"""Base class for dhcp and device tracker watching."""
def __init__(self, hass, address_data, integration_matchers):
"""Initialize class."""
super().__init__()
self.hass = hass
self._integration_matchers = integration_matchers
self._address_data = address_data
def process_client(self, ip_address, hostname, mac_address):
"""Process a client."""
if is_link_local(make_ip_address(ip_address)):
# Ignore self assigned addresses
return
data = self._address_data.get(ip_address)
if data and data[MAC_ADDRESS] == mac_address and data[HOSTNAME] == hostname:
# If the address data is the same no need
# to process it
return
self._address_data[ip_address] = {MAC_ADDRESS: mac_address, HOSTNAME: hostname}
self.process_updated_address_data(ip_address, self._address_data[ip_address])
def process_updated_address_data(self, ip_address, data):
"""Process the address data update."""
lowercase_hostname = data[HOSTNAME].lower()
uppercase_mac = data[MAC_ADDRESS].upper()
_LOGGER.debug(
"Processing updated address data for %s: mac=%s hostname=%s",
ip_address,
uppercase_mac,
lowercase_hostname,
)
for entry in self._integration_matchers:
if MAC_ADDRESS in entry and not fnmatch.fnmatch(
uppercase_mac, entry[MAC_ADDRESS]
):
continue
if HOSTNAME in entry and not fnmatch.fnmatch(
lowercase_hostname, entry[HOSTNAME]
):
continue
_LOGGER.debug("Matched %s against %s", data, entry)
self.create_task(
self.hass.config_entries.flow.async_init(
entry["domain"],
context={"source": DOMAIN},
data={IP_ADDRESS: ip_address, **data},
)
)
@abstractmethod
def create_task(self, task):
"""Pass a task to async_add_task based on which context we are in."""
class DeviceTrackerWatcher(WatcherBase):
"""Class to watch dhcp data from routers."""
def __init__(self, hass, address_data, integration_matchers):
"""Initialize class."""
super().__init__(hass, address_data, integration_matchers)
self._unsub = None
async def async_stop(self):
"""Stop watching for new device trackers."""
if self._unsub:
self._unsub()
self._unsub = None
async def async_start(self):
"""Stop watching for new device trackers."""
self._unsub = async_track_state_added_domain(
self.hass, [DEVICE_TRACKER_DOMAIN], self._async_process_device_event
)
for state in self.hass.states.async_all(DEVICE_TRACKER_DOMAIN):
self._async_process_device_state(state)
@callback
def _async_process_device_event(self, event: Event):
"""Process a device tracker state change event."""
self._async_process_device_state(event.data.get("new_state"))
@callback
def _async_process_device_state(self, state: State):
"""Process a device tracker state."""
if state.state != STATE_HOME:
return
attributes = state.attributes
if attributes.get(ATTR_SOURCE_TYPE) != SOURCE_TYPE_ROUTER:
return
ip_address = attributes.get(ATTR_IP)
hostname = attributes.get(ATTR_HOST_NAME)
mac_address = attributes.get(ATTR_MAC)
if ip_address is None or hostname is None or mac_address is None:
return
self.process_client(ip_address, hostname, _format_mac(mac_address))
def create_task(self, task):
"""Pass a task to async_create_task since we are in async context."""
self.hass.async_create_task(task)
class DHCPWatcher(WatcherBase):
"""Class to watch dhcp requests."""
def __init__(self, hass, address_data, integration_matchers):
"""Initialize class."""
super().__init__(hass, address_data, integration_matchers)
self._sniffer = None
self._started = threading.Event()
async def async_stop(self):
"""Stop watching for new device trackers."""
await self.hass.async_add_executor_job(self._stop)
def _stop(self):
"""Stop the thread."""
if self._started.is_set():
self._sniffer.stop()
async def async_start(self):
"""Start watching for dhcp packets."""
try:
_verify_l2socket_creation_permission()
except (Scapy_Exception, OSError) as ex:
if os.geteuid() == 0:
_LOGGER.error("Cannot watch for dhcp packets: %s", ex)
else:
_LOGGER.debug(
"Cannot watch for dhcp packets without root or CAP_NET_RAW: %s", ex
)
return
self._sniffer = AsyncSniffer(
filter=FILTER,
started_callback=self._started.set,
prn=self.handle_dhcp_packet,
store=0,
)
self._sniffer.start()
def handle_dhcp_packet(self, packet):
"""Process a dhcp packet."""
if DHCP not in packet:
return
options = packet[DHCP].options
request_type = _decode_dhcp_option(options, MESSAGE_TYPE)
if request_type != DHCP_REQUEST:
# DHCP request
return
ip_address = _decode_dhcp_option(options, REQUESTED_ADDR)
hostname = _decode_dhcp_option(options, HOSTNAME)
mac_address = _format_mac(packet[Ether].src)
if ip_address is None or hostname is None or mac_address is None:
return
self.process_client(ip_address, hostname, mac_address)
def create_task(self, task):
"""Pass a task to hass.add_job since we are in a thread."""
self.hass.add_job(task)
def _decode_dhcp_option(dhcp_options, key):
"""Extract and decode data from a packet option."""
for option in dhcp_options:
if len(option) < 2 or option[0] != key:
continue
value = option[1]
if value is None or key != HOSTNAME:
return value
# hostname is unicode
try:
return value.decode()
except (AttributeError, UnicodeDecodeError):
return None
def _format_mac(mac_address):
"""Format a mac address for matching."""
return format_mac(mac_address).replace(":", "")
def _verify_l2socket_creation_permission():
"""Create a socket using the scapy configured l2socket.
Try to create the socket
to see if we have permissions
since AsyncSniffer will do it another
thread so we will not be able to capture
any permission or bind errors.
"""
conf.L2socket()
+3
View File
@@ -0,0 +1,3 @@
"""Constants for the dhcp integration."""
DOMAIN = "dhcp"
@@ -0,0 +1,11 @@
{
"domain": "dhcp",
"name": "DHCP Discovery",
"documentation": "https://www.home-assistant.io/integrations/dhcp",
"requirements": [
"scapy==2.4.4"
],
"codeowners": [
"@bdraco"
]
}
@@ -2,6 +2,6 @@
"domain": "discord",
"name": "Discord",
"documentation": "https://www.home-assistant.io/integrations/discord",
"requirements": ["discord.py==1.5.1"],
"requirements": ["discord.py==1.6.0"],
"codeowners": []
}
+27 -4
View File
@@ -16,10 +16,15 @@ import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_TOKEN): cv.string})
ATTR_EMBED = "embed"
ATTR_EMBED_AUTHOR = "author"
ATTR_EMBED_FIELDS = "fields"
ATTR_EMBED_FOOTER = "footer"
ATTR_EMBED_THUMBNAIL = "thumbnail"
ATTR_IMAGES = "images"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_TOKEN): cv.string})
def get_service(hass, config, discovery_info=None):
"""Get the Discord notification service."""
@@ -43,16 +48,21 @@ class DiscordNotificationService(BaseNotificationService):
async def async_send_message(self, message, **kwargs):
"""Login to Discord, send message to channel(s) and log out."""
discord.VoiceClient.warn_nacl = False
discord_bot = discord.Client()
images = None
embedding = None
if ATTR_TARGET not in kwargs:
_LOGGER.error("No target specified")
return None
data = kwargs.get(ATTR_DATA) or {}
if ATTR_EMBED in data:
embedding = data[ATTR_EMBED]
fields = embedding.get(ATTR_EMBED_FIELDS)
if ATTR_IMAGES in data:
images = []
@@ -86,7 +96,20 @@ class DiscordNotificationService(BaseNotificationService):
files = []
for image in images:
files.append(discord.File(image))
await channel.send(message, files=files)
if embedding:
embed = discord.Embed(**embedding)
if fields:
for field_num, field_name in enumerate(fields):
embed.add_field(**fields[field_num])
if ATTR_EMBED_FOOTER in embedding:
embed.set_footer(**embedding[ATTR_EMBED_FOOTER])
if ATTR_EMBED_AUTHOR in embedding:
embed.set_author(**embedding[ATTR_EMBED_AUTHOR])
if ATTR_EMBED_THUMBNAIL in embedding:
embed.set_thumbnail(**embedding[ATTR_EMBED_THUMBNAIL])
await channel.send(message, files=files, embed=embed)
else:
await channel.send(message, files=files)
except (discord.errors.HTTPException, discord.errors.NotFound) as error:
_LOGGER.warning("Communication error: %s", error)
await discord_bot.logout()
@@ -281,7 +281,9 @@ class DlnaDmrDevice(MediaPlayerEntity):
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._device.volume_level
if self._device.has_volume_level:
return self._device.volume_level
return 0
@catch_request_errors()
async def async_set_volume_level(self, volume):

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