Compare commits

...

409 Commits

Author SHA1 Message Date
Franck Nijhof
adcb0260e0 Bumped version to 1.0.0b0 2020-12-02 22:07:58 +01:00
Marvin Wichmann
ce056656f8 Implement new Google TTS API via dedicated library (#43863)
Co-authored-by: Joakim Sørensen <hi@ludeeus.dev>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-02 22:03:31 +01:00
Erik Montnemery
30baf333c3 Improve handling of disabled devices (#43864) 2020-12-02 21:20:14 +01:00
Vladimír Záhradník
d518db1c95 Improve custom datatype parsing in Modbus sensor and climate (#42354) 2020-12-02 21:16:30 +01:00
Bram Kragten
5e3941badb Updated frontend to 20201202.0 (#43862) 2020-12-02 20:51:51 +01:00
Franck Nijhof
f2a371257d Translation update 2020-12-02 20:51:05 +01:00
Vladimir Dronnikov
a4b1990487 Add telegram_bot.send_voice service (#43433)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2020-12-02 20:44:02 +01:00
Martin Hjelmare
9043b7b214 Add ozw add-on discovery and mqtt client (#43838) 2020-12-02 20:03:29 +01:00
Kiall Mac Innes
8efa9c5097 Add support for MQTT Scenes (#42639) 2020-12-02 19:42:28 +01:00
Dermot Duffy
39601090ba Automatically select "Solid" effect in Hyperion (#43799) 2020-12-02 19:40:49 +01:00
Maciej Bieniek
b294e1c98c Add support for system health to AccuWeather integration (#43277) 2020-12-02 19:36:16 +01:00
Maciej Bieniek
86043b2957 Add support for system health to Airly integrarion (#43220) 2020-12-02 19:32:42 +01:00
Wictor
5fa00244da Allow members of allowed groups to talk to telegram_bot (#43241) 2020-12-02 19:25:35 +01:00
Quentame
519ec64522 Bump Synology DSM to 1.0.1 (#43860) 2020-12-02 19:12:38 +01:00
Robert Svensson
a8f0ad1dd8 Allow creating deCONZ config entry even when no bridge id is available 2020-12-02 19:10:47 +01:00
jjlawren
f2f935506e Use Plex websocket payloads to reduce overhead (#42332)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-02 19:00:13 +01:00
Robert Svensson
6e8efe2b67 Add fan support to deCONZ climate platform (#43721) 2020-12-02 18:08:46 +01:00
Jeff Irion
efacda6c33 Bump androidtv to 0.0.56 (#43859) 2020-12-02 17:47:09 +01:00
Robert Svensson
25db1dac23 Add preset support to deCONZ climate platform (#43722) 2020-12-02 17:14:05 +01:00
Pierre Ståhl
edb246d696 Refactor Apple TV integration (#31952)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-12-02 17:01:55 +01:00
Paulus Schoutsen
58648019c6 Deprecate the use of keyring and credstash (#43854) 2020-12-02 16:55:33 +01:00
Robert Svensson
ac2c01d20c deCONZ improve options updating entities (#42320) 2020-12-02 16:21:27 +01:00
Paulus Schoutsen
ff4897a09e Fix using execute on the notify_leaving_zone (#43858) 2020-12-02 16:21:00 +01:00
epenet
55edc9f858 Cleanup unique_id on onewire integration (#43783)
* Update construction of unique_id

* Move shared logic into OneWireBaseEntity
2020-12-02 15:11:20 +01:00
Paulus Schoutsen
06626af337 Guard for when refreshing token fails (#43855) 2020-12-02 14:55:47 +01:00
Joakim Sørensen
15b5057569 Increase timeout for snapshot upload (#43851) 2020-12-02 14:34:17 +01:00
Philip Allgaier
6fadc3e140 Make "invalid password" error message clearer (#43853) 2020-12-02 14:24:47 +01:00
Shulyaka
f744f7c34e Add new number entity integration (#42735)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2020-12-02 13:50:48 +01:00
Paulus Schoutsen
6c9c280bbb Migrate notify-leaving-zone to use mobile app device action (#43832) 2020-12-02 13:07:04 +01:00
Franck Nijhof
b092430d5b Merge branch 'master' into dev 2020-12-02 12:32:16 +01:00
Paulus Schoutsen
f3bb243b1d Do not warn for weak referenced entities (#43848) 2020-12-02 11:18:08 +01:00
Paulus Schoutsen
648f9e100d Use light turn on service (#43847) 2020-12-02 10:53:26 +01:00
Andre Lengwenus
93055884b7 Bump pypck to 0.7.7 (#43824) 2020-12-02 10:49:50 +01:00
Fabian Affolter
bbe9874320 Upgrade pylast to 4.0.0 (#43830) 2020-12-02 10:47:45 +01:00
Franck Nijhof
834f3603bc Correct service not found exception message (#43846) 2020-12-02 10:32:25 +01:00
Fabian Affolter
fec0d26d55 Upgrade TwitterAPI to 2.6.2.1 (#43833) 2020-12-02 10:20:12 +01:00
Chris Talkington
891edec73b Add test for is_internal_request (#43841) 2020-12-02 10:12:48 +01:00
etheralm
cf286d1c51 Bump libpurecool to 0.6.4 (#43779) 2020-12-02 09:51:46 +01:00
Bram Kragten
d93687b5ac Update area and target selectors add sequence selector (#43831)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-02 09:30:49 +01:00
Andreas Oberritter
3efda93875 Support more edl21 devices and sensors (#43603)
* edl21: Handle 1-0:96.50.1*1 and 1-0:96.1.0*255

* edl21: Use 1-0:96.1.0*255 as alternative electricity ID

* edl21: Fix copy-paste error in comments

* edl21: Add active amperage sensors

* edl21: Bump pysml to 0.0.3 to fix format of electricity ID
2020-12-01 22:50:09 -06:00
Chris
26892a629d Add ozw scene_instance to scene_activated (#43829)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-12-02 01:23:08 +01:00
HomeAssistant Azure
fc42f59b0b [ci skip] Translation update 2020-12-02 00:03:14 +00:00
Alexei Chetroi
032d569cd1 Add Analog cluster for Lumi plugs (#43817) 2020-12-01 14:34:07 -05:00
Paulus Schoutsen
0ee86ea8e4 Another try to get rid of Shelly flaky test (#43821) 2020-12-01 19:26:43 +01:00
Robert Svensson
1faef0a4d4 Make simple deCONZ thermostats work (#43781) 2020-12-01 18:49:58 +01:00
Andre Lengwenus
9ace7269ae Fix wrong temperature setting in LCN climate (#43818) 2020-12-01 18:45:08 +01:00
Paulus Schoutsen
1c9c99571e Use !input instead of !placeholder (#43820)
* Use !input instead of !placeholder

* Update input name

* Lint

* Move tests around
2020-12-01 18:21:36 +01:00
Paulus Schoutsen
7d23ff6511 Add device action to mobile app to notify (#43814) 2020-12-01 18:14:01 +01:00
Simone Chemelli
52217f1f60 Move uptime from relative time to absolute time (#43623) 2020-12-01 17:28:59 +01:00
Franck Nijhof
00d0c3f98b Remove invalidation version from Airvisual (#43810) 2020-12-01 17:13:15 +01:00
Franck Nijhof
b1c11eaaf8 Fix config validation tests for upcoming beta (#43811) 2020-12-01 17:07:28 +01:00
Franck Nijhof
24ba5bcbaf Remove Ubee Router integration (ADR-0004) (#43809) 2020-12-01 17:06:49 +01:00
Abílio Costa
8f3989e7d9 Remove unused stuff from ZHA registries (#43786) 2020-12-01 09:21:25 -05:00
Paulus Schoutsen
69710cb664 Base area IDs on initial name (#43804) 2020-12-01 12:45:56 +01:00
Aaron Bach
aad16b8055 Bump aiorecollect to 0.2.2 (#43796) 2020-12-01 12:22:57 +01:00
Franck Nijhof
885f325812 Upgrade elgato to 1.0.0 (#43792) 2020-12-01 10:50:55 +01:00
Paulus Schoutsen
14620e1573 Use entity platform for Neato (#43772) 2020-12-01 09:28:41 +01:00
Paulus Schoutsen
935ec59c56 Migrate foscam to use entity platform entity services (#43775) 2020-12-01 09:28:30 +01:00
Sören
39e7b30ab6 Add lock.open service to nello (#42141) 2020-12-01 08:36:36 +01:00
Paulus Schoutsen
cf5be049b3 Warn when referencing missing devices/areas (#43787) 2020-12-01 08:01:27 +01:00
HomeAssistant Azure
cf9598fe4f [ci skip] Translation update 2020-12-01 00:03:00 +00:00
Erik Montnemery
d18a33020b Fix MQTT birth message deadlock (#43790) 2020-12-01 01:01:22 +01:00
Paulus Schoutsen
a5c79a1f84 Add support for device class in target selector (#43768) 2020-11-30 23:35:24 +01:00
Martin Hjelmare
2f73590714 Increase Supervisor add-on helper timeout (#43778) 2020-11-30 20:40:31 +01:00
Willem-Jan
ba4d630470 Add authentication support to bsblan (#42306) 2020-11-30 20:13:16 +01:00
Martin Hjelmare
434cec7a88 Pin pip < 20.3 (#43771) 2020-11-30 19:38:39 +01:00
Martin Hjelmare
3f5d7e85c3 Add show progress to ozw config flow (#43310) 2020-11-30 18:59:15 +01:00
Dermot Duffy
7ad2a6be30 Add hyperion config options flow (#43673) 2020-11-30 18:38:52 +01:00
Ernst Klamer
14d1466400 Add device information to solarlog integration (#43680)
* Update sensor.py

* Changed as requested

Thanks, tested and works ok.

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

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-11-30 09:40:43 -06:00
Diogo Gomes
3e24868a9e Add system health check to IPMA (#43762) 2020-11-30 16:24:18 +01:00
Paulus Schoutsen
0de9e8e952 Allow specifying device_id as target (#43767) 2020-11-30 14:27:02 +01:00
Alan Tse
e307e1315a Fix updating of Tesla switches after command (#43754)
closes #43454
2020-11-30 09:57:32 +01:00
Joakim Plate
aade4e63b8 Support asking covers to stop using google assistant (#43537) 2020-11-30 09:34:34 +01:00
springstan
f221bfae52 Report correct weather condition at night for OpenWeatherMap (#42982) 2020-11-30 09:32:06 +01:00
Allen Porter
945a0a9f7e Add nest device triggers for camera and doorbell events (#43548) 2020-11-30 09:19:42 +01:00
J. Nick Koston
2cbb93be43 Always keep the current recorder run when purging (#43733) 2020-11-30 09:13:50 +01:00
HomeAssistant Azure
533f22bb76 [ci skip] Translation update 2020-11-30 00:03:45 +00:00
Ron Klinkien
eab6a0508b Address FireServiceRota late code review (#43741)
* Address review comment from PR #38206

* Address review comment from PR #43638

* Address review comment from PR #43700

* isort fixed

* Better code for duty entity update

* Removed all pylint relative-beyond-top-level

* Removed logger entry from entity state method
2020-11-29 23:54:27 +01:00
Bas Nijholt
6eeb9c0e49 ZHA: remove unused 'from_cache' argument from 'async_get_state' and add 'async_update' (#42413)
* remove unused 'from_cache' argument from 'async_get_state'

* define async_update to use homeassistant.update_entity
2020-11-29 14:47:46 -05:00
David F. Mulcahey
54425ae0f3 Use the correct property for full init event (#43745) 2020-11-29 11:59:23 -05:00
Andre Lengwenus
493eaef616 Bump pypck to v0.7.6 (#43710)
* Bump pypck to v0.7.6

* Await commands to ensure that they are received.
2020-11-29 16:30:17 +01:00
HomeAssistant Azure
5462d6e798 [ci skip] Translation update 2020-11-29 00:04:09 +00:00
springstan
d5efb3e3de Remove temporary variable by only retrieving needed value (#42522) 2020-11-29 00:44:25 +01:00
Franck Nijhof
65ab8cbc71 Add support for multiple tags and devices in tag trigger (#43098)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-29 00:06:32 +01:00
Paulus Schoutsen
e406a47293 Allow configuring the delay in the motion light blueprint (#43737) 2020-11-28 23:56:15 +01:00
J. Nick Koston
1162d9a752 Create tables with a charset that can hold all expected data under mysql (#43732)
By default these tables are created with utf8 which can only hold 3 bytes. This
meant that all emjoi would trigger a MySQLdb._exceptions.OperationalError because
they are 4 bytes.

This will only fix the issue for users who recreate their tables.
2020-11-28 23:54:05 +01:00
Paulus Schoutsen
dd513147a5 Add a service target (#43725) 2020-11-28 23:33:32 +01:00
David F. Mulcahey
a2e1efca33 Add additional events to enhance the ZHA device pairing experience (#43729)
* support better feedback in the device pairing UI

* update pairing events
2020-11-28 17:29:56 -05:00
Ron Klinkien
2c6a72b3ad Add Response switch platform to FireServiceRota integration (#43700)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-28 21:42:51 +01:00
Paulus Schoutsen
898a07fa75 Make threshold binary sensor faster (#43695) 2020-11-28 10:19:10 -10:00
Erik Montnemery
5d7d2dee2a Bump hatasmota to 0.1.2 (#43719) 2020-11-28 10:18:06 -10:00
Maciej Bieniek
a4ae2d210e Bump Brother library to version 0.1.20 (#43628) 2020-11-28 10:17:16 -10:00
moinmoin-sh
337b8d279e Ensure MariaDB/MySQL can be purged and handle states being deleted out from under the recorder (#43610)
* MariaDB doesn't purge #42402

This addresses  home-assistant#42402
Relationships within table "states" and between tables "states" and "events " home-assistant#40467 prevent the purge from working correctly. The database increases w/o any purge.
This proposal sets related indices to NULL and permits deleting of rows.
Further explanations can be found here home-assistant#42402
This proposal also allows to purge the tables "events" and "states" in any order.

* Update models.py

Corrected for Black style requirements

* Update homeassistant/components/recorder/models.py

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

* Add the options to foreign key constraints

* purge old states when database gets deleted out from under us

* pylint

Co-authored-by: J. Nick Koston <nick@koston.org>
2020-11-28 08:42:29 -10:00
Paulus Schoutsen
cb96bd9d0b Blueprint config to override blueprint (#43724) 2020-11-28 13:19:58 +01:00
Robert Svensson
6fa3e287da Add support to control cooling in deCONZ climate platform (#43720)
* Add fan support

* Add HVAC cool support

* Fix Martins comment from #43607

* Add preset support

* Improve climate test coverage

* Remove fan support

* Remove preset support

* Remove last preset piece
2020-11-28 13:11:13 +01:00
cgtobi
65bc128c86 Bump pyatmo to v4.2.1 (#43713) 2020-11-28 09:29:16 +01:00
HomeAssistant Azure
d5c2ef5a94 [ci skip] Translation update 2020-11-28 00:03:36 +00:00
Alexei Chetroi
c42b650031 Bump up ZHA dependencies (#43707)
bellows==0.21.0
zigpy==0.28.1
zha-quirks==0.0.47
2020-11-27 16:39:44 -05:00
jjlawren
7c0e148b59 Proxy Plex media browser images (#43111)
Co-authored-by: rajlaud <50647620+rajlaud@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-27 22:14:19 +01:00
javicalle
e10762af9b Bump RFLink to v0.0.55 (#43704) 2020-11-27 21:11:57 +01:00
Aaron Bach
fd6a2b078b Fix exception upon lock initialization on V2 SimpliSafe systems (#43705) 2020-11-27 21:09:37 +01:00
Franck Nijhof
0d5dc4aeee Add system health check to Spotify (#43249) 2020-11-27 19:25:17 +01:00
Matthias Alphart
29091f4537 Update xknx to 0.15.6 (#43645) 2020-11-27 18:06:15 +01:00
Paulus Schoutsen
5b6d9abe2a Await callbacks to keep cleaner stacktraces (#43693) 2020-11-27 17:48:43 +01:00
Paulus Schoutsen
20ed40d7ad Use utcnow from date util for http.ban (#43686) 2020-11-27 17:04:52 +01:00
Paulus Schoutsen
d34753473d Use run_job for service helper (#43696) 2020-11-27 16:12:39 +01:00
shred86
bdb04dcb9d Add Abode MFA support (#43572) 2020-11-27 13:39:26 +01:00
Paulus Schoutsen
ea55051161 Stub finding custom integrations in tests (#43692) 2020-11-27 12:53:16 +01:00
Paulus Schoutsen
897ff31ffe Maybe fix flaky test (#43690) 2020-11-27 12:31:42 +01:00
David Bonnes
bb146680ce Eliminate evohome unhandled exceptions when client API call fails (#43681) 2020-11-27 12:05:51 +01:00
Paulus Schoutsen
3a17e22cfa Convert API integration to async setup (#43685) 2020-11-27 09:50:19 +01:00
Shay Levy
2498340e1f Fix Shelly uptime sensor (#43651)
Fix sensor to include time zone
Report new value only if delta > 5 seconds
Modify REST sensors class to use callable attributes
2020-11-27 09:40:06 +01:00
David Bonnes
5e3f4954f7 Code quality improvement for evohome (#43678) 2020-11-27 09:28:05 +01:00
David Bonnes
c2cc605343 Bugfix schedule assigned to wrong day of week (#43676) 2020-11-27 09:20:20 +01:00
Philip Allgaier
bf4e6a289a Add option to deactivate a user (#43463) 2020-11-27 09:13:16 +01:00
Erik Montnemery
a5e5c5c7e6 Exclude disabled entities from async_entries_for_device (#43665) 2020-11-27 09:03:44 +01:00
Paulus Schoutsen
f9fa24950b OAuth2 to use current request header (#43668) 2020-11-27 08:55:34 +01:00
Michael Thingnes
69c2818c56 Met.no: Fix for zero temp entries (#43684)
Addresses issue #43587
2020-11-27 08:07:49 +01:00
Bram Kragten
3bd0c7188c Updated frontend to 20201126.0 (#43682) 2020-11-27 01:16:16 +01:00
HomeAssistant Azure
301a3e5481 [ci skip] Translation update 2020-11-27 00:03:57 +00:00
Erik Montnemery
974e099e2a Small cleanup of Tasmota (#43642) 2020-11-26 22:34:26 +01:00
Franck Nijhof
deb45f7c90 Upgrade pre-commit to 2.9.2 (#43655) 2020-11-26 22:34:17 +01:00
Erik Montnemery
8ff10ad3b8 Fix MQTT threading bug (#43667) 2020-11-26 22:33:58 +01:00
Paulus Schoutsen
e1de36fda8 Fix check config (#43663) 2020-11-26 22:25:21 +01:00
Paulus Schoutsen
edf70e9f06 Make input_datetime better handle timezones (#43396)
* Make input_datetime better handle timezones

* Improve test coverage

* Apply suggestions from code review

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

* Revert change to time format

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-11-26 20:20:10 +01:00
Jasper Slits
f3033ec01d Add Roomba support for automatic emptying of bin (#43594) 2020-11-26 17:48:54 +01:00
Paulus Schoutsen
a15abf8802 Merge pull request #43664 from home-assistant/rc 2020-11-26 17:42:21 +01:00
Paulus Schoutsen
0ede3da686 Bumped version to 0.118.4 2020-11-26 16:02:28 +00:00
Pascal Vizeli
ecaa9d925b Fix deadlock if an integration from stage_1 fails (#43657) 2020-11-26 16:02:24 +00:00
Paulus Schoutsen
793fdb5317 Tweak template digit detection (#43621) 2020-11-26 16:02:23 +00:00
Paulus Schoutsen
c4108d4ef1 Disable parsing scientific/complex number notation in template type (#43170) 2020-11-26 16:02:22 +00:00
Sören
373ad44760 Bump avea to 1.5.1 (#43618) 2020-11-26 15:58:42 +00:00
epenet
44c8fce33b Fix duplicate check on onewire config flow (#43590) 2020-11-26 15:58:41 +00:00
Sébastien RAMAGE
94f7f70f2d Update zigpy-zigate to 0.7.3 (#43427)
* Update zigpy-zigate to 0.7.3
Fix probing pizigate

* Update zigpy-zigate to 0.7.3
Fix probing pizigate
2020-11-26 15:58:40 +00:00
Erik Montnemery
18135bb1a1 Make MQTT climate return PRESET_NONE when no preset is set (#43257) 2020-11-26 15:58:40 +00:00
Erik Montnemery
dc8364fd3a Support disabling devices (#43293) 2020-11-26 16:45:02 +01:00
Paulus Schoutsen
39efbcb815 Allow importing gist (#43659) 2020-11-26 16:00:50 +01:00
Bram Kragten
4aa181416c Add area selector, remove date selector (#43658)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-26 15:44:25 +01:00
Ron Klinkien
316a2750df Add Duty binary_sensor platform to FireServiceRota integration (#43638) 2020-11-26 15:42:55 +01:00
Paulus Schoutsen
eb3e5cf446 Suggest folder when importing blueprint and store source_url (#43650) 2020-11-26 11:46:59 +01:00
Pascal Vizeli
d4f9c1979f Fix deadlock if an integration from stage_1 fails (#43657) 2020-11-26 11:38:30 +01:00
HomeAssistant Azure
ed16c5078f [ci skip] Translation update 2020-11-26 00:03:15 +00:00
Olivér Falvai
c439d25ac3 Add location to summary sensor attributes (#43641) 2020-11-25 13:41:56 -07:00
Bram Kragten
8533d9cae0 Add default to inputs (#43636) 2020-11-25 20:05:43 +01:00
Bram Kragten
3767af14f3 Add more selectors (#43639) 2020-11-25 20:03:56 +01:00
Erik Montnemery
6706ea36de Add Tasmota cover (#43368)
* Add Tasmota cover

* Update tests

* Bump hatasmota to 0.1.0
2020-11-25 18:52:09 +01:00
Erik Montnemery
314497d013 Add Tasmota fan (#43353)
* Add Tasmota fan

* Bump hatasmota to 0.1.0

* Apply suggestions from code review
2020-11-25 17:40:32 +01:00
Joakim Plate
ac551179ae Fix flapping derivative tests where time would move between state changes (#43579) 2020-11-25 17:08:00 +01:00
Ron Klinkien
ea52ffc2d9 Add FireServiceRota/BrandweerRooster integration (#38206)
Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-25 16:38:49 +01:00
Paulus Schoutsen
1de2554f70 Tweak template digit detection (#43621) 2020-11-25 16:10:33 +01:00
Simone Chemelli
b7f36106df Add Shelly UNI ADC sensor (#43490) 2020-11-25 15:37:07 +01:00
Shay Levy
ebaf143cf6 Support for Shelly Input Events (#43479) 2020-11-25 15:29:01 +01:00
Paulus Schoutsen
b3be708db6 Add default config if not there (#43321)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-25 15:10:04 +01:00
Paulus Schoutsen
0cf3736162 Bump ha-ffmpeg to 3.0.2 (#43597) 2020-11-25 08:45:15 +01:00
Sören
c421555db3 Bump avea to 1.5.1 (#43618) 2020-11-25 08:25:09 +01:00
Teemu R
183154c017 Bump pyhs100 dependency to fix hs220 discoverability issues (#43619)
Fixes #39395
2020-11-25 08:21:48 +01:00
HomeAssistant Azure
cbfd8a5a14 [ci skip] Translation update 2020-11-25 00:03:05 +00:00
Allen Porter
745823dd55 Add nest SDM API camera/doorbell events (#42700)
* Add nest SDM API camera/doorbell events

Events are fired  when pubsub messages are received.   When messages
are received lookup a home assistant device id from the nest
device id, so that the home assistant device id can be included
in the event payload.

* Update homeassistant/components/nest/__init__.py

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

* Update nest code style based on PR feedback

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-24 14:34:43 -08:00
Matthias Alphart
7f9a7791bf update xknx to 0.15.4 (#43536) 2020-11-24 22:58:17 +01:00
epenet
48d9f1a61b Fix duplicate check on onewire config flow (#43590) 2020-11-24 21:44:31 +01:00
Robert Svensson
acb94b0b59 Add tilt support to deCONZ covers (#43607) 2020-11-24 21:42:11 +01:00
Paulus Schoutsen
dae286aed5 Upgrade foobot_async to 1.0.0 (#43611) 2020-11-24 21:25:23 +01:00
koolsb
aed6980b14 Fix Meraki API Auth (#43578) 2020-11-24 19:56:39 +01:00
Erik Montnemery
d493f18e50 Bump hatasmota to 0.1.1 (#43608) 2020-11-24 19:55:15 +01:00
springstan
5a892e8fca Add unknown_authorize_url_generation to base strings for config flows (#42484) 2020-11-24 12:00:16 -05:00
Allen Porter
3dd14e05e3 Update nest library and switch events to async (#43583) 2020-11-24 08:53:50 -07:00
Robert Svensson
7214d6517a Fix conversion of cover position between HASS and deCONZ (#43602) 2020-11-24 14:47:56 +01:00
Paulus Schoutsen
755f15abe3 Fix warning generated by surpetcare test (#43598) 2020-11-24 12:40:25 +01:00
JJdeVries
8f18af9e80 Decrease asuswrt connects per sensor (#43383)
* asuswrt: Decrease number of connects per sensor

* Changed implementation to DataUpdateCoordinator

* review changes

* update aioasuswrt dependency

* Apply suggestions from code review

Add explicit return None and fix type hint

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

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-11-24 04:50:57 +01:00
HomeAssistant Azure
fcd8684d40 [ci skip] Translation update 2020-11-24 00:04:19 +00:00
Franck Nijhof
c157fbef1e Bump version to 0.119.0dev0 (#43485) 2020-11-23 22:06:59 +01:00
Maikel Punie
059f1a35f2 Use references in config flow for solaredge (#43511) 2020-11-23 15:34:46 -05:00
starkillerOG
0c30abda61 Add Motion Blinds integration (#42989)
Co-authored-by: J. Nick Koston <nick@koston.org>
2020-11-23 10:33:14 -10:00
Denix
66efe92b3f Cannot use vcgencmd in HassOS (#42710) 2020-11-23 14:56:50 -05:00
Franck Nijhof
612e74703b Add webhook to default config (#43521) 2020-11-23 14:49:13 -05:00
Greg
65796eee2d Add codeowner to Enphase Envoy manifest (#43402) 2020-11-23 19:58:30 +01:00
Thibaut
b272e16b44 Fix RTS cover with set position available (#43555) 2020-11-23 08:13:52 -10:00
Oliver
0c64873c10 Update denonavr to 0.9.7 (#43546) 2020-11-23 06:54:06 -10:00
Paulus Schoutsen
d84d819b62 Merge pull request #43561 from home-assistant/rc 2020-11-23 15:37:34 +01:00
Franck Nijhof
9368ab5480 Upgrade sentry-sdk to 0.19.4 (#43504) 2020-11-23 08:09:17 -05:00
Robin Wohlers-Reichel
2fcd1a250b update solax to 0.2.5 (#43564) 2020-11-23 08:05:22 -05:00
Franck Nijhof
bb17d4552e Upgrade Docker base image to 2020.11.2 (#43560) 2020-11-23 12:10:48 +00:00
Franck Nijhof
a3d9a94339 Upgrade Docker base image to 2020.11.2 (#43560) 2020-11-23 13:10:30 +01:00
Robert Svensson
55cbd5aa0d Track deCONZ lib changes to light based devices (#43366)
* Improve control of covers

* Log backtrace if available

* Do not create entity for controller tool
Binary sensor should use state rather than is_tripped
Add some more tests to lights and sensors

* Bump dependency to v74

* Fix Balloobs comments
2020-11-23 11:37:11 +01:00
Daniel Hjelseth Høyer
3b105c415b Mill Heater: Add support for Energy consumption (#43523) 2020-11-23 10:26:50 +01:00
Anders Melchiorsen
47ff04d9ad Optimize reading of Sonos source mode (#43541) 2020-11-23 10:20:06 +01:00
Paulus Schoutsen
f823fac45c Bumped version to 0.118.3 2020-11-23 09:08:09 +00:00
Franck Nijhof
3955d64b6a Upgrade Docker base image to 2020.11.1 (#43538) 2020-11-23 09:05:33 +00:00
ahertz
a08f809c46 Bump sleepyq to 0.8.1 (#43505) 2020-11-23 09:05:32 +00:00
On Freund
e94111f725 Gracefully handle no uuid in kodi discovery (#43494) 2020-11-23 09:05:31 +00:00
Franck Nijhof
99399cfd29 Ensure Plex content_id in play_on_sonos service is a string (#43483) 2020-11-23 09:05:30 +00:00
Aaron Bach
882b1eb941 Fix unhandled exception when IQVIA API fails to return data (#43359) 2020-11-23 09:05:29 +00:00
Aaron Bach
92cd1373de Fix bug related to possibly missing task ID in Notion API data (#43330)
* Fix bug related to possibly missing task ID in Notion API data

* Calculate unique ID once

* Code review

* Simplify

* Code review
2020-11-23 09:05:28 +00:00
Kevin Fronczak
8ab15f6867 Update Blinkpy to fix non-updating blink mini cameras (#43549) 2020-11-23 09:42:28 +01:00
dependabot[bot]
b144a980da Bump actions/stale from v3.0.13 to v3.0.14 (#43551)
Bumps [actions/stale](https://github.com/actions/stale) from v3.0.13 to v3.0.14.
- [Release notes](https://github.com/actions/stale/releases)
- [Commits](https://github.com/actions/stale/compare/v3.0.13...87c2b794b9b47a9bec68ae03c01aeb572ffebdb1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-11-23 09:36:52 +01:00
Robert Svensson
ffe0c1cd58 Add device id to deconz_event (#43552) 2020-11-23 09:22:44 +01:00
Aaron Bach
a4f7b7d784 Fix bug related to possibly missing task ID in Notion API data (#43330)
* Fix bug related to possibly missing task ID in Notion API data

* Calculate unique ID once

* Code review

* Simplify

* Code review
2020-11-22 20:15:38 -07:00
HomeAssistant Azure
492ef81069 [ci skip] Translation update 2020-11-23 00:04:13 +00:00
Maciej Bieniek
885e393df9 Make Brother uptime sensor disabled by default (#43478) 2020-11-22 11:42:16 -10:00
Franck Nijhof
20db980695 Upgrade Docker base image to 2020.11.1 (#43538) 2020-11-22 22:39:07 +01:00
Anders Melchiorsen
9ffdec63cc Optimize Sonos current playing state (#43517) 2020-11-22 22:16:03 +01:00
Daniel Rheinbay
498654a1e0 Add vendor effects to Yeelight integration (#42711)
Add effects shipped by Yeelight in their apps, as these effects are quite popular and have been missing from Home Assistant.
2020-11-22 21:41:09 +01:00
Aaron Bach
0b5851e403 Clean up RainMachine config entry (#43508) 2020-11-22 14:22:47 +01:00
Lasath Fernando
86cf184903 Add seek support to plex media players (#43420)
Turns out plexapi lib already supports this, so we just need to
pass through the command.
2020-11-22 14:05:15 +01:00
Anders Melchiorsen
62da64867c Optimize Sonos favorites updates (#43516) 2020-11-22 14:04:46 +01:00
Greg
a633341dc9 Bump envoy version to 0.17.0 (#43498) 2020-11-22 14:02:27 +01:00
Aaron Bach
3ffd97acd4 Fix unhandled exception when IQVIA API fails to return data (#43359) 2020-11-22 13:59:23 +01:00
Aaron Bach
be39104a36 Re-organize OpenUV constants (#43453)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2020-11-22 12:50:22 +01:00
Anders Melchiorsen
7319c4692d Optimize Sonos queue position (#43514) 2020-11-22 12:25:13 +01:00
ahertz
d7e696b90a Bump sleepyq to 0.8.1 (#43505) 2020-11-22 12:21:27 +01:00
Franck Nijhof
a0a44f12a7 Clean up Solar-log review comments (#43503) 2020-11-22 05:40:31 +01:00
HomeAssistant Azure
acca35cdc4 [ci skip] Translation update 2020-11-22 00:04:54 +00:00
On Freund
b2c9bd2ca6 Gracefully handle no uuid in kodi discovery (#43494) 2020-11-21 20:47:57 +01:00
Crash
76eb5aeeb6 Add updated British Voices (#43496)
Say hello to Charlotte and James.
https://cloud.ibm.com/apidocs/text-to-speech?code=python#getvoice
2020-11-21 20:24:04 +01:00
Ernst Klamer
977ed942ba Deprecate YAML config for Solar-log (#43484)
Per ADR-0010, this PR deprecates YAML configuration for Solar-log. Users who already use the Solar-log integration do not need to take action, as their configuration has already been imported into the UI.
2020-11-21 17:50:46 +01:00
Franck Nijhof
a092b4c204 Ensure Plex content_id in play_on_sonos service is a string (#43483) 2020-11-21 16:32:55 +01:00
Xiaonan Shen
ca053d4499 Bump rpi-bad-power to 0.1.0 (#43476) 2020-11-21 13:21:51 +01:00
Jakub Kolář
c5803614e1 Upgrade discord.py to 1.5.1 (#43473)
* Upgrade discord.py to 1.5.1

discord.py 1.5.1 introduces support for Intents that are required by future bots and integrations. While this is not yet required by Home Assistant Discord integration it is needed in the future, because old API which discord.py 1.4.1 uses is now deprecated. Also it would solve dependency collision with my custom component discord_game.

* Upgrade discord.py to 1.5.1
2020-11-21 13:07:17 +01:00
Indu Prakash
7213d5f31b Support for multiple states in history_stats (#43416)
Co-authored-by: Indu Prakash <6459774+InduPrakash@users.noreply.github.com>
2020-11-21 12:44:37 +01:00
FlavorFx
f22f568169 Fix Luftdaten.info data retrieval (#43471)
Fix Luftdaten.info data retrieval
2020-11-21 11:52:34 +01:00
Franck Nijhof
db0dee1ab2 Add repeat mode support to Spotify (#43247) 2020-11-21 11:38:40 +01:00
Franck Nijhof
7c6e80952b Upgrade debugpy to 1.2.0 (#43328) 2020-11-21 11:37:41 +01:00
Aidan Timson
d7bf783da6 Add reauth support for OVO Energy (#38882) 2020-11-21 11:21:23 +01:00
Adam Król
cccd4d734c Fix ConnectTimeout during wolflink start (#43418) 2020-11-21 11:10:11 +01:00
Alan Murray
45618f8054 Bump acmeda dependency aiopulse to 0.4.2 (#43217) 2020-11-21 11:00:08 +01:00
Julien "_FrnchFrgg_" Rivaud
b2bd68a4a0 Avoid arbitrarily reducing ZHA climate temperature information (#43442)
The Zigbee specification mandates that temperatures are given in
1/100ths of degrees, and several devices are able to provide temperature
readings or accept target temperature settings with precision equal or
better than 0.1°C. Yet the ZHA climate entity implementation advertises
an hard-coded precision of 0.5°C.

Avoid arbitrary coarsening by advertising 0.1°C precision, as passing
through the full 0.01°C precision is probably not that useful.
2020-11-21 00:06:50 -05:00
Tom
db60a71603 Change Plugwise integration to plugwise module (#43036)
* Switch to plugwise module and forthcoming changes

* Adjusted according to review

* Fix leaving out domain for tests

* Add tests for exceptions

* Add more tests for exceptions

* Version bump

* Wording on test

* Catch-up with dev
2020-11-20 20:43:20 -06:00
Alexei Chetroi
e32669a2d9 Remove zigpy monkey patching (#43456)
* Use event handler for incoming messages from zigpy

* Bump up zha dependency
2020-11-20 20:02:52 -05:00
HomeAssistant Azure
43955d3aa8 [ci skip] Translation update 2020-11-21 00:08:32 +00:00
Ville Skyttä
cab186d28f Add ONVIF PTZ Stop support (#39734) 2020-11-20 22:59:11 +01:00
Aaron Bach
b4381c5005 Move Notion logger to a package logger (#43450) 2020-11-20 22:47:48 +01:00
Martin Hjelmare
22f63be30e Remove salt integration for webscraping (#43452) 2020-11-20 21:48:23 +01:00
Paulus Schoutsen
bed28ac2e7 Merge pull request #43440 from home-assistant/rc 2020-11-20 21:41:40 +01:00
Martin Hjelmare
a745594712 Move legacy device tracker setup to legacy module (#43447) 2020-11-20 21:28:00 +01:00
Aaron Bach
8779592952 Move Ambient PWS logger to a package logger (#43448) 2020-11-20 21:20:56 +01:00
Aaron Bach
a880ef6a4e Move Flo logger to a package logger (#43449) 2020-11-20 21:14:44 +01:00
springstan
dd4f41c1db Use weather attribute conditions constants (#39945) 2020-11-20 21:04:03 +01:00
Philip Allgaier
700336258b Fix empty local_ip config flow (#43333) 2020-11-20 12:40:34 -07:00
Aleix Murtra
0f823b4c0e Upgrade Beewi Smartclim component to 0.0.10 (#43441) 2020-11-20 19:16:18 +01:00
Paulus Schoutsen
f2e78d6ea0 Bumped version to 0.118.2 2020-11-20 16:54:21 +00:00
Paulus Schoutsen
9822cacbba Fix time trigger based on entities ignoring entities if initially in the past (#43431) 2020-11-20 16:54:16 +00:00
Brett
43c53f7043 Increase Advantage Air retry limit for older systems (#43417) 2020-11-20 16:54:16 +00:00
Rob Bierbooms
7708da6713 Update dsmr_parser to 0.23 (#43403) 2020-11-20 16:47:47 +01:00
Paulus Schoutsen
dc5f7aedd7 Verify that we register blueprints on automation setup (#43434) 2020-11-20 15:57:57 +01:00
Paulus Schoutsen
bbb82ded68 Fix time trigger based on entities ignoring entities if initially in the past (#43431) 2020-11-20 15:43:28 +01:00
Philip Allgaier
82b7cc8ac7 Provide HA username via auth WS (#43283) 2020-11-20 15:42:19 +01:00
Paulus Schoutsen
43ba053030 Add support for checking minimum HA version (#43350) 2020-11-20 15:24:42 +01:00
Joakim Plate
e98f36e357 Support openRelativePercent for google assistant covers (#43336) 2020-11-20 15:20:09 +01:00
100ferhas
9b3c97345a Update Alexa supported languages (#43139) 2020-11-20 08:47:17 -05:00
Brig Lamoreaux
c42b1f65b3 Add Srp energy component (#41091) 2020-11-20 08:18:02 -05:00
Sébastien RAMAGE
2ed27fc15d Update zigpy-zigate to 0.7.3 (#43427)
* Update zigpy-zigate to 0.7.3
Fix probing pizigate

* Update zigpy-zigate to 0.7.3
Fix probing pizigate
2020-11-20 08:05:51 -05:00
Brett
6dc84654b0 Increase Advantage Air retry limit for older systems (#43417) 2020-11-20 09:23:38 +01:00
Andrew Hayworth
88367dc466 Update python-awair to 0.2.1 (#43415)
This version of python awair has support for the local api - which is
cool! But more importantly, this version also is less strict about the
versions of aiohttp it depends on.

This version prepares for the upcoming pip resolver changes outlined here:
https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html#what-will-change

cc https://github.com/ahayworth/python_awair/issues/18
2020-11-20 09:07:59 +01:00
ehendrix23
3a42277130 Update pymyq to 2.0.10 (#43413)
* Bump version of pymyq to 2.0.9

* Bump version of pymyq to 2.0.10
2020-11-19 16:26:44 -10:00
HomeAssistant Azure
c255e74463 [ci skip] Translation update 2020-11-20 00:06:26 +00:00
Paulus Schoutsen
390668e192 Check config to use config platforms (#43407) 2020-11-19 22:05:36 +01:00
Anton Palgunov
d3f952f831 Add new air-humidifier device CA4 with miot protocol (#39398)
* Add new air-humidifier device CA4 with miot protocol

* Update homeassistant/components/xiaomi_miio/fan.py

Co-authored-by: Teemu R. <tpr@iki.fi>

* xiomi_miio fan Standard speed for fan, removed static attributes

* xiomi_miio fan Standard speed for fan, removed static attributes

* xiomi_miio fan unnessary elif

* added mode_mapping

* Changed mode_mapping to constant also move reverse to constant

* Removerd water_level

* Return to exclude breaking changes

Co-authored-by: Teemu R. <tpr@iki.fi>
2020-11-19 21:56:53 +01:00
Paulus Schoutsen
e9d895d283 Merge pull request #43400 from home-assistant/rc 2020-11-19 20:58:23 +01:00
David
f693c8a9fd Add twinkly integration (#42103)
* Add twinkly integration

* Add tests for the Twinkly integration

* Update Twinkly client package to fix typo

* Remove support of configuration.yaml from Twinkly integration

* Add ability to unload Twinkly component from the UI

* Remove dead code from Twinkly

* Fix invalid error namespace in Twinkly for python 3.7

* Fix tests failing on CI

* Workaround code analysis issue

* Move twinkly client init out of entry setup so it can be re-used between entries

* Test the twinkly component initialization

* React to PR review and add few more tests
2020-11-19 12:22:12 -05:00
Paulus Schoutsen
2741362b23 Bumped version to 0.118.1 2020-11-19 17:13:54 +00:00
Joakim Sørensen
044edd3d9e Add back system_health_info to the base of lovelace (#43382) 2020-11-19 17:13:45 +00:00
Raman Gupta
0bfcfb859a Bump pyvizio to 0.1.57 (#43374) 2020-11-19 17:13:44 +00:00
J. Nick Koston
e15f3b7b8e Fix homekit bridges when no name was provided (#43364) 2020-11-19 17:13:43 +00:00
Erik Montnemery
18fdf3a429 Bump hatasmota to 0.0.32 (#43360) 2020-11-19 17:13:42 +00:00
Adam Król
59d6eaf856 Bump wolf_smartset to 0.1.8 and handle server fetch error (#43351) 2020-11-19 17:13:42 +00:00
Bram Kragten
5dcbb634f6 Fix selector to return the selector type (#43395) 2020-11-19 16:48:43 +01:00
Adam Król
edd25ae338 Bump wolf_smartset to 0.1.8 and handle server fetch error (#43351) 2020-11-19 15:24:28 +01:00
Aidan Timson
17e1a2a78a Update ovoenergy to v1.1.11 (#43391) 2020-11-19 15:23:20 +01:00
Allen Porter
2d14f07396 Improve nest SDM integration error handling (#43271) 2020-11-19 12:26:49 +01:00
J. Nick Koston
a3061ebd8d Fix homekit bridges when no name was provided (#43364) 2020-11-19 12:10:23 +01:00
Erik Montnemery
d61998e184 Raise in base implementation of FanEntity.oscillate (#43354) 2020-11-19 12:05:52 +01:00
Anders Melchiorsen
390b45b149 Reword numeric_state trigger (#43367) 2020-11-19 12:00:01 +01:00
Joakim Sørensen
3ca8f0c475 Add back system_health_info to the base of lovelace (#43382) 2020-11-19 11:55:21 +01:00
Shay Levy
982624b3ac Support for Shelly Binary Input Sensors (#43313)
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-19 11:42:24 +01:00
Simone Chemelli
3dbfd2cb70 Add shelly installed firmware info (#43221) 2020-11-19 10:47:23 +01:00
Erik Montnemery
0bf9734af1 Bump hatasmota to 0.0.32 (#43360) 2020-11-19 09:07:35 +01:00
Raman Gupta
4205367aa7 Bump pyvizio to 0.1.57 (#43374) 2020-11-19 08:46:20 +01:00
Oliver
f555e20fac Update denonavr to 0.9.6 (#43370) 2020-11-18 19:46:30 -10:00
Alexei Chetroi
54c4e9335f Refactor ZHA sensor initialization (#43339)
* Refactor ZHA sensors to use cached values after restart

* Get attr from cluster, not channel

* Run cached state through formatter method

* Use cached values for div/multiplier for SmartEnergy channel

* Restore batter voltage from cache

* Refactor sensor to use cached values only

* Update tests

* Add battery sensor test
2020-11-18 21:34:12 -05:00
Alexei Chetroi
70a3489845 Update Zha dependencies (#43373) 2020-11-18 20:31:22 -05:00
Alexei Chetroi
f1693e2433 Refactor ZHA tests to allow attribute reads during device initialization (#43357)
* Allow plugging zigpy attribute reads in tests

* Migrate ZHA tests to use new patched attribute reads

* Remove logging in tests
2020-11-18 19:12:38 -05:00
HomeAssistant Azure
a1306059af [ci skip] Translation update 2020-11-19 00:11:12 +00:00
Franck Nijhof
aec17d25fa Merge pull request #43356 from home-assistant/rc 2020-11-18 19:01:29 +01:00
Franck Nijhof
462793f43c Bumped version to 0.118.0 2020-11-18 17:45:38 +01:00
Bram Kragten
ddd7fff6cd Updated frontend to 20201111.2 (#43334) 2020-11-18 17:43:04 +01:00
Erik Montnemery
d2a3811186 Bump hatasmota to 0.0.31 (#43319) 2020-11-18 17:22:26 +01:00
Raman Gupta
27707d9711 Abort vizio discovery flow without unique ID (#43303)
* abort vizio discovery flow if unique ID cant be found because it means we cant connect

* add tests

* fix abort call
2020-11-18 17:22:17 +01:00
Aaron Bach
0e155f5240 Bump aioguardian to 1.0.4 (#43299) 2020-11-18 17:22:07 +01:00
rikroe
9920aaf1f7 Bump bimmer_connected to 0.7.13 (#43294)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2020-11-18 17:21:58 +01:00
Chris
b25e8be3f2 Tweak OZW Light discovery (#43325) 2020-11-18 15:18:59 +01:00
Bram Kragten
de5e49b37c Updated frontend to 20201111.2 (#43334) 2020-11-18 08:24:25 +01:00
HomeAssistant Azure
262fadd9bc [ci skip] Translation update 2020-11-18 00:09:55 +00:00
Alex-Klein
682417d2c7 Add TV channel name to smartthings integration (#41729)
* Adds TV channel name to smartthings integration

* fix on attribute to not override key
2020-11-17 17:20:59 -06:00
Joakim Plate
1567fadda3 Improve Google Assistant cover assumed state handling (#43255)
* Set command only cover
* No need for override position now that we support command only
* Return empty state response for assumed state

Fixes #43178
Reverts: #23498

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-17 21:55:46 +01:00
mvn23
212fb572e1 Fix kodi media_player unavailable at start (#41714) 2020-11-17 07:27:58 -10:00
Maciej Wilczyński
6a5546afc1 Add an option to template delay_on/off in template binary sensor (#43259) 2020-11-17 07:24:04 -10:00
Erik Montnemery
14aba1f9de Bump hatasmota to 0.0.31 (#43319) 2020-11-17 18:02:41 +01:00
Klaas Schoute
b358103b58 Update cloud integration to 0.38.0 (#43314)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-17 16:31:59 +01:00
Martin Hjelmare
e73d2c65cd Add progress translation key to hassfest (#43311) 2020-11-17 11:44:06 +01:00
dependabot[bot]
aa6376a691 Bump codecov/codecov-action from v1.0.14 to v1.0.15 (#43304)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from v1.0.14 to v1.0.15.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Commits](https://github.com/codecov/codecov-action/compare/v1.0.14...239febf655bba88b16ff5dea1d3135ea8663a1f9)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-11-17 08:55:56 +01:00
Thomas
4e6035d057 Improvement to allow parsing of station ID in vasttrafik integration. Addresses #34851 (#43136) 2020-11-17 07:34:20 +01:00
Raman Gupta
95504b7408 Abort vizio discovery flow without unique ID (#43303)
* abort vizio discovery flow if unique ID cant be found because it means we cant connect

* add tests

* fix abort call
2020-11-17 07:11:42 +01:00
Chris Talkington
c861a1c850 Update directv to 0.4.0 (#43302)
* update directv to 0.4.0

* Update requirements_all.txt

* Update requirements_test_all.txt
2020-11-17 06:50:04 +01:00
Tom
ed36cb7beb Add notification binary_sensor to Plugwise integration (#41473)
* Notifications extract from beta

* Remove info loggings

* Delete notification service

* Only notifications for right smiles

* Revert to correct logic

* Catchup with dev (mostly with ourselves from #41201)

* Remove debug logging

* Naming improvement

* Improve test quality as per codecov patch requirement

* Revert to original condition (and appropriately test)

* Fix delete_notification_service, bring tests fixtures up to 1.6.0 including notifications

* Review comment (@bouwew)

* Correct test value

* Re-apply #40108 fix after rebase sidestep

* Update tests/components/plugwise/test_init.py

Co-authored-by: Chris Talkington <chris@talkingtontech.com>

* Add needed state to imports

* Remove separate gw unload code

* Change entry_fail approach

* Revert persistent notification part

* Revert persistent notification part - lint

* Update homeassistant/components/plugwise/binary_sensor.py

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

* Update homeassistant/components/plugwise/binary_sensor.py

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

* Rework reuse of sensor in binary_sensor

* Explicit state attribute keys

* Remove tempfile

* List of notifications per severity

* Update homeassistant/components/plugwise/binary_sensor.py

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

Co-authored-by: Chris Talkington <chris@talkingtontech.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2020-11-16 19:54:44 -06:00
HomeAssistant Azure
c8113e6b11 [ci skip] Translation update 2020-11-17 00:06:49 +00:00
rikroe
096d73eb4f Bump bimmer_connected to 0.7.13 (#43294)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2020-11-17 00:31:54 +01:00
Aaron Bach
699402ca18 Bump aioguardian to 1.0.4 (#43299) 2020-11-16 16:31:35 -07:00
Jc2k
3e1f2a5103 Refactor how entities are created for homekit_controller services (#43242) 2020-11-16 23:11:39 +00:00
Bram Kragten
d11d1343a8 Updated frontend to 20201111.1 (#43298) 2020-11-16 22:56:45 +01:00
Paulus Schoutsen
c04b600ddd Update pytradfri to 7.0.4 (#43297) 2020-11-16 22:55:53 +01:00
uvjustin
414f167508 Remove pts adjustments in stream (#42399)
* Remove unnecessary pts adjustments

* Add comments

* Use -inf for initial last_dts to be more clear

* Use video first_pts as common adjuster in recorder

* Remove seek(0) before av.open
2020-11-16 15:13:33 -05:00
reaper7
159ebe1dac Fix Enigma2 available entity property (#43292)
Available property should return self.e2_box.is_offlin negation

fixes previous PR: https://github.com/home-assistant/core/pull/42407
2020-11-16 20:25:07 +01:00
Erik Montnemery
52e1282d8c Make MQTT climate return PRESET_NONE when no preset is set (#43257) 2020-11-16 20:10:55 +01:00
michaeldavie
4ffba281db Bump env_canada to 0.2.4, fix config validation (#43251) 2020-11-16 20:09:18 +01:00
Sylvia van Os
a164989e41 Bump PyEssent to 0.14 (#43282) 2020-11-16 18:53:42 +01:00
Paulus Schoutsen
f19f743bed Update pytradfri to 7.0.3 (#43289) 2020-11-16 18:26:22 +01:00
Paulus Schoutsen
479de9433c Convert core tests to async (#43287) 2020-11-16 18:25:55 +01:00
Paulus Schoutsen
819dd27925 Automatically clean up executor as part of closing loop (#43284) 2020-11-16 15:43:48 +01:00
Greg Dowling
5d83f0a911 Bump Pywemo to 0.5.3 (#43263) 2020-11-16 13:17:58 +01:00
Greg Dowling
31f3ce81ee Bump pyvera to 0.3.11 (#43262) 2020-11-16 13:16:49 +01:00
dependabot[bot]
63abe8b4f9 Bump actions/upload-artifact from v2.2.0 to v2.2.1 (#43272)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from v2.2.0 to v2.2.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.2.0...726a6dcd0199f578459862705eed35cda05af50b)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-11-16 12:39:14 +01:00
Guy Khmelnitsky
5ea55f7e89 Xiaomi Device Tracker - Move "Refreshing device list" to debug (#43276) 2020-11-16 12:31:45 +01:00
Philip Allgaier
7280dbd431 Bump requests to 2.25.0 (#43279) 2020-11-16 12:27:42 +01:00
Sebastian Muszynski
1783e1ae64 Bump python-miio and construct version (#43267) 2020-11-16 12:22:32 +01:00
Adrian Suwała
246ad8dba9 Rewrite ecobee unittest tests to pytest (#42584) 2020-11-16 11:54:51 +01:00
Simone Chemelli
4c2bf1ddf5 Avoid creating battery sensor if Shelly device is external powered (#43243) 2020-11-16 11:49:23 +01:00
Michael
60314ecc61 Apply suggestions from #42697 to synology_dsm (#43197)
* apply suggestions from #42697

* fix tests

* use MockConfigEntry for test

* use hass.config_entries.async_setup()

* disable default fixture

* rename marker to no_bypass_setup
2020-11-16 11:18:48 +01:00
Chris Talkington
467d79c7fd Add tests for browse media image proxy (#43076)
* add tests for browse media image proxy

* Update test_init.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py
2020-11-16 07:49:41 +01:00
HomeAssistant Azure
7dcfc8f1fa [ci skip] Translation update 2020-11-16 00:03:40 +00:00
J. Nick Koston
9d0cd9c1b1 Convert most esphome state updates to callbacks (#43246) 2020-11-15 22:18:23 +01:00
Tim de Boer
149ba088d4 Add dsmr_reader telegram timestamp and device classes (#42909)
* Added Telegram timestamp

* added to 'dsmr/reading/timestamp' and 'dsmr/consumption/gas/read_at'

* Fixed import sorting

* Replaced 'kW' with ENERGY_KILO_WATT_HOUR

* Added device_class, changed unit_of_measurement with fallback on device_class

* Fixed typo

* Fixed 'black' coding format

* Removed fallback, added unit_of_measurement and CURRENCY_EURO as device_class

* Fixed newline

* Removed 'timestamp' unit_of_meassure

* Removed icons from defintions with device_class

* Updated device_classes

* Updated device_classes

* Updated device_classes

* Added 'entity_registry_enabled_default' properties

* Added 'entity_registry_enabled_default' properties, fixed typo

* MQTT discovery will be in another pull-request
2020-11-15 14:52:31 -06:00
Pieter De Baets
61475cf090 Support gas meter capability for smartthings (#41310) 2020-11-15 14:42:11 -06:00
Sébastien RAMAGE
65b2ef659e Update zigpy-zigate to 0.7.2 (#43252)
* update zigpy_zigate to v0.7.1

* bump zigpy-zigate to 0.7.2
2020-11-15 12:40:45 -05:00
J. Nick Koston
869cb83170 Set should_poll for zone entities (#43212) 2020-11-15 11:40:03 -06:00
Clifford Roche
eb9e9e67f0 Update greeclimate to 0.10.3 (#43248)
* Update greeclimate to 0.10.3

* Device search needs to be mocked in tests
2020-11-15 11:06:51 -06:00
Florian Klien
a6f47ac380 Remove yessssms integration (#43200) 2020-11-15 16:40:59 +01:00
HomeAssistant Azure
9a21421807 [ci skip] Translation update 2020-11-15 00:03:48 +00:00
Alexei Chetroi
84569549f8 Refactor ZHA light initialization (#43149)
* Refactor ZHA light initialization

* Don't do redundant attribute reads
2020-11-14 17:24:41 -05:00
Andrew Sayre
cd42d82f9d Bump pysmartthings and pysmartapp (#43240) 2020-11-14 21:57:42 +01:00
J. Nick Koston
4e00a8a3d0 Eliminate doorbird switch polling (#43215) 2020-11-14 21:47:38 +01:00
J. Nick Koston
8b63e22c99 Bypass the slow update warning for group updates (#43209) 2020-11-14 21:46:24 +01:00
Andrew Sayre
11a437bac9 Bump pyheos to 0.7.2 (#43239) 2020-11-14 21:46:01 +01:00
J. Nick Koston
810561e313 Switch ios to dispatching instead of polling (#43233) 2020-11-14 21:43:49 +01:00
Josef Schlehofer
085aa3c99d Upgrade youtube_dl to version 2020.11.12 (#43231) 2020-11-14 21:41:04 +01:00
michaeldavie
a3e8968e8a Remove OpenCV dependecy from Environment Canada (#43235)
* Bump env_canada to 0.2.2

* Revert PR #38731
2020-11-14 21:16:14 +01:00
Austin Drummond
d1566bd210 Add HomeKit humidifier/dehumidifier (#42311)
* add HomeKit humidifier/dehumidifier

* added more test coverage

* simplified char logic

Co-authored-by: Quentame <polletquentin74@me.com>

* use mode constants

* Renamed HomeKit Contorller

Co-authored-by: Quentame <polletquentin74@me.com>

* improved threshold logic

* split up homekit humidifier into 2 entities

* fixed tests

* fixed mode and switch logic

* added set mode tests

* removed redundant methods present in base class

Co-authored-by: Quentame <polletquentin74@me.com>
2020-11-14 17:59:13 +00:00
Glenn Waters
c6608f7f49 Bump elkm1-lib to 0.8.8 (#43230) 2020-11-14 07:13:37 -10:00
b4dpxl
bb31de1de7 Add support for Broadlink BG1 devices (#42314)
* Support for BG1 switches after config flow updates to core Broadlink component

* updates based on @felipediel feedback

* Update updater.py

* Update switch.py

* Update switch.py
2020-11-14 14:59:41 +01:00
Jc2k
cc396b9736 Add initial camera support to homekit_controller (#43100) 2020-11-14 12:07:22 +00:00
Clifford Roche
dc8db033b9 Update greeclimate to 0.10.2 (#43206) 2020-11-14 11:57:36 +01:00
Thomas Delaet
293f8eaaf3 Add quarter-hour period feature for utility_meter component (#41999)
* add support for quarter-hourly intervals to utility meter (15 minutes)

* add tests

* add test for every quarter

* Update homeassistant/components/utility_meter/sensor.py

Co-authored-by: springstan <46536646+springstan@users.noreply.github.com>

Co-authored-by: springstan <46536646+springstan@users.noreply.github.com>
2020-11-14 11:53:59 +01:00
J. Nick Koston
bf955f3eb8 Fix typo in lw12wifi shoud_poll (#43213) 2020-11-14 11:12:00 +01:00
Allen Porter
3e46c42307 Catch the right nest stream refresh exception error (#43189) 2020-11-13 22:37:09 -08:00
HomeAssistant Azure
d7f1deeb00 [ci skip] Translation update 2020-11-14 00:07:02 +00:00
Aaron Bach
907907e6f9 Revert "Remove YAML config for Tile (#43064)" (#43199)
This reverts commit 19f48e180c.
2020-11-14 00:04:34 +01:00
czechmark
16364636d1 Update fitbit battery status (#42980)
* Read the device_status in order to update the fitbit battery

* Read the device_status in order to update the fitbit battery
2020-11-13 23:10:04 +01:00
Paulus Schoutsen
bae026a6fe Add support for selectors in services.yaml (#43162)
* Add support for selectors in services.yaml

* Add base schema validation
2020-11-13 22:53:55 +01:00
Paulus Schoutsen
df25b53bb8 Fix automation in packages (#43202) 2020-11-13 22:49:01 +01:00
Thomas
6e7b14f139 Hide cancelled trips from being displayed in vasttrafik integration (#43184) 2020-11-13 17:51:33 +01:00
Charles Garwood
71b8aad91b Update ozw get_config_parameter websocket response (#43058) 2020-11-13 10:06:34 -05:00
Martin Hjelmare
6daf40b254 Copy default vscode settings during bootstrap (#43180) 2020-11-13 16:03:40 +01:00
Paulus Schoutsen
37361a6332 Prevent spider from doing I/O in the event loop (#43182) 2020-11-13 16:03:08 +01:00
Paulus Schoutsen
36b4a8e128 Revert opensky flight latitude and longitude (#43185)
This reverts commit 0f46916f9e.
2020-11-13 09:52:15 -05:00
Daniel
5de30e19bb Rewrite google_wifi unittest tests to pytest style (#42030) 2020-11-13 15:40:46 +01:00
Franck Nijhof
976640102f Upgrade sentry-sdk to 0.19.3 (#43176) 2020-11-13 14:42:40 +01:00
Paulus Schoutsen
5ecaa9e4d2 Add log message server startup (#43177) 2020-11-13 14:39:50 +01:00
Sébastien RAMAGE
ea550d475a update zigpy_zigate to v0.7.1 (#43159) 2020-11-13 08:35:41 -05:00
Paulus Schoutsen
7a85b3ea66 Add test to access current request in websocket API (#43133) 2020-11-13 14:20:47 +01:00
Anders Melchiorsen
55b214ec9c Fix time_date interval for DST (#43166) 2020-11-13 13:32:56 +01:00
Paulus Schoutsen
0a717f313e Improve entity domain validator (#43164) 2020-11-13 13:31:43 +01:00
Anders Melchiorsen
87d86026ed Fix time_date timestamp offsets (#43165) 2020-11-13 13:29:57 +01:00
Paulus Schoutsen
8dbd54bed1 Disable parsing scientific/complex number notation in template type (#43170) 2020-11-13 13:22:29 +01:00
Simone Chemelli
7bcd92172a Remove relative time sensor from cert_expiry (#42338) 2020-11-13 13:15:37 +01:00
Paulus Schoutsen
890d740093 Further improve MFI tests (#43167) 2020-11-13 10:51:27 +01:00
Paulus Schoutsen
3404e6ded1 Update translations 2020-11-13 10:40:51 +01:00
J. Nick Koston
4907996dd6 Guard against empty ssdp locations (#43156) 2020-11-13 09:40:25 +01:00
Anders Melchiorsen
96641cab02 Fix playing of Spotify URIs on Sonos (#43154) 2020-11-13 09:40:00 +01:00
Joakim Sørensen
f50869d62e Add missing 'hassio' translation string (#43127)
* Add missing 'hassio' translation string

* Fix typo...
2020-11-13 09:39:36 +01:00
Aaron Bach
ad7f1446be Streamline SimpliSafe errors (#43117) 2020-11-13 09:38:58 +01:00
Marvin Wichmann
899fc3d35a Update xknx to 0.15.3 (#42026)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-13 09:37:45 +01:00
Anders Melchiorsen
1a888433cb Fix beat calculation (#43142) 2020-11-13 09:33:00 +01:00
Anders Melchiorsen
e0f1d0ab20 Mock time_date sensor tests (#43141) 2020-11-13 09:31:55 +01:00
HomeAssistant Azure
76b843118b [ci skip] Translation update 2020-11-13 00:10:16 +00:00
Aaron Bach
3f09c9bc0e Fix bug preventing Notion entities from updating their bridge (#43122) 2020-11-12 21:45:20 +01:00
Martin Hjelmare
b84415344f Bump hass-nabucasa to 0.37.2 (#43146) 2020-11-12 20:15:31 +01:00
Simone Chemelli
31502e960a Shelly: minor improvements (#43138) 2020-11-12 19:49:06 +01:00
Felipe Martins Diel
c620fa344e Add support for learning RF commands with Broadlink remotes (#39671)
* Add support for learning RF codes with Broadlink remotes

* Rename INFRARED and RADIOFREQUENCY to COMMAND_TYPE_IR and COMMAND_TYPE_RF

* Rewrite if expression as normal if statement

* Use COMMAND_TYPE_IR directly and improve error messages
2020-11-12 19:10:29 +01:00
Aaron Bach
7921be1b5f Fix incorrect Notion battery state calculation (#43108)
* Fix incorrect Notion battery state calculation

* Both cases
2020-11-12 10:47:47 -07:00
Erik Montnemery
7b7bb0a15f Bump hatasmota to 0.0.30 (#43140) 2020-11-12 18:29:06 +01:00
Paulus Schoutsen
a68d6a63bb Add VSCode debug launch conf (#43130) 2020-11-12 15:46:49 +01:00
Shay Levy
ad06b6b340 Revert "shelly_naming" rebase errors (#43134) 2020-11-12 15:22:51 +01:00
Philip Allgaier
bbd7402793 Add reload support to KNX (#42429)
* Add reload support to KNX

* Changes from review (platform reset + asyncio.gather)

* Changes from review (proper asyncio.gather usage)
2020-11-12 14:34:08 +01:00
Martin Hjelmare
a665e152a9 Fix aurora config flow tests (#43128) 2020-11-12 12:11:05 +01:00
Franck Nijhof
6f326a7ea4 Add support for multiple event triggers in automation (#43097)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-12 11:58:28 +01:00
Philip Allgaier
673ac21de4 Added missing system health translation for "Views" (#43126) 2020-11-12 11:50:24 +01:00
Aaron Bach
24840cce23 Add a config flow for Recollect Waste (#43063)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-12 11:00:42 +01:00
Simone Chemelli
cdc53329d0 Add Shelly totalWorkTime as Lamp life (Shelly Air) (#43112)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-12 10:38:53 +01:00
SukramJ
d7e5d1bfaf Bump dependency for HomematicIP Cloud (#43018)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2020-11-12 10:33:01 +01:00
Shay Levy
22a0464dce Make Appliance Type Case-insensitive (#43114)
"appliance_type" is a free text parameter in the device settings, this fix will make the comparison case-insensitive
2020-11-12 10:12:56 +01:00
Joshua M. Boniface
be93060e99 Move setup_url_for_address to pyWeMo library (#42722)
* Move setup_url_for_address to PyWemo library

* Bump pywemo to 0.5.2

* Use module-level function call

* Update requirements via script
2020-11-12 06:37:27 +01:00
HomeAssistant Azure
8a0907acf9 [ci skip] Translation update 2020-11-12 00:10:58 +00:00
Bram Kragten
1c7080d5c5 Add save and delete WS commands to blueprints (#42907)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-11 23:32:46 +01:00
djtimca
d47b3a5f44 Fix Aurora integration including externalizing API to PyPi and adding config_flow (#43045)
Co-authored-by: Pawel <pszafer@gmail.com>
2020-11-11 20:36:16 +01:00
Chris Talkington
df5a8c4dac Use media player image proxy for roku media browser (#43070) 2020-11-11 20:17:46 +01:00
Simone Chemelli
d8b067ebf9 Add Shelly support for REST sensors (#40429) 2020-11-11 20:13:14 +01:00
Joakim Sørensen
403514ccb3 Add system health section for the Supervisor (#43074) 2020-11-11 20:12:24 +01:00
springstan
37bcfd1d2f Simplify distance conversion (#43107) 2020-11-11 20:10:17 +01:00
Moritz Schmitz von Hülst
434d39a5ea Add initial rest query params (#42198)
* add initial rest query params

* of course I didn't run black

* fix tests

* fix tests

* add test

* reformat

* add binary sensor test

* fix tests

* add one more test and fix switch

* should not have touched that

* if you don't pay attention once
2020-11-11 09:03:55 -10:00
Hmmbob
6a7b71e8fb Bump pycsspeechtts to 1.0.4 (#43105) 2020-11-11 19:47:03 +01:00
1324 changed files with 32077 additions and 7507 deletions

View File

@@ -48,7 +48,9 @@ omit =
homeassistant/components/anel_pwrctrl/switch.py
homeassistant/components/anthemav/media_player.py
homeassistant/components/apcupsd/*
homeassistant/components/apple_tv/*
homeassistant/components/apple_tv/__init__.py
homeassistant/components/apple_tv/media_player.py
homeassistant/components/apple_tv/remote.py
homeassistant/components/aqualogic/*
homeassistant/components/aquostv/media_player.py
homeassistant/components/arcam_fmj/media_player.py
@@ -65,6 +67,9 @@ omit =
homeassistant/components/asterisk_mbox/*
homeassistant/components/aten_pe/*
homeassistant/components/atome/*
homeassistant/components/aurora/__init__.py
homeassistant/components/aurora/binary_sensor.py
homeassistant/components/aurora/const.py
homeassistant/components/aurora_abb_powerone/sensor.py
homeassistant/components/avea/light.py
homeassistant/components/avion/light.py
@@ -259,6 +264,11 @@ omit =
homeassistant/components/fibaro/*
homeassistant/components/filesize/sensor.py
homeassistant/components/fints/sensor.py
homeassistant/components/fireservicerota/__init__.py
homeassistant/components/fireservicerota/binary_sensor.py
homeassistant/components/fireservicerota/const.py
homeassistant/components/fireservicerota/sensor.py
homeassistant/components/fireservicerota/switch.py
homeassistant/components/firmata/__init__.py
homeassistant/components/firmata/binary_sensor.py
homeassistant/components/firmata/board.py
@@ -533,12 +543,16 @@ omit =
homeassistant/components/minio/*
homeassistant/components/mitemp_bt/sensor.py
homeassistant/components/mjpeg/camera.py
homeassistant/components/mobile_app/*
homeassistant/components/mochad/*
homeassistant/components/modbus/climate.py
homeassistant/components/modbus/cover.py
homeassistant/components/modbus/switch.py
homeassistant/components/modbus/sensor.py
homeassistant/components/modem_callerid/sensor.py
homeassistant/components/motion_blinds/__init__.py
homeassistant/components/motion_blinds/const.py
homeassistant/components/motion_blinds/cover.py
homeassistant/components/motion_blinds/sensor.py
homeassistant/components/mpchc/media_player.py
homeassistant/components/mpd/media_player.py
homeassistant/components/mqtt_room/sensor.py
@@ -560,7 +574,18 @@ omit =
homeassistant/components/neato/vacuum.py
homeassistant/components/nederlandse_spoorwegen/sensor.py
homeassistant/components/nello/lock.py
homeassistant/components/nest/*
homeassistant/components/nest/__init__.py
homeassistant/components/nest/api.py
homeassistant/components/nest/binary_sensor.py
homeassistant/components/nest/camera.py
homeassistant/components/nest/camera_legacy.py
homeassistant/components/nest/camera_sdm.py
homeassistant/components/nest/climate.py
homeassistant/components/nest/climate_legacy.py
homeassistant/components/nest/climate_sdm.py
homeassistant/components/nest/local_auth.py
homeassistant/components/nest/sensor.py
homeassistant/components/nest/sensor_legacy.py
homeassistant/components/netatmo/__init__.py
homeassistant/components/netatmo/api.py
homeassistant/components/netatmo/camera.py
@@ -666,6 +691,7 @@ 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
@@ -708,6 +734,7 @@ omit =
homeassistant/components/rainforest_eagle/sensor.py
homeassistant/components/raspihats/*
homeassistant/components/raspyrfm/*
homeassistant/components/recollect_waste/__init__.py
homeassistant/components/recollect_waste/sensor.py
homeassistant/components/recswitch/switch.py
homeassistant/components/reddit/*
@@ -745,7 +772,6 @@ omit =
homeassistant/components/russound_rnet/media_player.py
homeassistant/components/sabnzbd/*
homeassistant/components/saj/sensor.py
homeassistant/components/salt/device_tracker.py
homeassistant/components/satel_integra/*
homeassistant/components/schluter/*
homeassistant/components/scrape/sensor.py
@@ -821,6 +847,7 @@ omit =
homeassistant/components/spotcrime/sensor.py
homeassistant/components/spotify/__init__.py
homeassistant/components/spotify/media_player.py
homeassistant/components/spotify/system_health.py
homeassistant/components/squeezebox/__init__.py
homeassistant/components/squeezebox/browse_media.py
homeassistant/components/squeezebox/media_player.py
@@ -935,7 +962,6 @@ omit =
homeassistant/components/twilio_call/notify.py
homeassistant/components/twilio_sms/notify.py
homeassistant/components/twitter/notify.py
homeassistant/components/ubee/device_tracker.py
homeassistant/components/ubus/device_tracker.py
homeassistant/components/ue_smart_radio/media_player.py
homeassistant/components/unifiled/*
@@ -1054,7 +1080,6 @@ omit =
homeassistant/components/zha/core/device.py
homeassistant/components/zha/core/gateway.py
homeassistant/components/zha/core/helpers.py
homeassistant/components/zha/core/patches.py
homeassistant/components/zha/core/registries.py
homeassistant/components/zha/core/typing.py
homeassistant/components/zha/entity.py

View File

@@ -12,17 +12,21 @@
"redhat.vscode-yaml",
"esbenp.prettier-vscode"
],
// Please keep this file in sync with settings in home-assistant/.vscode/settings.default.json
"settings": {
"python.pythonPath": "/usr/local/bin/python",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"python.testing.pytestArgs": ["--no-cov"],
"python.testing.pytestEnabled": true,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/bin/bash",
"yaml.customTags": [
"!input scalar",
"!secret scalar",
"!include_dir_named scalar",
"!include_dir_list scalar",

View File

@@ -48,7 +48,7 @@ jobs:
run: |
python -m venv venv
. venv/bin/activate
pip install -U pip setuptools
pip install -U "pip<20.3" setuptools
pip install -r requirements.txt -r requirements_test.txt
- name: Restore pre-commit environment from cache
id: cache-precommit
@@ -611,7 +611,7 @@ jobs:
run: |
python -m venv venv
. venv/bin/activate
pip install -U pip setuptools wheel
pip install -U "pip<20.3" setuptools wheel
pip install -r requirements_all.txt
pip install -r requirements_test.txt
pip install -e .
@@ -741,7 +741,7 @@ jobs:
-p no:sugar \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.2.0
uses: actions/upload-artifact@v2.2.1
with:
name: coverage-${{ matrix.python-version }}-group${{ matrix.group }}
path: .coverage
@@ -785,4 +785,4 @@ jobs:
coverage report --fail-under=94
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.14
uses: codecov/codecov-action@v1.0.15

View File

@@ -12,7 +12,7 @@ jobs:
# 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.13
uses: actions/stale@v3.0.14
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90
@@ -48,7 +48,7 @@ jobs:
# - 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.13
uses: actions/stale@v3.0.14
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# PRs have a CLA signed label, we can misuse it to apply this policy

View File

@@ -48,6 +48,7 @@ repos:
- id: check-executables-have-shebangs
stages: [manual]
- id: check-json
exclude: (.vscode|.devcontainer)
- id: no-commit-to-branch
args:
- --branch=dev

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Home Assistant",
"type": "python",
"request": "launch",
"module": "homeassistant",
"args": ["--debug", "-c", "config"]
}
]
}

9
.vscode/settings.default.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
// Please keep this file in sync with settings in home-assistant/.devcontainer/devcontainer.json
"python.formatting.provider": "black",
// Added --no-cov to work around TypeError: message must be set
// 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
}

View File

@@ -37,6 +37,7 @@ homeassistant/components/amcrest/* @pnbruckner
homeassistant/components/androidtv/* @JeffLIrion
homeassistant/components/apache_kafka/* @bachya
homeassistant/components/api/* @home-assistant/core
homeassistant/components/apple_tv/* @postlund
homeassistant/components/apprise/* @caronc
homeassistant/components/aprs/* @PhilRW
homeassistant/components/arcam_fmj/* @elupus
@@ -48,6 +49,7 @@ homeassistant/components/atag/* @MatsNL
homeassistant/components/aten_pe/* @mtdcr
homeassistant/components/atome/* @baqs
homeassistant/components/august/* @bdraco
homeassistant/components/aurora/* @djtimca
homeassistant/components/aurora_abb_powerone/* @davet2001
homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automation/* @home-assistant/core
@@ -116,7 +118,6 @@ homeassistant/components/dunehd/* @bieniu
homeassistant/components/dwd_weather_warnings/* @runningman84 @stephan192 @Hummel95
homeassistant/components/dweet/* @fabaff
homeassistant/components/dynalite/* @ziv1234
homeassistant/components/dyson/* @etheralm
homeassistant/components/eafm/* @Jc2k
homeassistant/components/ecobee/* @marthoc
homeassistant/components/ecovacs/* @OverloadUT
@@ -131,6 +132,7 @@ homeassistant/components/emoncms/* @borpin
homeassistant/components/emulated_kasa/* @kbickar
homeassistant/components/enigma2/* @fbradyirl
homeassistant/components/enocean/* @bdurrer
homeassistant/components/enphase_envoy/* @gtdiehl
homeassistant/components/entur_public_transport/* @hfurubotten
homeassistant/components/environment_canada/* @michaeldavie
homeassistant/components/ephember/* @ttroy50
@@ -144,6 +146,7 @@ homeassistant/components/ezviz/* @baqs
homeassistant/components/fastdotcom/* @rohankapoorcom
homeassistant/components/file/* @fabaff
homeassistant/components/filter/* @dgomes
homeassistant/components/fireservicerota/* @cyberjunky
homeassistant/components/firmata/* @DaAwesomeP
homeassistant/components/fixer/* @fabaff
homeassistant/components/flick_electric/* @ZephireNZ
@@ -275,6 +278,7 @@ homeassistant/components/mobile_app/* @robbiet480
homeassistant/components/modbus/* @adamchengtkc @janiversen @vzahradnik
homeassistant/components/monoprice/* @etsinko @OnFreund
homeassistant/components/moon/* @fabaff
homeassistant/components/motion_blinds/* @starkillerOG
homeassistant/components/mpd/* @fabaff
homeassistant/components/mqtt/* @home-assistant/core @emontnemery
homeassistant/components/msteams/* @peroyvind
@@ -305,6 +309,7 @@ homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
homeassistant/components/nuheat/* @bdraco
homeassistant/components/nuki/* @pschmitt @pvizeli
homeassistant/components/numato/* @clssn
homeassistant/components/number/* @home-assistant/core @Shulyaka
homeassistant/components/nut/* @bdraco
homeassistant/components/nws/* @MatthewFlamm
homeassistant/components/nzbget/* @chriscla
@@ -335,7 +340,7 @@ homeassistant/components/pi_hole/* @fabaff @johnluetke @shenxn
homeassistant/components/pilight/* @trekky12
homeassistant/components/plaato/* @JohNan
homeassistant/components/plex/* @jjlawren
homeassistant/components/plugwise/* @CoMPaTech @bouwew
homeassistant/components/plugwise/* @CoMPaTech @bouwew @brefra
homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa
homeassistant/components/point/* @fredrike
homeassistant/components/poolsense/* @haemishkyd
@@ -360,6 +365,7 @@ homeassistant/components/raincloud/* @vanstinator
homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
homeassistant/components/rainmachine/* @bachya
homeassistant/components/random/* @fabaff
homeassistant/components/recollect_waste/* @bachya
homeassistant/components/rejseplanen/* @DarkFox
homeassistant/components/repetier/* @MTrab
homeassistant/components/rfxtrx/* @danielhiversen @elupus @RobBie1221
@@ -374,7 +380,6 @@ homeassistant/components/rpi_power/* @shenxn @swetoast
homeassistant/components/ruckus_unleashed/* @gabe565
homeassistant/components/safe_mode/* @home-assistant/core
homeassistant/components/saj/* @fredericvl
homeassistant/components/salt/* @bjornorri
homeassistant/components/samsungtv/* @escoand
homeassistant/components/scene/* @home-assistant/core
homeassistant/components/schluter/* @prairieapps
@@ -422,6 +427,7 @@ homeassistant/components/splunk/* @Bre77
homeassistant/components/spotify/* @frenck
homeassistant/components/sql/* @dgomes
homeassistant/components/squeezebox/* @rajlaud
homeassistant/components/srp_energy/* @briglx
homeassistant/components/starline/* @anonym-tsk
homeassistant/components/statistics/* @fabaff
homeassistant/components/stiebel_eltron/* @fucm
@@ -469,7 +475,7 @@ homeassistant/components/transmission/* @engrbm87 @JPHutchins
homeassistant/components/tts/* @pvizeli
homeassistant/components/tuya/* @ollo69
homeassistant/components/twentemilieu/* @frenck
homeassistant/components/ubee/* @mzdrale
homeassistant/components/twinkly/* @dr1rrb
homeassistant/components/unifi/* @Kane610
homeassistant/components/unifiled/* @florisvdk
homeassistant/components/upb/* @gwww
@@ -516,7 +522,6 @@ homeassistant/components/yamaha_musiccast/* @jalmeroth
homeassistant/components/yandex_transport/* @rishatik92 @devbis
homeassistant/components/yeelight/* @rytilahti @zewelor @shenxn
homeassistant/components/yeelightsunflower/* @lindsaymarkward
homeassistant/components/yessssms/* @flowolf
homeassistant/components/yi/* @bachya
homeassistant/components/zeroconf/* @bdraco
homeassistant/components/zerproc/* @emlove

View File

@@ -1,11 +1,11 @@
{
"image": "homeassistant/{arch}-homeassistant",
"build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:2020.10.1",
"armhf": "homeassistant/armhf-homeassistant-base:2020.10.1",
"armv7": "homeassistant/armv7-homeassistant-base:2020.10.1",
"amd64": "homeassistant/amd64-homeassistant-base:2020.10.1",
"i386": "homeassistant/i386-homeassistant-base:2020.10.1"
"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"
},
"labels": {
"io.hass.type": "core"

View File

@@ -284,6 +284,7 @@ class AuthManager:
self,
user: models.User,
name: Optional[str] = None,
is_active: Optional[bool] = None,
group_ids: Optional[List[str]] = None,
) -> None:
"""Update a user."""
@@ -294,6 +295,12 @@ class AuthManager:
kwargs["group_ids"] = group_ids
await self._store.async_update_user(user, **kwargs)
if is_active is not None:
if is_active is True:
await self.async_activate_user(user)
else:
await self.async_deactivate_user(user)
async def async_activate_user(self, user: models.User) -> None:
"""Activate a user."""
await self._store.async_activate_user(user)

View File

@@ -15,11 +15,7 @@ import yarl
from homeassistant import config as conf_util, config_entries, core, loader
from homeassistant.components import http
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP,
REQUIRED_NEXT_PYTHON_DATE,
REQUIRED_NEXT_PYTHON_VER,
)
from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import (
@@ -142,11 +138,9 @@ async def async_setup_hass(
_LOGGER.warning("Detected that frontend did not load. Activating safe mode")
# Ask integrations to shut down. It's messy but we can't
# do a clean stop without knowing what is broken
hass.async_track_tasks()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP, {})
with contextlib.suppress(asyncio.TimeoutError):
async with hass.timeout.async_timeout(10):
await hass.async_block_till_done()
await hass.async_stop()
safe_mode = True
old_config = hass.config
@@ -534,7 +528,7 @@ async def _async_set_up_integrations(
_LOGGER.warning("Setup timed out for stage 1 - moving forward")
# Enables after dependencies
async_set_domains_to_be_loaded(hass, stage_1_domains | stage_2_domains)
async_set_domains_to_be_loaded(hass, stage_2_domains)
if stage_2_domains:
_LOGGER.info("Setting up stage 2: %s", stage_2_domains)

View File

@@ -4,12 +4,12 @@ from copy import deepcopy
from functools import partial
from abodepy import Abode
from abodepy.exceptions import AbodeException
from abodepy.exceptions import AbodeAuthenticationException, AbodeException
import abodepy.helpers.timeline as TIMELINE
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_DATE,
@@ -110,18 +110,34 @@ async def async_setup_entry(hass, config_entry):
username = config_entry.data.get(CONF_USERNAME)
password = config_entry.data.get(CONF_PASSWORD)
polling = config_entry.data.get(CONF_POLLING)
cache = hass.config.path(DEFAULT_CACHEDB)
# For previous config entries where unique_id is None
if config_entry.unique_id is None:
hass.config_entries.async_update_entry(
config_entry, unique_id=config_entry.data[CONF_USERNAME]
)
try:
cache = hass.config.path(DEFAULT_CACHEDB)
abode = await hass.async_add_executor_job(
Abode, username, password, True, True, True, cache
)
hass.data[DOMAIN] = AbodeSystem(abode, polling)
except AbodeAuthenticationException as ex:
LOGGER.error("Invalid credentials: %s", ex)
await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data=config_entry.data,
)
return False
except (AbodeException, ConnectTimeout, HTTPError) as ex:
LOGGER.error("Unable to connect to Abode: %s", str(ex))
LOGGER.error("Unable to connect to Abode: %s", ex)
raise ConfigEntryNotReady from ex
hass.data[DOMAIN] = AbodeSystem(abode, polling)
for platform in ABODE_PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)

View File

@@ -1,15 +1,16 @@
"""Config flow for the Abode Security System component."""
from abodepy import Abode
from abodepy.exceptions import AbodeException
from abodepy.exceptions import AbodeAuthenticationException, AbodeException
from abodepy.helpers.errors import MFA_CODE_REQUIRED
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, HTTP_BAD_REQUEST
from homeassistant.core import callback
from .const import DEFAULT_CACHEDB, DOMAIN, LOGGER # pylint: disable=unused-import
CONF_MFA = "mfa_code"
CONF_POLLING = "polling"
@@ -25,53 +26,146 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}
self.mfa_data_schema = {
vol.Required(CONF_MFA): str,
}
self._cache = None
self._mfa_code = None
self._password = None
self._polling = False
self._username = None
async def _async_abode_login(self, step_id):
"""Handle login with Abode."""
self._cache = self.hass.config.path(DEFAULT_CACHEDB)
errors = {}
try:
await self.hass.async_add_executor_job(
Abode, self._username, self._password, True, False, False, self._cache
)
except (AbodeException, ConnectTimeout, HTTPError) as ex:
if ex.errcode == MFA_CODE_REQUIRED[0]:
return await self.async_step_mfa()
LOGGER.error("Unable to connect to Abode: %s", ex)
if ex.errcode == HTTP_BAD_REQUEST:
errors = {"base": "invalid_auth"}
else:
errors = {"base": "cannot_connect"}
if errors:
return self.async_show_form(
step_id=step_id, data_schema=vol.Schema(self.data_schema), errors=errors
)
return await self._async_create_entry()
async def _async_abode_mfa_login(self):
"""Handle multi-factor authentication (MFA) login with Abode."""
try:
# Create instance to access login method for passing MFA code
abode = Abode(
auto_login=False,
get_devices=False,
get_automations=False,
cache_path=self._cache,
)
await self.hass.async_add_executor_job(
abode.login, self._username, self._password, self._mfa_code
)
except AbodeAuthenticationException:
return self.async_show_form(
step_id="mfa",
data_schema=vol.Schema(self.mfa_data_schema),
errors={"base": "invalid_mfa_code"},
)
return await self._async_create_entry()
async def _async_create_entry(self):
"""Create the config entry."""
config_data = {
CONF_USERNAME: self._username,
CONF_PASSWORD: self._password,
CONF_POLLING: self._polling,
}
existing_entry = await self.async_set_unique_id(self._username)
if existing_entry:
self.hass.config_entries.async_update_entry(
existing_entry, data=config_data
)
# Reload the Abode config entry otherwise devices will remain unavailable
self.hass.async_create_task(
self.hass.config_entries.async_reload(existing_entry.entry_id)
)
return self.async_abort(reason="reauth_successful")
return self.async_create_entry(title=self._username, data=config_data)
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if not user_input:
return self._show_form()
username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD]
polling = user_input.get(CONF_POLLING, False)
cache = self.hass.config.path(DEFAULT_CACHEDB)
try:
await self.hass.async_add_executor_job(
Abode, username, password, True, True, True, cache
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=vol.Schema(self.data_schema)
)
except (AbodeException, ConnectTimeout, HTTPError) as ex:
LOGGER.error("Unable to connect to Abode: %s", str(ex))
if ex.errcode == HTTP_BAD_REQUEST:
return self._show_form({"base": "invalid_auth"})
return self._show_form({"base": "cannot_connect"})
self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]
return self.async_create_entry(
title=user_input[CONF_USERNAME],
data={
CONF_USERNAME: username,
CONF_PASSWORD: password,
CONF_POLLING: polling,
},
)
return await self._async_abode_login(step_id="user")
@callback
def _show_form(self, errors=None):
"""Show the form to the user."""
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(self.data_schema),
errors=errors if errors else {},
)
async def async_step_mfa(self, user_input=None):
"""Handle a multi-factor authentication (MFA) flow."""
if user_input is None:
return self.async_show_form(
step_id="mfa", data_schema=vol.Schema(self.mfa_data_schema)
)
self._mfa_code = user_input[CONF_MFA]
return await self._async_abode_mfa_login()
async def async_step_reauth(self, config):
"""Handle reauthorization request from Abode."""
self._username = config[CONF_USERNAME]
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(self, user_input=None):
"""Handle reauthorization flow."""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_USERNAME, default=self._username): str,
vol.Required(CONF_PASSWORD): str,
}
),
)
self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD]
return await self._async_abode_login(step_id="reauth_confirm")
async def async_step_import(self, import_config):
"""Import a config entry from configuration.yaml."""
if self._async_current_entries():
LOGGER.warning("Only one configuration of abode is allowed.")
LOGGER.warning("Already configured. Only a single configuration possible.")
return self.async_abort(reason="single_instance_allowed")
self._polling = import_config.get(CONF_POLLING, False)
return await self.async_step_user(import_config)

View File

@@ -3,7 +3,7 @@
"name": "Abode",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/abode",
"requirements": ["abodepy==1.1.0"],
"requirements": ["abodepy==1.2.0"],
"codeowners": ["@shred86"],
"homekit": {
"models": ["Abode", "Iota"]

View File

@@ -7,14 +7,30 @@
"username": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]"
}
},
"mfa": {
"title": "Enter your MFA code for Abode",
"data": {
"mfa_code": "MFA code (6-digits)"
}
},
"reauth_confirm": {
"title": "Fill in your Abode login information",
"data": {
"username": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]"
}
}
},
"error": {
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_mfa_code": "Invalid MFA code"
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
}
}

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9",
"single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace."
},
"error": {
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
"invalid_mfa_code": "Neplatn\u00fd k\u00f3d MFA"
},
"step": {
"mfa": {
"data": {
"mfa_code": "K\u00f3d MFA (6 \u010d\u00edslic)"
},
"title": "Zadejte k\u00f3d MFA pro Abode"
},
"reauth_confirm": {
"data": {
"password": "Heslo",
"username": "E-mail"
},
"title": "Vypl\u0148te sv\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje do Abode"
},
"user": {
"data": {
"password": "Heslo",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Re-authentication was successful",
"single_instance_allowed": "Already configured. Only a single configuration possible."
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication"
"invalid_auth": "Invalid authentication",
"invalid_mfa_code": "Invalid MFA code"
},
"step": {
"mfa": {
"data": {
"mfa_code": "MFA code (6-digits)"
},
"title": "Enter your MFA code for Abode"
},
"reauth_confirm": {
"data": {
"password": "Password",
"username": "Email"
},
"title": "Fill in your Abode login information"
},
"user": {
"data": {
"password": "Password",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "La reautenticaci\u00f3n fue exitosa",
"single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n."
},
"error": {
"cannot_connect": "No se pudo conectar",
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida"
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida",
"invalid_mfa_code": "C\u00f3digo MFA inv\u00e1lido"
},
"step": {
"mfa": {
"data": {
"mfa_code": "C\u00f3digo MFA (6 d\u00edgitos)"
},
"title": "Introduce tu c\u00f3digo MFA para Abode"
},
"reauth_confirm": {
"data": {
"password": "Contrase\u00f1a",
"username": "Correo electronico"
},
"title": "Rellene su informaci\u00f3n de inicio de sesi\u00f3n de Abode"
},
"user": {
"data": {
"password": "Contrase\u00f1a",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Taastuvastamine \u00f5nnestus",
"single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine."
},
"error": {
"cannot_connect": "\u00dchendamine nurjus",
"invalid_auth": "Tuvastamise viga"
"invalid_auth": "Tuvastamise viga",
"invalid_mfa_code": "Kehtetu MFA-kood"
},
"step": {
"mfa": {
"data": {
"mfa_code": "MFA kood (6-kohaline)"
},
"title": "Sisesta oma Abode MFA kood"
},
"reauth_confirm": {
"data": {
"password": "Salas\u00f5na",
"username": "E-post"
},
"title": "Sisesta oma Abode sisselogimisteave"
},
"user": {
"data": {
"password": "Salas\u00f5na",

View File

@@ -3,6 +3,9 @@
"abort": {
"single_instance_allowed": "Csak egyetlen Abode konfigur\u00e1ci\u00f3 enged\u00e9lyezett."
},
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "La riautenticazione ha avuto successo",
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
},
"error": {
"cannot_connect": "Impossibile connettersi",
"invalid_auth": "Autenticazione non valida"
"invalid_auth": "Autenticazione non valida",
"invalid_mfa_code": "Codice MFA non valido"
},
"step": {
"mfa": {
"data": {
"mfa_code": "Codice MFA (6 cifre)"
},
"title": "Inserisci il tuo codice MFA per Abode"
},
"reauth_confirm": {
"data": {
"password": "Password",
"username": "E-mail"
},
"title": "Inserisci le tue informazioni di accesso Abode"
},
"user": {
"data": {
"password": "Password",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Re-authentifikatioun war erfollegr\u00e4ich",
"single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun m\u00e9iglech."
},
"error": {
"cannot_connect": "Feeler beim verbannen",
"invalid_auth": "Ong\u00eblteg Authentifikatioun"
"invalid_auth": "Ong\u00eblteg Authentifikatioun",
"invalid_mfa_code": "Ong\u00ebltege MFA Code"
},
"step": {
"mfa": {
"data": {
"mfa_code": "MFA code (6 Zifferen)"
},
"title": "G\u00ebff dain MFA code fir Abode un"
},
"reauth_confirm": {
"data": {
"password": "Passwuert",
"username": "E-Mail"
},
"title": "F\u00ebll deng Abode Login Informatiounen aus"
},
"user": {
"data": {
"password": "Passwuert",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Reautentisering var vellykket",
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
},
"error": {
"cannot_connect": "Tilkobling mislyktes",
"invalid_auth": "Ugyldig godkjenning"
"invalid_auth": "Ugyldig godkjenning",
"invalid_mfa_code": "Ugyldig MFA-kode"
},
"step": {
"mfa": {
"data": {
"mfa_code": "MFA-kode (6-sifre)"
},
"title": "Skriv inn din MFA-kode for Abode"
},
"reauth_confirm": {
"data": {
"password": "Passord",
"username": "E-post"
},
"title": "Fyll ut innloggingsinformasjonen for Abode"
},
"user": {
"data": {
"password": "Passord",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119",
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
},
"error": {
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
"invalid_auth": "Niepoprawne uwierzytelnienie"
"invalid_auth": "Niepoprawne uwierzytelnienie",
"invalid_mfa_code": "Nieprawid\u0142owy kod uwierzytelniania wielosk\u0142adnikowego"
},
"step": {
"mfa": {
"data": {
"mfa_code": "6-cyfrowy kod uwierzytelniania wielosk\u0142adnikowego"
},
"title": "Wprowad\u017a kod uwierzytelniania wielosk\u0142adnikowego dla Abode"
},
"reauth_confirm": {
"data": {
"password": "Has\u0142o",
"username": "Adres e-mail"
},
"title": "Wprowad\u017a informacje logowania Abode"
},
"user": {
"data": {
"password": "Has\u0142o",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.",
"single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e."
},
"error": {
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.",
"invalid_auth": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f."
"invalid_auth": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f.",
"invalid_mfa_code": "\u041d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434 MFA."
},
"step": {
"mfa": {
"data": {
"mfa_code": "\u041a\u043e\u0434 MFA (6 \u0446\u0438\u0444\u0440)"
},
"title": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 MFA \u0434\u043b\u044f Abode"
},
"reauth_confirm": {
"data": {
"password": "\u041f\u0430\u0440\u043e\u043b\u044c",
"username": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b"
},
"title": "Abode"
},
"user": {
"data": {
"password": "\u041f\u0430\u0440\u043e\u043b\u044c",

View File

@@ -1,13 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f",
"single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u8a2d\u5099\u3002"
},
"error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557",
"invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548"
"invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548",
"invalid_mfa_code": "\u591a\u6b65\u9a5f\u8a8d\u8b49\u78bc\u7121\u6548"
},
"step": {
"mfa": {
"data": {
"mfa_code": "\u591a\u6b65\u9a5f\u8a8d\u8b49\u78bc\uff086 \u4f4d\uff09"
},
"title": "\u8f38\u5165 Abode \u591a\u6b65\u9a5f\u8a8d\u8b49\u78bc"
},
"reauth_confirm": {
"data": {
"password": "\u5bc6\u78bc",
"username": "\u96fb\u5b50\u90f5\u4ef6"
},
"title": "\u586b\u5beb Abode \u767b\u5165\u8cc7\u8a0a"
},
"user": {
"data": {
"password": "\u5bc6\u78bc",

View File

@@ -1,4 +1,20 @@
"""Constants for AccuWeather integration."""
from homeassistant.components.weather import (
ATTR_CONDITION_CLEAR_NIGHT,
ATTR_CONDITION_CLOUDY,
ATTR_CONDITION_EXCEPTIONAL,
ATTR_CONDITION_FOG,
ATTR_CONDITION_HAIL,
ATTR_CONDITION_LIGHTNING,
ATTR_CONDITION_LIGHTNING_RAINY,
ATTR_CONDITION_PARTLYCLOUDY,
ATTR_CONDITION_POURING,
ATTR_CONDITION_RAINY,
ATTR_CONDITION_SNOWY,
ATTR_CONDITION_SNOWY_RAINY,
ATTR_CONDITION_SUNNY,
ATTR_CONDITION_WINDY,
)
from homeassistant.const import (
ATTR_DEVICE_CLASS,
CONCENTRATION_PARTS_PER_CUBIC_METER,
@@ -29,20 +45,20 @@ NAME = "AccuWeather"
UNDO_UPDATE_LISTENER = "undo_update_listener"
CONDITION_CLASSES = {
"clear-night": [33, 34, 37],
"cloudy": [7, 8, 38],
"exceptional": [24, 30, 31],
"fog": [11],
"hail": [25],
"lightning": [15],
"lightning-rainy": [16, 17, 41, 42],
"partlycloudy": [4, 6, 35, 36],
"pouring": [18],
"rainy": [12, 13, 14, 26, 39, 40],
"snowy": [19, 20, 21, 22, 23, 43, 44],
"snowy-rainy": [29],
"sunny": [1, 2, 3, 5],
"windy": [32],
ATTR_CONDITION_CLEAR_NIGHT: [33, 34, 37],
ATTR_CONDITION_CLOUDY: [7, 8, 38],
ATTR_CONDITION_EXCEPTIONAL: [24, 30, 31],
ATTR_CONDITION_FOG: [11],
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_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_WINDY: [32],
}
FORECAST_DAYS = [0, 1, 2, 3, 4]

View File

@@ -31,5 +31,11 @@
}
}
}
},
"system_health": {
"info": {
"can_reach_server": "Reach AccuWeather server",
"remaining_requests": "Remaining allowed requests"
}
}
}

View File

@@ -0,0 +1,27 @@
"""Provide info to system health."""
from accuweather.const import ENDPOINT
from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback
from .const import COORDINATOR, DOMAIN
@callback
def async_register(
hass: HomeAssistant, register: system_health.SystemHealthRegistration
) -> None:
"""Register system health callbacks."""
register.async_register_info(system_health_info)
async def system_health_info(hass):
"""Get info for the info page."""
remaining_requests = list(hass.data[DOMAIN].values())[0][
COORDINATOR
].accuweather.requests_remaining
return {
"can_reach_server": system_health.async_check_can_reach_url(hass, ENDPOINT),
"remaining_requests": remaining_requests,
}

View File

@@ -31,5 +31,11 @@
"title": "AccuWeather Options"
}
}
},
"system_health": {
"info": {
"can_reach_server": "Reach AccuWeather server",
"remaining_requests": "Remaining allowed requests"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"config": {
"error": {
"requests_exceeded": "Se super\u00f3 el n\u00famero permitido de solicitudes a la API de Accuweather. Tiene que esperar o cambiar la clave de API."
},
"step": {
"user": {
"description": "Si necesita ayuda con la configuraci\u00f3n, eche un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nAlgunos sensores no est\u00e1n habilitados de forma predeterminada. Puede habilitarlos en el registro de entidades despu\u00e9s de la configuraci\u00f3n de integraci\u00f3n. La previsi\u00f3n meteorol\u00f3gica no est\u00e1 habilitada de forma predeterminada. Puede habilitarlo en las opciones de integraci\u00f3n.",
"title": "AccuWeather"
}
}
},
"options": {
"step": {
"user": {
"data": {
"forecast": "Pron\u00f3stico del tiempo"
},
"description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos."
}
}
}
}

View File

@@ -31,5 +31,11 @@
"title": "AccuWeatheri valikud"
}
}
},
"system_health": {
"info": {
"can_reach_server": "\u00dchendu Accuweatheri serveriga",
"remaining_requests": "Lubatud taotlusi on j\u00e4\u00e4nud"
}
}
}

View File

@@ -3,7 +3,7 @@
"name": "Rollease Acmeda Automate",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/acmeda",
"requirements": ["aiopulse==0.4.0"],
"requirements": ["aiopulse==0.4.2"],
"codeowners": [
"@atmurray"
]

View File

@@ -6,7 +6,7 @@
"step": {
"user": {
"data": {
"id": "Verts-ID"
"id": "Vert ID"
},
"title": "Velg en hub du vil legge til"
}

View File

@@ -1,5 +1,8 @@
{
"config": {
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {

View File

@@ -9,7 +9,7 @@
},
"step": {
"hassio_confirm": {
"description": "Czy chcesz skonfigurowa\u0107 Home Assistant, aby po\u0142\u0105czy\u0142 si\u0119 z AdGuard Home przez dodatek Hass.io {addon}?",
"description": "Czy chcesz skonfigurowa\u0107 Home Assistanta, aby po\u0142\u0105czy\u0142 si\u0119 z AdGuard Home przez dodatek Hass.io {addon}?",
"title": "AdGuard Home przez dodatek Hass.io"
},
"user": {

View File

@@ -1,6 +1,6 @@
"""Constants used by Advantage Air integration."""
DOMAIN = "advantage_air"
ADVANTAGE_AIR_RETRY = 5
ADVANTAGE_AIR_RETRY = 10
ADVANTAGE_AIR_STATE_OPEN = "open"
ADVANTAGE_AIR_STATE_CLOSE = "close"
ADVANTAGE_AIR_STATE_ON = "on"

View File

@@ -0,0 +1,9 @@
{
"config": {
"step": {
"user": {
"title": "Conectar"
}
}
}
}

View File

@@ -0,0 +1,17 @@
{
"config": {
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {
"ip_address": "IP c\u00edm",
"port": "Port"
},
"description": "Csatlakozzon az Advantage Air fali t\u00e1blag\u00e9p API-j\u00e1hoz.",
"title": "Csatlakoz\u00e1s"
}
}
}
}

View File

@@ -0,0 +1,17 @@
{
"config": {
"error": {
"cannot_connect": "\u10db\u10d0\u10e0\u10ea\u10ee\u10d8 \u10e8\u10d4\u10d4\u10e0\u10d7\u10d4\u10d1\u10d8\u10e1\u10d0\u10e1"
},
"step": {
"user": {
"data": {
"ip_address": "IP \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8",
"port": "\u10de\u10dd\u10e0\u10e2\u10d8"
},
"description": "\u10d3\u10d0\u10e3\u10d9\u10d0\u10d5\u10e8\u10d8\u10e0\u10d3\u10d8\u10d7 \u10d7\u10e5\u10d5\u10d4\u10dc\u10d8 Advantage Air API-\u10e1 \u10d9\u10d4\u10d3\u10d4\u10da\u10d6\u10d4 \u10d3\u10d0\u10db\u10dd\u10dc\u10e2\u10d0\u10df\u10d4\u10d1\u10e3\u10da\u10d8 \u10e2\u10d0\u10d1\u10da\u10d4\u10e2\u10d8\u10d7",
"title": "\u10d3\u10d0\u10d9\u10d0\u10d5\u10e8\u10d8\u10e0\u10d4\u10d1\u10d0"
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"config": {
"step": {
"user": {
"title": "Pove\u017eite se"
}
}
}
}

View File

@@ -1,5 +1,8 @@
{
"config": {
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {

View File

@@ -0,0 +1,7 @@
{
"config": {
"error": {
"cannot_connect": "\u10db\u10d0\u10e0\u10ea\u10ee\u10d8 \u10e8\u10d4\u10d4\u10e0\u10d7\u10d4\u10d1\u10d8\u10e1\u10d0\u10e1"
}
}
}

View File

@@ -19,5 +19,10 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
}
},
"system_health": {
"info": {
"can_reach_server": "Reach Airly server"
}
}
}

View File

@@ -0,0 +1,22 @@
"""Provide info to system health."""
from airly import Airly
from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback
@callback
def async_register(
hass: HomeAssistant, register: system_health.SystemHealthRegistration
) -> None:
"""Register system health callbacks."""
register.async_register_info(system_health_info)
async def system_health_info(hass):
"""Get info for the info page."""
return {
"can_reach_server": system_health.async_check_can_reach_url(
hass, Airly.AIRLY_API_URL
)
}

View File

@@ -19,5 +19,10 @@
"title": "Airly"
}
}
},
"system_health": {
"info": {
"can_reach_server": "Reach Airly server"
}
}
}

View File

@@ -19,5 +19,10 @@
"title": ""
}
}
},
"system_health": {
"info": {
"can_reach_server": "\u00dchendu Airly serveriga"
}
}
}

View File

@@ -49,7 +49,7 @@ DATA_LISTENER = "listener"
DEFAULT_ATTRIBUTION = "Data provided by AirVisual"
DEFAULT_NODE_PRO_UPDATE_INTERVAL = timedelta(minutes=1)
CONFIG_SCHEMA = cv.deprecated(DOMAIN, invalidation_version="0.119")
CONFIG_SCHEMA = cv.deprecated(DOMAIN)
@callback

View File

@@ -1,6 +1,7 @@
{
"config": {
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
"general_error": "Ismeretlen hiba t\u00f6rt\u00e9nt."
},
"step": {
@@ -15,6 +16,11 @@
"data": {
"password": "Jelsz\u00f3"
}
},
"reauth_confirm": {
"data": {
"api_key": "API kulcs"
}
}
}
}

View File

@@ -0,0 +1,15 @@
{
"config": {
"abort": {
"reauth_successful": "\u10ec\u10d0\u10e0\u10db\u10d0\u10e2\u10d4\u10d1\u10e3\u10da\u10d8 \u10e0\u10d4-\u10d0\u10d5\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0"
},
"step": {
"reauth_confirm": {
"data": {
"api_key": "API Key"
},
"title": "AirVisual \u10e0\u10d4-\u10d0\u10d5\u10d7\u10d4\u10dc\u10d7\u10d8\u10d9\u10d0\u10ea\u10d8\u10d0"
}
}
}
}

View File

@@ -15,9 +15,9 @@
"is_triggered": "{entity_name} on h\u00e4iret andnud"
},
"trigger_type": {
"armed_away": "{entity_name} valvestatus",
"armed_home": "{entity_name} valvestatus kodure\u017eiimis",
"armed_night": "{entity_name} valvestatus \u00f6\u00f6re\u017eiimis",
"armed_away": "{entity_name} valvestati",
"armed_home": "{entity_name} valvestati kodure\u017eiimis",
"armed_night": "{entity_name} valvestati \u00f6\u00f6re\u017eiimis",
"disarmed": "{entity_name} v\u00f5eti valvest maha",
"triggered": "{entity_name} andis h\u00e4iret"
}

View File

@@ -0,0 +1,35 @@
{
"config": {
"create_entry": {
"default": "Conectado con \u00e9xito a AlarmDecoder."
},
"step": {
"protocol": {
"data": {
"device_baudrate": "Tasa de baudios del dispositivo",
"device_path": "Ruta del dispositivo"
},
"title": "Configurar los ajustes de conexi\u00f3n"
},
"user": {
"data": {
"protocol": "Protocolo"
}
}
}
},
"options": {
"step": {
"arm_settings": {
"data": {
"alt_night_mode": "Modo nocturno alternativo"
}
},
"init": {
"data": {
"edit_select": "Editar"
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
{
"config": {
"create_entry": {
"default": "Sikeres csatlakoz\u00e1s az AlarmDecoderhez."
},
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
}
}
}

View File

@@ -0,0 +1,7 @@
{
"config": {
"abort": {
"already_configured": "Naprava je \u017ee nastavljena"
}
}
}

View File

@@ -412,10 +412,17 @@ class AlexaLockController(AlexaCapability):
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):
@@ -454,6 +461,7 @@ class AlexaSceneController(AlexaCapability):
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
@@ -461,6 +469,7 @@ class AlexaSceneController(AlexaCapability):
"es-ES",
"fr-FR",
"it-IT",
"ja-JP",
}
def __init__(self, entity, supports_deactivation):
@@ -488,8 +497,10 @@ class AlexaBrightnessController(AlexaCapability):
"en-US",
"es-ES",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):
@@ -532,8 +543,10 @@ class AlexaColorController(AlexaCapability):
"en-US",
"es-ES",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):
@@ -581,8 +594,10 @@ class AlexaColorTemperatureController(AlexaCapability):
"en-US",
"es-ES",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):
@@ -669,7 +684,18 @@ class AlexaSpeaker(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-speaker.html
"""
supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"}
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"it-IT",
"ja-JP",
}
def name(self):
"""Return the Alexa API name of this interface."""
@@ -716,7 +742,16 @@ class AlexaStepSpeaker(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-stepspeaker.html
"""
supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"}
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"it-IT",
}
def name(self):
"""Return the Alexa API name of this interface."""
@@ -866,7 +901,16 @@ class AlexaContactSensor(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-contactsensor.html
"""
supported_locales = {"en-CA", "en-US", "it-IT"}
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-IN",
"en-US",
"es-ES",
"it-IT",
"ja-JP",
}
def __init__(self, hass, entity):
"""Initialize the entity."""
@@ -905,7 +949,17 @@ class AlexaMotionSensor(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-motionsensor.html
"""
supported_locales = {"en-CA", "en-US", "it-IT"}
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-IN",
"en-US",
"es-ES",
"it-IT",
"ja-JP",
"pt-BR",
}
def __init__(self, hass, entity):
"""Initialize the entity."""
@@ -955,6 +1009,7 @@ class AlexaThermostatController(AlexaCapability):
"fr-FR",
"it-IT",
"ja-JP",
"pt-BR",
}
def __init__(self, hass, entity):
@@ -1127,7 +1182,7 @@ class AlexaSecurityPanelController(AlexaCapability):
"fr-FR",
"it-IT",
"ja-JP",
"pt_BR",
"pt-BR",
}
def __init__(self, hass, entity):
@@ -1509,7 +1564,7 @@ class AlexaRangeController(AlexaCapability):
min_value = float(self.entity.attributes[input_number.ATTR_MIN])
max_value = float(self.entity.attributes[input_number.ATTR_MAX])
precision = float(self.entity.attributes.get(input_number.ATTR_STEP, 1))
unit = self.entity.attributes.get(input_number.ATTR_UNIT_OF_MEASUREMENT)
unit = self.entity.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
self._resource = AlexaPresetResource(
["Value", AlexaGlobalCatalog.SETTING_PRESET],
@@ -1623,6 +1678,7 @@ class AlexaToggleController(AlexaCapability):
"fr-FR",
"it-IT",
"ja-JP",
"pt-BR",
}
def __init__(self, entity, instance, non_controllable=False):
@@ -1679,7 +1735,21 @@ class AlexaChannelController(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-channelcontroller.html
"""
supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"}
supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):
"""Return the Alexa API name of this interface."""
@@ -1693,7 +1763,6 @@ class AlexaDoorbellEventSource(AlexaCapability):
"""
supported_locales = {
"en-US",
"de-DE",
"en-AU",
"en-CA",
@@ -1702,8 +1771,10 @@ class AlexaDoorbellEventSource(AlexaCapability):
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
}
@@ -1723,7 +1794,7 @@ class AlexaPlaybackStateReporter(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-playbackstatereporter.html
"""
supported_locales = {"de-DE", "en-GB", "en-US", "fr-FR"}
supported_locales = {"de-DE", "en-GB", "en-US", "es-MX", "fr-FR"}
def name(self):
"""Return the Alexa API name of this interface."""
@@ -1761,7 +1832,7 @@ class AlexaSeekController(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-seekcontroller.html
"""
supported_locales = {"de-DE", "en-GB", "en-US"}
supported_locales = {"de-DE", "en-GB", "en-US", "es-MX"}
def name(self):
"""Return the Alexa API name of this interface."""
@@ -1833,7 +1904,7 @@ class AlexaEqualizerController(AlexaCapability):
https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-equalizercontroller.html
"""
supported_locales = {"en-US"}
supported_locales = {"de-DE", "en-IN", "en-US", "es-ES", "it-IT", "ja-JP", "pt-BR"}
VALID_SOUND_MODES = {
"MOVIE",
"MUSIC",
@@ -1929,8 +2000,10 @@ class AlexaCameraStreamController(AlexaCapability):
"en-US",
"es-ES",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self):

View File

@@ -995,14 +995,14 @@ async def async_api_set_mode(hass, config, directive, context):
# Fan Direction
if instance == f"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
_, direction = mode.split(".")
direction = mode.split(".")[1]
if direction in (fan.DIRECTION_REVERSE, fan.DIRECTION_FORWARD):
service = fan.SERVICE_SET_DIRECTION
data[fan.ATTR_DIRECTION] = direction
# Cover Position
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
_, position = mode.split(".")
position = mode.split(".")[1]
if position == cover.STATE_CLOSED:
service = cover.SERVICE_CLOSE_COVER

View File

@@ -0,0 +1,7 @@
{
"config": {
"create_entry": {
"default": "Sikeres autentik\u00e1ci\u00f3"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"config": {
"abort": {
"already_configured": "\u10d0\u10dc\u10d2\u10d0\u10e0\u10d8\u10e8\u10d8 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8\u10d0",
"missing_configuration": "\u10d4\u10e1 \u10d9\u10dd\u10db\u10de\u10dd\u10dc\u10d4\u10dc\u10e2\u10d8 \u10d0\u10e0 \u10d0\u10e0\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8. \u10d2\u10d7\u10ee\u10dd\u10d5\u10d7 \u10db\u10d8\u10e7\u10d5\u10d4\u10d7 \u10d3\u10dd\u10d9\u10e3\u10db\u10d4\u10dc\u10e2\u10d0\u10ea\u10d8\u10d0\u10e1"
}
}
}

View File

@@ -1,6 +1,5 @@
"""Support for Ambient Weather Station Service."""
import asyncio
import logging
from aioambient import Client
from aioambient.errors import WebsocketError
@@ -39,12 +38,11 @@ from .const import (
CONF_APP_KEY,
DATA_CLIENT,
DOMAIN,
LOGGER,
TYPE_BINARY_SENSOR,
TYPE_SENSOR,
)
_LOGGER = logging.getLogger(__name__)
DATA_CONFIG = "config"
DEFAULT_SOCKET_MIN_RETRY = 15
@@ -307,7 +305,7 @@ async def async_setup_entry(hass, config_entry):
hass.loop.create_task(ambient.ws_connect())
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = ambient
except WebsocketError as err:
_LOGGER.error("Config entry failed: %s", err)
LOGGER.error("Config entry failed: %s", err)
raise ConfigEntryNotReady from err
async def _async_disconnect_websocket(*_):
@@ -337,7 +335,7 @@ async def async_migrate_entry(hass, config_entry):
"""Migrate old entry."""
version = config_entry.version
_LOGGER.debug("Migrating from version %s", version)
LOGGER.debug("Migrating from version %s", version)
# 1 -> 2: Unique ID format changed, so delete and re-import:
if version == 1:
@@ -350,7 +348,7 @@ async def async_migrate_entry(hass, config_entry):
version = config_entry.version = 2
hass.config_entries.async_update_entry(config_entry)
_LOGGER.info("Migration to version %s successful", version)
LOGGER.info("Migration to version %s successful", version)
return True
@@ -377,7 +375,7 @@ class AmbientStation:
try:
await connect()
except WebsocketError as err:
_LOGGER.error("Error with the websocket connection: %s", err)
LOGGER.error("Error with the websocket connection: %s", err)
self._ws_reconnect_delay = min(2 * self._ws_reconnect_delay, 480)
async_call_later(self._hass, self._ws_reconnect_delay, connect)
@@ -386,13 +384,13 @@ class AmbientStation:
def on_connect():
"""Define a handler to fire when the websocket is connected."""
_LOGGER.info("Connected to websocket")
LOGGER.info("Connected to websocket")
def on_data(data):
"""Define a handler to fire when the data is received."""
mac_address = data["macAddress"]
if data != self.stations[mac_address][ATTR_LAST_DATA]:
_LOGGER.debug("New data received: %s", data)
LOGGER.debug("New data received: %s", data)
self.stations[mac_address][ATTR_LAST_DATA] = data
async_dispatcher_send(
self._hass, f"ambient_station_data_update_{mac_address}"
@@ -400,7 +398,7 @@ class AmbientStation:
def on_disconnect():
"""Define a handler to fire when the websocket is disconnected."""
_LOGGER.info("Disconnected from websocket")
LOGGER.info("Disconnected from websocket")
def on_subscribed(data):
"""Define a handler to fire when the subscription is set."""
@@ -408,7 +406,7 @@ class AmbientStation:
if station["macAddress"] in self.stations:
continue
_LOGGER.debug("New station subscription: %s", data)
LOGGER.debug("New station subscription: %s", data)
# Only create entities based on the data coming through the socket.
# If the user is monitoring brightness (in W/m^2), make sure we also

View File

@@ -1,5 +1,8 @@
"""Define constants for the Ambient PWS component."""
import logging
DOMAIN = "ambient_station"
LOGGER = logging.getLogger(__package__)
ATTR_LAST_DATA = "last_data"
ATTR_MONITORED_CONDITIONS = "monitored_conditions"

View File

@@ -236,7 +236,7 @@ class AmcrestCam(Camera):
# streaming via ffmpeg
streaming_url = self._rtsp_url
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
stream = CameraMjpeg(self._ffmpeg.binary)
await stream.open_camera(streaming_url, extra_cmd=self._ffmpeg_arguments)
try:

View File

@@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/androidtv",
"requirements": [
"adb-shell[async]==0.2.1",
"androidtv[async]==0.0.54",
"androidtv[async]==0.0.56",
"pure-python-adb[async]==0.3.0.dev0"
],
"codeowners": ["@JeffLIrion"]

View File

@@ -56,7 +56,7 @@ STREAM_PING_PAYLOAD = "ping"
STREAM_PING_INTERVAL = 50 # seconds
def setup(hass, config):
async def async_setup(hass, config):
"""Register the API with the HTTP interface."""
hass.http.register_view(APIStatusView)
hass.http.register_view(APIEventStream)

View File

@@ -1,273 +1,363 @@
"""Support for Apple TV."""
"""The Apple TV integration."""
import asyncio
import logging
from typing import Sequence, TypeVar, Union
from random import randrange
from pyatv import AppleTVDevice, connect_to_apple_tv, scan_for_apple_tvs
from pyatv.exceptions import DeviceAuthenticationError
import voluptuous as vol
from pyatv import connect, exceptions, scan
from pyatv.const import Protocol
from homeassistant.components.discovery import SERVICE_APPLE_TV
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME
from homeassistant.helpers import discovery
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.components.remote import DOMAIN as REMOTE_DOMAIN
from homeassistant.const import (
CONF_ADDRESS,
CONF_NAME,
CONF_PROTOCOL,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity import Entity
from .const import CONF_CREDENTIALS, CONF_IDENTIFIER, CONF_START_OFF, DOMAIN
_LOGGER = logging.getLogger(__name__)
DOMAIN = "apple_tv"
SERVICE_SCAN = "apple_tv_scan"
SERVICE_AUTHENTICATE = "apple_tv_authenticate"
ATTR_ATV = "atv"
ATTR_POWER = "power"
CONF_LOGIN_ID = "login_id"
CONF_START_OFF = "start_off"
CONF_CREDENTIALS = "credentials"
DEFAULT_NAME = "Apple TV"
DATA_APPLE_TV = "data_apple_tv"
DATA_ENTITIES = "data_apple_tv_entities"
BACKOFF_TIME_UPPER_LIMIT = 300 # Five minutes
KEY_CONFIG = "apple_tv_configuring"
NOTIFICATION_TITLE = "Apple TV Notification"
NOTIFICATION_ID = "apple_tv_notification"
NOTIFICATION_AUTH_ID = "apple_tv_auth_notification"
NOTIFICATION_AUTH_TITLE = "Apple TV Authentication"
NOTIFICATION_SCAN_ID = "apple_tv_scan_notification"
NOTIFICATION_SCAN_TITLE = "Apple TV Scan"
SOURCE_REAUTH = "reauth"
T = TypeVar("T")
SIGNAL_CONNECTED = "apple_tv_connected"
SIGNAL_DISCONNECTED = "apple_tv_disconnected"
# This version of ensure_list interprets an empty dict as no value
def ensure_list(value: Union[T, Sequence[T]]) -> Sequence[T]:
"""Wrap value in list if it is not one."""
if value is None or (isinstance(value, dict) and not value):
return []
return value if isinstance(value, list) else [value]
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.All(
ensure_list,
[
vol.Schema(
{
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_LOGIN_ID): cv.string,
vol.Optional(CONF_CREDENTIALS): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_START_OFF, default=False): cv.boolean,
}
)
],
)
},
extra=vol.ALLOW_EXTRA,
)
# Currently no attributes but it might change later
APPLE_TV_SCAN_SCHEMA = vol.Schema({})
APPLE_TV_AUTHENTICATE_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids})
def request_configuration(hass, config, atv, credentials):
"""Request configuration steps from the user."""
configurator = hass.components.configurator
async def configuration_callback(callback_data):
"""Handle the submitted configuration."""
pin = callback_data.get("pin")
try:
await atv.airplay.finish_authentication(pin)
hass.components.persistent_notification.async_create(
f"Authentication succeeded!<br /><br />"
f"Add the following to credentials: "
f"in your apple_tv configuration:<br /><br />{credentials}",
title=NOTIFICATION_AUTH_TITLE,
notification_id=NOTIFICATION_AUTH_ID,
)
except DeviceAuthenticationError as ex:
hass.components.persistent_notification.async_create(
f"Authentication failed! Did you enter correct PIN?<br /><br />Details: {ex}",
title=NOTIFICATION_AUTH_TITLE,
notification_id=NOTIFICATION_AUTH_ID,
)
hass.async_add_job(configurator.request_done, instance)
instance = configurator.request_config(
"Apple TV Authentication",
configuration_callback,
description="Please enter PIN code shown on screen.",
submit_caption="Confirm",
fields=[{"id": "pin", "name": "PIN Code", "type": "password"}],
)
async def scan_apple_tvs(hass):
"""Scan for devices and present a notification of the ones found."""
atvs = await scan_for_apple_tvs(hass.loop, timeout=3)
devices = []
for atv in atvs:
login_id = atv.login_id
if login_id is None:
login_id = "Home Sharing disabled"
devices.append(
f"Name: {atv.name}<br />Host: {atv.address}<br />Login ID: {login_id}"
)
if not devices:
devices = ["No device(s) found"]
found_devices = "<br /><br />".join(devices)
hass.components.persistent_notification.async_create(
f"The following devices were found:<br /><br />{found_devices}",
title=NOTIFICATION_SCAN_TITLE,
notification_id=NOTIFICATION_SCAN_ID,
)
PLATFORMS = [MP_DOMAIN, REMOTE_DOMAIN]
async def async_setup(hass, config):
"""Set up the Apple TV component."""
if DATA_APPLE_TV not in hass.data:
hass.data[DATA_APPLE_TV] = {}
"""Set up the Apple TV integration."""
return True
async def async_service_handler(service):
"""Handle service calls."""
entity_ids = service.data.get(ATTR_ENTITY_ID)
if service.service == SERVICE_SCAN:
hass.async_add_job(scan_apple_tvs, hass)
return
async def async_setup_entry(hass, entry):
"""Set up a config entry for Apple TV."""
manager = AppleTVManager(hass, entry)
hass.data.setdefault(DOMAIN, {})[entry.unique_id] = manager
if entity_ids:
devices = [
device
for device in hass.data[DATA_ENTITIES]
if device.entity_id in entity_ids
async def on_hass_stop(event):
"""Stop push updates when hass stops."""
await manager.disconnect()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
async def setup_platforms():
"""Set up platforms and initiate connection."""
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_setup(entry, component)
for component in PLATFORMS
]
else:
devices = hass.data[DATA_ENTITIES]
for device in devices:
if service.service != SERVICE_AUTHENTICATE:
continue
atv = device.atv
credentials = await atv.airplay.generate_credentials()
await atv.airplay.load_credentials(credentials)
_LOGGER.debug("Generated new credentials: %s", credentials)
await atv.airplay.start_authentication()
hass.async_add_job(request_configuration, hass, config, atv, credentials)
async def atv_discovered(service, info):
"""Set up an Apple TV that was auto discovered."""
await _setup_atv(
hass,
config,
{
CONF_NAME: info["name"],
CONF_HOST: info["host"],
CONF_LOGIN_ID: info["properties"]["hG"],
CONF_START_OFF: False,
},
)
await manager.init()
discovery.async_listen(hass, SERVICE_APPLE_TV, atv_discovered)
tasks = [_setup_atv(hass, config, conf) for conf in config.get(DOMAIN, [])]
if tasks:
await asyncio.wait(tasks)
hass.services.async_register(
DOMAIN, SERVICE_SCAN, async_service_handler, schema=APPLE_TV_SCAN_SCHEMA
)
hass.services.async_register(
DOMAIN,
SERVICE_AUTHENTICATE,
async_service_handler,
schema=APPLE_TV_AUTHENTICATE_SCHEMA,
)
hass.async_create_task(setup_platforms())
return True
async def _setup_atv(hass, hass_config, atv_config):
"""Set up an Apple TV."""
name = atv_config.get(CONF_NAME)
host = atv_config.get(CONF_HOST)
login_id = atv_config.get(CONF_LOGIN_ID)
start_off = atv_config.get(CONF_START_OFF)
credentials = atv_config.get(CONF_CREDENTIALS)
if host in hass.data[DATA_APPLE_TV]:
return
details = AppleTVDevice(name, host, login_id)
session = async_get_clientsession(hass)
atv = connect_to_apple_tv(details, hass.loop, session=session)
if credentials:
await atv.airplay.load_credentials(credentials)
power = AppleTVPowerManager(hass, atv, start_off)
hass.data[DATA_APPLE_TV][host] = {ATTR_ATV: atv, ATTR_POWER: power}
hass.async_create_task(
discovery.async_load_platform(
hass, "media_player", DOMAIN, atv_config, hass_config
async def async_unload_entry(hass, entry):
"""Unload an Apple TV config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
if unload_ok:
manager = hass.data[DOMAIN].pop(entry.unique_id)
await manager.disconnect()
hass.async_create_task(
discovery.async_load_platform(hass, "remote", DOMAIN, atv_config, hass_config)
)
return unload_ok
class AppleTVPowerManager:
"""Manager for global power management of an Apple TV.
class AppleTVEntity(Entity):
"""Device that sends commands to an Apple TV."""
An instance is used per device to share the same power state between
several platforms.
"""
def __init__(self, name, identifier, manager):
"""Initialize device."""
self.atv = None
self.manager = manager
self._name = name
self._identifier = identifier
def __init__(self, hass, atv, is_off):
"""Initialize power manager."""
self.hass = hass
self.atv = atv
self.listeners = []
self._is_on = not is_off
async def async_added_to_hass(self):
"""Handle when an entity is about to be added to Home Assistant."""
def init(self):
"""Initialize power management."""
if self._is_on:
self.atv.push_updater.start()
@callback
def _async_connected(atv):
"""Handle that a connection was made to a device."""
self.atv = atv
self.async_device_connected(atv)
self.async_write_ha_state()
@callback
def _async_disconnected():
"""Handle that a connection to a device was lost."""
self.async_device_disconnected()
self.atv = None
self.async_write_ha_state()
self.async_on_remove(
async_dispatcher_connect(
self.hass, f"{SIGNAL_CONNECTED}_{self._identifier}", _async_connected
)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SIGNAL_DISCONNECTED}_{self._identifier}",
_async_disconnected,
)
)
def async_device_connected(self, atv):
"""Handle when connection is made to device."""
def async_device_disconnected(self):
"""Handle when connection was lost to device."""
@property
def turned_on(self):
"""Return true if device is on or off."""
return self._is_on
def device_info(self):
"""Return the device info."""
return {
"identifiers": {(DOMAIN, self._identifier)},
"manufacturer": "Apple",
"name": self.name,
}
def set_power_on(self, value):
"""Change if a device is on or off."""
if value != self._is_on:
self._is_on = value
if not self._is_on:
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def unique_id(self):
"""Return a unique ID."""
return self._identifier
@property
def should_poll(self):
"""No polling needed for Apple TV."""
return False
class AppleTVManager:
"""Connection and power manager for an Apple TV.
An instance is used per device to share the same power state between
several platforms. It also manages scanning and connection establishment
in case of problems.
"""
def __init__(self, hass, config_entry):
"""Initialize power manager."""
self.config_entry = config_entry
self.hass = hass
self.atv = None
self._is_on = not config_entry.options.get(CONF_START_OFF, False)
self._connection_attempts = 0
self._connection_was_lost = False
self._task = None
async def init(self):
"""Initialize power management."""
if self._is_on:
await self.connect()
def connection_lost(self, _):
"""Device was unexpectedly disconnected.
This is a callback function from pyatv.interface.DeviceListener.
"""
_LOGGER.warning('Connection lost to Apple TV "%s"', self.atv.name)
if self.atv:
self.atv.close()
self.atv = None
self._connection_was_lost = True
self._dispatch_send(SIGNAL_DISCONNECTED)
self._start_connect_loop()
def connection_closed(self):
"""Device connection was (intentionally) closed.
This is a callback function from pyatv.interface.DeviceListener.
"""
if self.atv:
self.atv.close()
self.atv = None
self._dispatch_send(SIGNAL_DISCONNECTED)
self._start_connect_loop()
async def connect(self):
"""Connect to device."""
self._is_on = True
self._start_connect_loop()
async def disconnect(self):
"""Disconnect from device."""
_LOGGER.debug("Disconnecting from device")
self._is_on = False
try:
if self.atv:
self.atv.push_updater.listener = None
self.atv.push_updater.stop()
else:
self.atv.push_updater.start()
self.atv.close()
self.atv = None
if self._task:
self._task.cancel()
self._task = None
except Exception: # pylint: disable=broad-except
_LOGGER.exception("An error occurred while disconnecting")
for listener in self.listeners:
self.hass.async_create_task(listener.async_update_ha_state())
def _start_connect_loop(self):
"""Start background connect loop to device."""
if not self._task and self.atv is None and self._is_on:
self._task = asyncio.create_task(self._connect_loop())
else:
_LOGGER.debug(
"Not starting connect loop (%s, %s)", self.atv is None, self._is_on
)
async def _connect_loop(self):
"""Connect loop background task function."""
_LOGGER.debug("Starting connect loop")
# Try to find device and connect as long as the user has said that
# we are allowed to connect and we are not already connected.
while self._is_on and self.atv is None:
try:
conf = await self._scan()
if conf:
await self._connect(conf)
except exceptions.AuthenticationError:
self._auth_problem()
break
except asyncio.CancelledError:
pass
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Failed to connect")
self.atv = None
if self.atv is None:
self._connection_attempts += 1
backoff = min(
randrange(2 ** self._connection_attempts), BACKOFF_TIME_UPPER_LIMIT
)
_LOGGER.debug("Reconnecting in %d seconds", backoff)
await asyncio.sleep(backoff)
_LOGGER.debug("Connect loop ended")
self._task = None
def _auth_problem(self):
"""Problem to authenticate occurred that needs intervention."""
_LOGGER.debug("Authentication error, reconfigure integration")
name = self.config_entry.data.get(CONF_NAME)
identifier = self.config_entry.unique_id
self.hass.components.persistent_notification.create(
"An irrecoverable connection problem occurred when connecting to "
f"`f{name}`. Please go to the Integrations page and reconfigure it",
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID,
)
# Add to event queue as this function is called from a task being
# cancelled from disconnect
asyncio.create_task(self.disconnect())
self.hass.async_create_task(
self.hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data={CONF_NAME: name, CONF_IDENTIFIER: identifier},
)
)
async def _scan(self):
"""Try to find device by scanning for it."""
identifier = self.config_entry.unique_id
address = self.config_entry.data[CONF_ADDRESS]
protocol = Protocol(self.config_entry.data[CONF_PROTOCOL])
_LOGGER.debug("Discovering device %s", identifier)
atvs = await scan(
self.hass.loop, identifier=identifier, protocol=protocol, hosts=[address]
)
if atvs:
return atvs[0]
_LOGGER.debug(
"Failed to find device %s with address %s, trying to scan",
identifier,
address,
)
atvs = await scan(self.hass.loop, identifier=identifier, protocol=protocol)
if atvs:
return atvs[0]
_LOGGER.debug("Failed to find device %s, trying later", identifier)
return None
async def _connect(self, conf):
"""Connect to device."""
credentials = self.config_entry.data[CONF_CREDENTIALS]
session = async_get_clientsession(self.hass)
for protocol, creds in credentials.items():
conf.set_credentials(Protocol(int(protocol)), creds)
_LOGGER.debug("Connecting to device %s", self.config_entry.data[CONF_NAME])
self.atv = await connect(conf, self.hass.loop, session=session)
self.atv.listener = self
self._dispatch_send(SIGNAL_CONNECTED, self.atv)
self._address_updated(str(conf.address))
self._connection_attempts = 0
if self._connection_was_lost:
_LOGGER.info(
'Connection was re-established to Apple TV "%s"', self.atv.service.name
)
self._connection_was_lost = False
@property
def is_connecting(self):
"""Return true if connection is in progress."""
return self._task is not None
def _address_updated(self, address):
"""Update cached address in config entry."""
_LOGGER.debug("Changing address to %s", address)
self.hass.config_entries.async_update_entry(
self.config_entry, data={**self.config_entry.data, CONF_ADDRESS: address}
)
def _dispatch_send(self, signal, *args):
"""Dispatch a signal to all entities managed by this manager."""
async_dispatcher_send(
self.hass, f"{signal}_{self.config_entry.unique_id}", *args
)

View File

@@ -0,0 +1,408 @@
"""Config flow for Apple TV integration."""
from ipaddress import ip_address
import logging
from random import randrange
from pyatv import exceptions, pair, scan
from pyatv.const import Protocol
from pyatv.convert import protocol_str
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import (
CONF_ADDRESS,
CONF_NAME,
CONF_PIN,
CONF_PROTOCOL,
CONF_TYPE,
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_CREDENTIALS, CONF_IDENTIFIER, CONF_START_OFF
from .const import DOMAIN # pylint: disable=unused-import
_LOGGER = logging.getLogger(__name__)
DEVICE_INPUT = "device_input"
INPUT_PIN_SCHEMA = vol.Schema({vol.Required(CONF_PIN, default=None): int})
DEFAULT_START_OFF = False
PROTOCOL_PRIORITY = [Protocol.MRP, Protocol.DMAP, Protocol.AirPlay]
async def device_scan(identifier, loop, cache=None):
"""Scan for a specific device using identifier as filter."""
def _filter_device(dev):
if identifier is None:
return True
if identifier == str(dev.address):
return True
if identifier == dev.name:
return True
return any([service.identifier == identifier for service in dev.services])
def _host_filter():
try:
return [ip_address(identifier)]
except ValueError:
return None
if cache:
matches = [atv for atv in cache if _filter_device(atv)]
if matches:
return cache, matches[0]
for hosts in [_host_filter(), None]:
scan_result = await scan(loop, timeout=3, hosts=hosts)
matches = [atv for atv in scan_result if _filter_device(atv)]
if matches:
return scan_result, matches[0]
return scan_result, None
def is_valid_credentials(credentials):
"""Verify that credentials are valid for establishing a connection."""
return (
credentials.get(Protocol.MRP.value) is not None
or credentials.get(Protocol.DMAP.value) is not None
)
class AppleTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Apple TV."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get options flow for this handler."""
return AppleTVOptionsFlow(config_entry)
def __init__(self):
"""Initialize a new AppleTVConfigFlow."""
self.target_device = None
self.scan_result = None
self.atv = None
self.protocol = None
self.pairing = None
self.credentials = {} # Protocol -> credentials
async def async_step_reauth(self, info):
"""Handle initial step when updating invalid credentials."""
await self.async_set_unique_id(info[CONF_IDENTIFIER])
self.target_device = info[CONF_IDENTIFIER]
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["title_placeholders"] = {"name": info[CONF_NAME]}
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["identifier"] = self.unique_id
return await self.async_step_reconfigure()
async def async_step_reconfigure(self, user_input=None):
"""Inform user that reconfiguration is about to start."""
if user_input is not None:
return await self.async_find_device_wrapper(
self.async_begin_pairing, allow_exist=True
)
return self.async_show_form(step_id="reconfigure")
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
# Be helpful to the user and look for devices
if self.scan_result is None:
self.scan_result, _ = await device_scan(None, self.hass.loop)
errors = {}
default_suggestion = self._prefill_identifier()
if user_input is not None:
self.target_device = user_input[DEVICE_INPUT]
try:
await self.async_find_device()
except DeviceNotFound:
errors["base"] = "no_devices_found"
except DeviceAlreadyConfigured:
errors["base"] = "already_configured"
except exceptions.NoServiceError:
errors["base"] = "no_usable_service"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(
self.atv.identifier, raise_on_progress=False
)
return await self.async_step_confirm()
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{vol.Required(DEVICE_INPUT, default=default_suggestion): str}
),
errors=errors,
description_placeholders={"devices": self._devices_str()},
)
async def async_step_zeroconf(self, discovery_info):
"""Handle device found via zeroconf."""
service_type = discovery_info[CONF_TYPE]
properties = discovery_info["properties"]
if service_type == "_mediaremotetv._tcp.local.":
identifier = properties["UniqueIdentifier"]
name = properties["Name"]
elif service_type == "_touch-able._tcp.local.":
identifier = discovery_info["name"].split(".")[0]
name = properties["CtlN"]
else:
return self.async_abort(reason="unknown")
await self.async_set_unique_id(identifier)
self._abort_if_unique_id_configured()
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context["identifier"] = self.unique_id
self.context["title_placeholders"] = {"name": name}
self.target_device = identifier
return await self.async_find_device_wrapper(self.async_step_confirm)
async def async_find_device_wrapper(self, next_func, allow_exist=False):
"""Find a specific device and call another function when done.
This function will do error handling and bail out when an error
occurs.
"""
try:
await self.async_find_device(allow_exist)
except DeviceNotFound:
return self.async_abort(reason="no_devices_found")
except DeviceAlreadyConfigured:
return self.async_abort(reason="already_configured")
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
return self.async_abort(reason="unknown")
return await next_func()
async def async_find_device(self, allow_exist=False):
"""Scan for the selected device to discover services."""
self.scan_result, self.atv = await device_scan(
self.target_device, self.hass.loop, cache=self.scan_result
)
if not self.atv:
raise DeviceNotFound()
self.protocol = self.atv.main_service().protocol
if not allow_exist:
for identifier in self.atv.all_identifiers:
if identifier in self._async_current_ids():
raise DeviceAlreadyConfigured()
# If credentials were found, save them
for service in self.atv.services:
if service.credentials:
self.credentials[service.protocol.value] = service.credentials
async def async_step_confirm(self, user_input=None):
"""Handle user-confirmation of discovered node."""
if user_input is not None:
return await self.async_begin_pairing()
return self.async_show_form(
step_id="confirm", description_placeholders={"name": self.atv.name}
)
async def async_begin_pairing(self):
"""Start pairing process for the next available protocol."""
self.protocol = self._next_protocol_to_pair()
# Dispose previous pairing sessions
if self.pairing is not None:
await self.pairing.close()
self.pairing = None
# Any more protocols to pair? Else bail out here
if not self.protocol:
await self.async_set_unique_id(self.atv.main_service().identifier)
return self._async_get_entry(
self.atv.main_service().protocol,
self.atv.name,
self.credentials,
self.atv.address,
)
# Initiate the pairing process
abort_reason = None
session = async_get_clientsession(self.hass)
self.pairing = await pair(
self.atv, self.protocol, self.hass.loop, session=session
)
try:
await self.pairing.begin()
except exceptions.ConnectionFailedError:
return await self.async_step_service_problem()
except exceptions.BackOffError:
abort_reason = "backoff"
except exceptions.PairingError:
_LOGGER.exception("Authentication problem")
abort_reason = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
abort_reason = "unknown"
if abort_reason:
if self.pairing:
await self.pairing.close()
return self.async_abort(reason=abort_reason)
# Choose step depending on if PIN is required from user or not
if self.pairing.device_provides_pin:
return await self.async_step_pair_with_pin()
return await self.async_step_pair_no_pin()
async def async_step_pair_with_pin(self, user_input=None):
"""Handle pairing step where a PIN is required from the user."""
errors = {}
if user_input is not None:
try:
self.pairing.pin(user_input[CONF_PIN])
await self.pairing.finish()
self.credentials[self.protocol.value] = self.pairing.service.credentials
return await self.async_begin_pairing()
except exceptions.PairingError:
_LOGGER.exception("Authentication problem")
errors["base"] = "invalid_auth"
except AbortFlow:
raise
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
return self.async_show_form(
step_id="pair_with_pin",
data_schema=INPUT_PIN_SCHEMA,
errors=errors,
description_placeholders={"protocol": protocol_str(self.protocol)},
)
async def async_step_pair_no_pin(self, user_input=None):
"""Handle step where user has to enter a PIN on the device."""
if user_input is not None:
await self.pairing.finish()
if self.pairing.has_paired:
self.credentials[self.protocol.value] = self.pairing.service.credentials
return await self.async_begin_pairing()
await self.pairing.close()
return self.async_abort(reason="device_did_not_pair")
pin = randrange(1000, stop=10000)
self.pairing.pin(pin)
return self.async_show_form(
step_id="pair_no_pin",
description_placeholders={
"protocol": protocol_str(self.protocol),
"pin": pin,
},
)
async def async_step_service_problem(self, user_input=None):
"""Inform user that a service will not be added."""
if user_input is not None:
self.credentials[self.protocol.value] = None
return await self.async_begin_pairing()
return self.async_show_form(
step_id="service_problem",
description_placeholders={"protocol": protocol_str(self.protocol)},
)
def _async_get_entry(self, protocol, name, credentials, address):
if not is_valid_credentials(credentials):
return self.async_abort(reason="invalid_config")
data = {
CONF_PROTOCOL: protocol.value,
CONF_NAME: name,
CONF_CREDENTIALS: credentials,
CONF_ADDRESS: str(address),
}
self._abort_if_unique_id_configured(reload_on_update=False, updates=data)
return self.async_create_entry(title=name, data=data)
def _next_protocol_to_pair(self):
def _needs_pairing(protocol):
if self.atv.get_service(protocol) is None:
return False
return protocol.value not in self.credentials
for protocol in PROTOCOL_PRIORITY:
if _needs_pairing(protocol):
return protocol
return None
def _devices_str(self):
return ", ".join(
[
f"`{atv.name} ({atv.address})`"
for atv in self.scan_result
if atv.identifier not in self._async_current_ids()
]
)
def _prefill_identifier(self):
# Return identifier (address) of one device that has not been paired with
for atv in self.scan_result:
if atv.identifier not in self._async_current_ids():
return str(atv.address)
return ""
class AppleTVOptionsFlow(config_entries.OptionsFlow):
"""Handle Apple TV options."""
def __init__(self, config_entry):
"""Initialize Apple TV options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)
async def async_step_init(self, user_input=None):
"""Manage the Apple TV options."""
if user_input is not None:
self.options[CONF_START_OFF] = user_input[CONF_START_OFF]
return self.async_create_entry(title="", data=self.options)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_START_OFF,
default=self.config_entry.options.get(
CONF_START_OFF, DEFAULT_START_OFF
),
): bool,
}
),
)
class DeviceNotFound(HomeAssistantError):
"""Error to indicate device could not be found."""
class DeviceAlreadyConfigured(HomeAssistantError):
"""Error to indicate device is already configured."""

View File

@@ -0,0 +1,11 @@
"""Constants for the Apple TV integration."""
DOMAIN = "apple_tv"
CONF_IDENTIFIER = "identifier"
CONF_CREDENTIALS = "credentials"
CONF_CREDENTIALS_MRP = "mrp"
CONF_CREDENTIALS_DMAP = "dmap"
CONF_CREDENTIALS_AIRPLAY = "airplay"
CONF_START_OFF = "start_off"

View File

@@ -1,9 +1,17 @@
{
"domain": "apple_tv",
"name": "Apple TV",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
"requirements": ["pyatv==0.3.13"],
"dependencies": ["configurator"],
"requirements": [
"pyatv==0.7.3"
],
"zeroconf": [
"_mediaremotetv._tcp.local.",
"_touch-able._tcp.local."
],
"after_dependencies": ["discovery"],
"codeowners": []
"codeowners": [
"@postlund"
]
}

View File

@@ -1,7 +1,7 @@
"""Support for Apple TV media player."""
import logging
import pyatv.const as atv_const
from pyatv.const import DeviceState, MediaType
from homeassistant.components.media_player import MediaPlayerEntity
from homeassistant.components.media_player.const import (
@@ -19,9 +19,7 @@ from homeassistant.components.media_player.const import (
SUPPORT_TURN_ON,
)
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
EVENT_HOMEASSISTANT_STOP,
STATE_IDLE,
STATE_OFF,
STATE_PAUSED,
@@ -31,10 +29,13 @@ from homeassistant.const import (
from homeassistant.core import callback
import homeassistant.util.dt as dt_util
from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV, DATA_ENTITIES
from . import AppleTVEntity
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
SUPPORT_APPLE_TV = (
SUPPORT_TURN_ON
| SUPPORT_TURN_OFF
@@ -48,108 +49,61 @@ SUPPORT_APPLE_TV = (
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Apple TV platform."""
if not discovery_info:
return
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Load Apple TV media player based on a config entry."""
name = config_entry.data[CONF_NAME]
manager = hass.data[DOMAIN][config_entry.unique_id]
async_add_entities([AppleTvMediaPlayer(name, config_entry.unique_id, manager)])
# Manage entity cache for service handler
if DATA_ENTITIES not in hass.data:
hass.data[DATA_ENTITIES] = []
name = discovery_info[CONF_NAME]
host = discovery_info[CONF_HOST]
atv = hass.data[DATA_APPLE_TV][host][ATTR_ATV]
power = hass.data[DATA_APPLE_TV][host][ATTR_POWER]
entity = AppleTvDevice(atv, name, power)
class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity):
"""Representation of an Apple TV media player."""
def __init__(self, name, identifier, manager, **kwargs):
"""Initialize the Apple TV media player."""
super().__init__(name, identifier, manager, **kwargs)
self._playing = None
@callback
def on_hass_stop(event):
"""Stop push updates when hass stops."""
atv.push_updater.stop()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
if entity not in hass.data[DATA_ENTITIES]:
hass.data[DATA_ENTITIES].append(entity)
async_add_entities([entity])
class AppleTvDevice(MediaPlayerEntity):
"""Representation of an Apple TV device."""
def __init__(self, atv, name, power):
"""Initialize the Apple TV device."""
self.atv = atv
self._name = name
self._playing = None
self._power = power
self._power.listeners.append(self)
def async_device_connected(self, atv):
"""Handle when connection is made to device."""
self.atv.push_updater.listener = self
self.atv.push_updater.start()
async def async_added_to_hass(self):
"""Handle when an entity is about to be added to Home Assistant."""
self._power.init()
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def unique_id(self):
"""Return a unique ID."""
return self.atv.metadata.device_id
@property
def should_poll(self):
"""No polling needed."""
return False
@callback
def async_device_disconnected(self):
"""Handle when connection was lost to device."""
self.atv.push_updater.stop()
self.atv.push_updater.listener = None
@property
def state(self):
"""Return the state of the device."""
if not self._power.turned_on:
if self.manager.is_connecting:
return None
if self.atv is None:
return STATE_OFF
if self._playing:
state = self._playing.play_state
if state in (
atv_const.PLAY_STATE_IDLE,
atv_const.PLAY_STATE_NO_MEDIA,
atv_const.PLAY_STATE_LOADING,
):
state = self._playing.device_state
if state in (DeviceState.Idle, DeviceState.Loading):
return STATE_IDLE
if state == atv_const.PLAY_STATE_PLAYING:
if state == DeviceState.Playing:
return STATE_PLAYING
if state in (
atv_const.PLAY_STATE_PAUSED,
atv_const.PLAY_STATE_FAST_FORWARD,
atv_const.PLAY_STATE_FAST_BACKWARD,
atv_const.PLAY_STATE_STOPPED,
):
# Catch fast forward/backward here so "play" is default action
if state in (DeviceState.Paused, DeviceState.Seeking, DeviceState.Stopped):
return STATE_PAUSED
return STATE_STANDBY # Bad or unknown state?
return None
@callback
def playstatus_update(self, updater, playing):
def playstatus_update(self, _, playing):
"""Print what is currently playing when it changes."""
self._playing = playing
self.async_write_ha_state()
@callback
def playstatus_error(self, updater, exception):
def playstatus_error(self, _, exception):
"""Inform about an error and restart push updates."""
_LOGGER.warning("A %s error occurred: %s", exception.__class__, exception)
# This will wait 10 seconds before restarting push updates. If the
# connection continues to fail, it will flood the log (every 10
# seconds) until it succeeds. A better approach should probably be
# implemented here later.
updater.start(initial_delay=10)
self._playing = None
self.async_write_ha_state()
@@ -157,50 +111,53 @@ class AppleTvDevice(MediaPlayerEntity):
def media_content_type(self):
"""Content type of current playing media."""
if self._playing:
media_type = self._playing.media_type
if media_type == atv_const.MEDIA_TYPE_VIDEO:
return MEDIA_TYPE_VIDEO
if media_type == atv_const.MEDIA_TYPE_MUSIC:
return MEDIA_TYPE_MUSIC
if media_type == atv_const.MEDIA_TYPE_TV:
return MEDIA_TYPE_TVSHOW
return {
MediaType.Video: MEDIA_TYPE_VIDEO,
MediaType.Music: MEDIA_TYPE_MUSIC,
MediaType.TV: MEDIA_TYPE_TVSHOW,
}.get(self._playing.media_type)
return None
@property
def media_duration(self):
"""Duration of current playing media in seconds."""
if self._playing:
return self._playing.total_time
return None
@property
def media_position(self):
"""Position of current playing media in seconds."""
if self._playing:
return self._playing.position
return None
@property
def media_position_updated_at(self):
"""Last valid time of media position."""
state = self.state
if state in (STATE_PLAYING, STATE_PAUSED):
if self.state in (STATE_PLAYING, STATE_PAUSED):
return dt_util.utcnow()
return None
async def async_play_media(self, media_type, media_id, **kwargs):
"""Send the play_media command to the media player."""
await self.atv.airplay.play_url(media_id)
await self.atv.stream.play_url(media_id)
@property
def media_image_hash(self):
"""Hash value for media image."""
state = self.state
if self._playing and state not in [STATE_OFF, STATE_IDLE]:
return self._playing.hash
if self._playing and state not in [None, STATE_OFF, STATE_IDLE]:
return self.atv.metadata.artwork_id
return None
async def async_get_media_image(self):
"""Fetch media image of current playing image."""
state = self.state
if self._playing and state not in [STATE_OFF, STATE_IDLE]:
return (await self.atv.metadata.artwork()), "image/png"
artwork = await self.atv.metadata.artwork()
if artwork:
return artwork.bytes, artwork.mimetype
return None, None
@@ -208,12 +165,8 @@ class AppleTvDevice(MediaPlayerEntity):
def media_title(self):
"""Title of current playing media."""
if self._playing:
if self.state == STATE_IDLE:
return "Nothing playing"
title = self._playing.title
return title if title else "No title"
return f"Establishing a connection to {self._name}..."
return self._playing.title
return None
@property
def supported_features(self):
@@ -222,22 +175,22 @@ class AppleTvDevice(MediaPlayerEntity):
async def async_turn_on(self):
"""Turn the media player on."""
self._power.set_power_on(True)
await self.manager.connect()
async def async_turn_off(self):
"""Turn the media player off."""
self._playing = None
self._power.set_power_on(False)
await self.manager.disconnect()
async def async_media_play_pause(self):
"""Pause media on media player."""
if not self._playing:
return
state = self.state
if state == STATE_PAUSED:
await self.atv.remote_control.play()
elif state == STATE_PLAYING:
await self.atv.remote_control.pause()
if self._playing:
state = self.state
if state == STATE_PAUSED:
await self.atv.remote_control.play()
elif state == STATE_PLAYING:
await self.atv.remote_control.pause()
return None
async def async_media_play(self):
"""Play media."""

View File

@@ -1,46 +1,32 @@
"""Remote control support for Apple TV."""
from homeassistant.components import remote
from homeassistant.const import CONF_HOST, CONF_NAME
from . import ATTR_ATV, ATTR_POWER, DATA_APPLE_TV
import logging
from homeassistant.components.remote import RemoteEntity
from homeassistant.const import CONF_NAME
from . import AppleTVEntity
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Apple TV remote platform."""
if not discovery_info:
return
name = discovery_info[CONF_NAME]
host = discovery_info[CONF_HOST]
atv = hass.data[DATA_APPLE_TV][host][ATTR_ATV]
power = hass.data[DATA_APPLE_TV][host][ATTR_POWER]
async_add_entities([AppleTVRemote(atv, power, name)])
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Load Apple TV remote based on a config entry."""
name = config_entry.data[CONF_NAME]
manager = hass.data[DOMAIN][config_entry.unique_id]
async_add_entities([AppleTVRemote(name, config_entry.unique_id, manager)])
class AppleTVRemote(remote.RemoteEntity):
class AppleTVRemote(AppleTVEntity, RemoteEntity):
"""Device that sends commands to an Apple TV."""
def __init__(self, atv, power, name):
"""Initialize device."""
self._atv = atv
self._name = name
self._power = power
self._power.listeners.append(self)
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def unique_id(self):
"""Return a unique ID."""
return self._atv.metadata.device_id
@property
def is_on(self):
"""Return true if device is on."""
return self._power.turned_on
return self.atv is not None
@property
def should_poll(self):
@@ -48,23 +34,21 @@ class AppleTVRemote(remote.RemoteEntity):
return False
async def async_turn_on(self, **kwargs):
"""Turn the device on.
This method is a coroutine.
"""
self._power.set_power_on(True)
"""Turn the device on."""
await self.manager.connect()
async def async_turn_off(self, **kwargs):
"""Turn the device off.
This method is a coroutine.
"""
self._power.set_power_on(False)
"""Turn the device off."""
await self.manager.disconnect()
async def async_send_command(self, command, **kwargs):
"""Send a command to one device."""
if not self.is_on:
_LOGGER.error("Unable to send commands, not connected to %s", self._name)
return
for single_command in command:
if not hasattr(self._atv.remote_control, single_command):
if not hasattr(self.atv.remote_control, single_command):
continue
await getattr(self._atv.remote_control, single_command)()
await getattr(self.atv.remote_control, single_command)()

View File

@@ -1,8 +0,0 @@
apple_tv_authenticate:
description: Start AirPlay device authentication.
fields:
entity_id:
description: Name(s) of entities to authenticate with.
example: media_player.apple_tv
apple_tv_scan:
description: Scan for Apple TV devices.

View File

@@ -0,0 +1,64 @@
{
"title": "Apple TV",
"config": {
"flow_title": "Apple TV: {name}",
"step": {
"user": {
"title": "Setup a new Apple TV",
"description": "Start by entering the device name (e.g. Kitchen or Bedroom) or IP address of the Apple TV you want to add. If any devices were automatically found on your network, they are shown below.\n\nIf you cannot see your device or experience any issues, try specifying the device IP address.\n\n{devices}",
"data": {
"device_input": "Device"
}
},
"reconfigure": {
"title": "Device reconfiguration",
"description": "This Apple TV is experiencing some connection difficulties and must be reconfigured."
},
"pair_with_pin": {
"title": "Pairing",
"description": "Pairing is required for the `{protocol}` protocol. Please enter the PIN code displayed on screen. Leading zeros shall be omitted, i.e. enter 123 if the displayed code is 0123.",
"data": {
"pin": "[%key:common::config_flow::data::pin%]"
}
},
"pair_no_pin": {
"title": "Pairing",
"description": "Pairing is required for the `{protocol}` service. Please enter PIN {pin} on your Apple TV to continue."
},
"service_problem": {
"title": "Failed to add service",
"description": "A problem occurred while pairing protocol `{protocol}`. It will be ignored."
},
"confirm": {
"title": "Confirm adding Apple TV",
"description": "You are about to add the Apple TV named `{name}` to Home Assistant.\n\n**To complete the process, you may have to enter multiple PIN codes.**\n\nPlease note that you will *not* be able to power off your Apple TV with this integration. Only the media player in Home Assistant will turn off!"
}
},
"error": {
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"no_usable_service": "A device was found but could not identify any way to establish a connection to it. If you keep seeing this message, try specifying its IP address or restarting your Apple TV.",
"unknown": "[%key:common::config_flow::error::unknown%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
},
"abort": {
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"already_configured_device": "[%key:common::config_flow::abort::already_configured_device%]",
"device_did_not_pair": "No attempt to finish pairing process was made from the device.",
"backoff": "Device does not accept pairing reqests at this time (you might have entered an invalid PIN code too many times), try again later.",
"invalid_config": "The configuration for this device is incomplete. Please try adding it again.",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
},
"options": {
"step": {
"init": {
"description": "Configure general device settings",
"data": {
"start_off": "Do not turn device on when starting Home Assistant"
}
}
}
}
}

View File

@@ -0,0 +1,64 @@
{
"config": {
"abort": {
"already_configured_device": "Device is already configured",
"already_in_progress": "Configuration flow is already in progress",
"backoff": "Device does not accept pairing reqests at this time (you might have entered an invalid PIN code too many times), try again later.",
"device_did_not_pair": "No attempt to finish pairing process was made from the device.",
"invalid_config": "The configuration for this device is incomplete. Please try adding it again.",
"no_devices_found": "No devices found on the network",
"unknown": "Unexpected error"
},
"error": {
"already_configured": "Device is already configured",
"invalid_auth": "Invalid authentication",
"no_devices_found": "No devices found on the network",
"no_usable_service": "A device was found but could not identify any way to establish a connection to it. If you keep seeing this message, try specifying its IP address or restarting your Apple TV.",
"unknown": "Unexpected error"
},
"flow_title": "Apple TV: {name}",
"step": {
"confirm": {
"description": "You are about to add the Apple TV named `{name}` to Home Assistant.\n\n**To complete the process, you may have to enter multiple PIN codes.**\n\nPlease note that you will *not* be able to power off your Apple TV with this integration. Only the media player in Home Assistant will turn off!",
"title": "Confirm adding Apple TV"
},
"pair_no_pin": {
"description": "Pairing is required for the `{protocol}` service. Please enter PIN {pin} on your Apple TV to continue.",
"title": "Pairing"
},
"pair_with_pin": {
"data": {
"pin": "PIN Code"
},
"description": "Pairing is required for the `{protocol}` protocol. Please enter the PIN code displayed on screen. Leading zeros shall be omitted, i.e. enter 123 if the displayed code is 0123.",
"title": "Pairing"
},
"reconfigure": {
"description": "This Apple TV is experiencing some connection difficulties and must be reconfigured.",
"title": "Device reconfiguration"
},
"service_problem": {
"description": "A problem occurred while pairing protocol `{protocol}`. It will be ignored.",
"title": "Failed to add service"
},
"user": {
"data": {
"device_input": "Device"
},
"description": "Start by entering the device name (e.g. Kitchen or Bedroom) or IP address of the Apple TV you want to add. If any devices were automatically found on your network, they are shown below.\n\nIf you cannot see your device or experience any issues, try specifying the device IP address.\n\n{devices}",
"title": "Setup a new Apple TV"
}
}
},
"options": {
"step": {
"init": {
"data": {
"start_off": "Do not turn device on when starting Home Assistant"
},
"description": "Configure general device settings"
}
}
},
"title": "Apple TV"
}

View File

@@ -0,0 +1,64 @@
{
"config": {
"abort": {
"already_configured_device": "Seade on juba h\u00e4\u00e4lestatud",
"already_in_progress": "Seadistamine on juba k\u00e4imas",
"backoff": "Seade ei aktsepteeri praegu sidumisn\u00f5udeid (v\u00f5ib-olla oled liiga palju kordi vale PIN-koodi sisestanud), proovi hiljem uuesti.",
"device_did_not_pair": "Seade ei \u00fcritatud sidumisprotsessi l\u00f5pule viia.",
"invalid_config": "Selle seadme s\u00e4tted on puudulikud. Proovi see uuesti lisada.",
"no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet",
"unknown": "Ootamatu t\u00f5rge"
},
"error": {
"already_configured": "Seade on juba h\u00e4\u00e4lestatud",
"invalid_auth": "Vigane autentimine",
"no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet",
"no_usable_service": "Leiti seade kuid ei suudetud tuvastada moodust \u00fchenduse loomiseks. Kui n\u00e4ed seda teadet pidevalt, proovi m\u00e4\u00e4rata seadme IP-aadress v\u00f5i taask\u00e4ivita Apple TV.",
"unknown": "Ootamatu t\u00f5rge"
},
"flow_title": "Apple TV: {name}",
"step": {
"confirm": {
"description": "Oled Home Assistantile lisamas Apple TV-d nimega {name}.\n\n**Protsessi l\u00f5puleviimiseks pead v\u00f5ib-olla sisestama mitu PIN-koodi.**\n\nPane t\u00e4hele, et selle sidumisega * ei saa * v\u00e4lja l\u00fclitada oma Apple TV-d. Ainult Home Assistant-i meediam\u00e4ngija l\u00fclitub v\u00e4lja!",
"title": "Kinnita Apple TV lisamine"
},
"pair_no_pin": {
"description": "Teenuse {protocol} sidumine on vajalik. J\u00e4tkamiseks sisesta oma Apple TV-s PIN-kood {pin} .",
"title": "Sidumine"
},
"pair_with_pin": {
"data": {
"pin": "PIN kood"
},
"description": "Vajalik on protokolli {protocol} sidumine. Sisesta ekraanil kuvatav PIN-kood. Alguse nullid j\u00e4etakse v\u00e4lja, st. sisesta 123, kui kuvatav kood on 0123.",
"title": "Sidumine"
},
"reconfigure": {
"description": "Sellel Apple TV-l on \u00fchendusprobleemid ja see tuleb uuesti seadistada.",
"title": "Seadme \u00fcmberseadistamine"
},
"service_problem": {
"description": "Protokolli {protocol} sidumisel ilmnes probleem. Seda ignoreeritakse.",
"title": "Teenuse lisamine eba\u00f5nnestus."
},
"user": {
"data": {
"device_input": "Seade"
},
"description": "Alustuseks sisesta lisatava Apple TV seadme nimi (nt K\u00f6\u00f6k v\u00f5i Magamistuba) v\u00f5i IP-aadress. Kui m\u00f5ni seade leiti teie v\u00f5rgust automaatselt kuvatakse see allpool. \n\n Kui ei n\u00e4e oma seadet v\u00f5i on probleeme, proovi m\u00e4\u00e4rata seadme IP-aadress. \n\n {devices}",
"title": "Seadista uus Apple TV sidumine"
}
}
},
"options": {
"step": {
"init": {
"data": {
"start_off": "\u00c4ra l\u00fclita seadet Home Assistanti k\u00e4ivitamisel sisse"
},
"description": "Seadme \u00fclds\u00e4tete seadistamine"
}
}
},
"title": ""
}

View File

@@ -0,0 +1,7 @@
{
"config": {
"abort": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
}
}
}

View File

@@ -5,10 +5,6 @@
"already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso",
"cannot_connect": "Impossibile connettersi"
},
"error": {
"one": "uno",
"other": "altri"
},
"flow_title": "Arcam FMJ su {host}",
"step": {
"confirm": {

View File

@@ -14,7 +14,7 @@
"flow_title": "Arcam FMJ na {host}",
"step": {
"confirm": {
"description": "Czy chcesz doda\u0107 Arcam FMJ na \"{host}\" do Home Assistant?"
"description": "Czy chcesz doda\u0107 Arcam FMJ na \"{host}\" do Home Assistanta?"
},
"user": {
"data": {

View File

@@ -88,7 +88,7 @@ class ArloCam(Camera):
_LOGGER.error(error_msg)
return
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
stream = CameraMjpeg(self._ffmpeg.binary)
await stream.open_camera(video.video_url, extra_cmd=self._ffmpeg_arguments)
try:

View File

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

View File

@@ -1,184 +1,163 @@
"""Asuswrt status sensors."""
from datetime import timedelta
import enum
import logging
from typing import Any, Dict, List, Optional
from aioasuswrt.asuswrt import AsusWrt
from homeassistant.const import DATA_GIGABYTES, DATA_RATE_MEGABITS_PER_SECOND
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from . import DATA_ASUSWRT
_LOGGER = logging.getLogger(__name__)
UPLOAD_ICON = "mdi:upload-network"
DOWNLOAD_ICON = "mdi:download-network"
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
@enum.unique
class _SensorTypes(enum.Enum):
DEVICES = "devices"
UPLOAD = "upload"
DOWNLOAD = "download"
DOWNLOAD_SPEED = "download_speed"
UPLOAD_SPEED = "upload_speed"
@property
def unit(self) -> Optional[str]:
"""Return a string with the unit of the sensortype."""
if self in (_SensorTypes.UPLOAD, _SensorTypes.DOWNLOAD):
return DATA_GIGABYTES
if self in (_SensorTypes.UPLOAD_SPEED, _SensorTypes.DOWNLOAD_SPEED):
return DATA_RATE_MEGABITS_PER_SECOND
return None
@property
def icon(self) -> Optional[str]:
"""Return the expected icon for the sensortype."""
if self in (_SensorTypes.UPLOAD, _SensorTypes.UPLOAD_SPEED):
return UPLOAD_ICON
if self in (_SensorTypes.DOWNLOAD, _SensorTypes.DOWNLOAD_SPEED):
return DOWNLOAD_ICON
return None
@property
def sensor_name(self) -> Optional[str]:
"""Return the name of the sensor."""
if self is _SensorTypes.DEVICES:
return "Asuswrt Devices Connected"
if self is _SensorTypes.UPLOAD:
return "Asuswrt Upload"
if self is _SensorTypes.DOWNLOAD:
return "Asuswrt Download"
if self is _SensorTypes.UPLOAD_SPEED:
return "Asuswrt Upload Speed"
if self is _SensorTypes.DOWNLOAD_SPEED:
return "Asuswrt Download Speed"
return None
@property
def is_speed(self) -> bool:
"""Return True if the type is an upload/download speed."""
return self in (_SensorTypes.UPLOAD_SPEED, _SensorTypes.DOWNLOAD_SPEED)
@property
def is_size(self) -> bool:
"""Return True if the type is the total upload/download size."""
return self in (_SensorTypes.UPLOAD, _SensorTypes.DOWNLOAD)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the asuswrt sensors."""
if discovery_info is None:
return
api = hass.data[DATA_ASUSWRT]
api: AsusWrt = hass.data[DATA_ASUSWRT]
devices = []
# Let's discover the valid sensor types.
sensors = [_SensorTypes(x) for x in discovery_info]
if "devices" in discovery_info:
devices.append(AsuswrtDevicesSensor(api))
if "download" in discovery_info:
devices.append(AsuswrtTotalRXSensor(api))
if "upload" in discovery_info:
devices.append(AsuswrtTotalTXSensor(api))
if "download_speed" in discovery_info:
devices.append(AsuswrtRXSensor(api))
if "upload_speed" in discovery_info:
devices.append(AsuswrtTXSensor(api))
data_handler = AsuswrtDataHandler(sensors, api)
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name="sensor",
update_method=data_handler.update_data,
# Polling interval. Will only be polled if there are subscribers.
update_interval=timedelta(seconds=30),
)
add_entities(devices)
await coordinator.async_refresh()
async_add_entities([AsuswrtSensor(coordinator, x) for x in sensors])
class AsuswrtSensor(Entity):
"""Representation of a asuswrt sensor."""
class AsuswrtDataHandler:
"""Class handling the API updates."""
_name = "generic"
def __init__(self, api: AsusWrt):
"""Initialize the sensor."""
def __init__(self, sensors: List[_SensorTypes], api: AsusWrt):
"""Initialize the handler class."""
self._api = api
self._state = None
self._devices = None
self._rates = None
self._speed = None
self._connect_error = False
self._sensors = sensors
self._connected = True
@property
def name(self):
"""Return the name of the sensor."""
return self._name
async def update_data(self) -> Dict[_SensorTypes, Any]:
"""Fetch the relevant data from the router."""
ret_dict: Dict[_SensorTypes, Any] = {}
try:
if _SensorTypes.DEVICES in self._sensors:
# Let's check the nr of devices.
devices = await self._api.async_get_connected_devices()
ret_dict[_SensorTypes.DEVICES] = len(devices)
if any(x.is_speed for x in self._sensors):
# Let's check the upload and download speed
speed = await self._api.async_get_current_transfer_rates()
ret_dict[_SensorTypes.DOWNLOAD_SPEED] = round(speed[0] / 125000, 2)
ret_dict[_SensorTypes.UPLOAD_SPEED] = round(speed[1] / 125000, 2)
if any(x.is_size for x in self._sensors):
rates = await self._api.async_get_bytes_total()
ret_dict[_SensorTypes.DOWNLOAD] = round(rates[0] / 1000000000, 1)
ret_dict[_SensorTypes.UPLOAD] = round(rates[1] / 1000000000, 1)
if not self._connected:
# Log a successful reconnect
self._connected = True
_LOGGER.warning("Successfully reconnected to ASUS router")
except OSError as err:
if self._connected:
# Log the first time connection was lost
_LOGGER.warning("Lost connection to router error due to: '%s'", err)
self._connected = False
return ret_dict
class AsuswrtSensor(CoordinatorEntity):
"""The asuswrt specific sensor class."""
def __init__(self, coordinator: DataUpdateCoordinator, sensor_type: _SensorTypes):
"""Initialize the sensor class."""
super().__init__(coordinator)
self._type = sensor_type
@property
def state(self):
"""Return the state of the sensor."""
return self._state
async def async_update(self):
"""Fetch status from asuswrt."""
try:
self._devices = await self._api.async_get_connected_devices()
self._rates = await self._api.async_get_bytes_total()
self._speed = await self._api.async_get_current_transfer_rates()
if self._connect_error:
self._connect_error = False
_LOGGER.info("Reconnected to ASUS router for %s update", self.entity_id)
except OSError as err:
if not self._connect_error:
self._connect_error = True
_LOGGER.error(
"Error connecting to ASUS router for %s update: %s",
self.entity_id,
err,
)
class AsuswrtDevicesSensor(AsuswrtSensor):
"""Representation of a asuswrt download speed sensor."""
_name = "Asuswrt Devices Connected"
async def async_update(self):
"""Fetch new state data for the sensor."""
await super().async_update()
if self._devices:
self._state = len(self._devices)
class AsuswrtRXSensor(AsuswrtSensor):
"""Representation of a asuswrt download speed sensor."""
_name = "Asuswrt Download Speed"
_unit = DATA_RATE_MEGABITS_PER_SECOND
return self.coordinator.data.get(self._type)
@property
def icon(self):
"""Return the icon."""
return DOWNLOAD_ICON
def name(self) -> str:
"""Return the name of the sensor."""
return self._type.sensor_name
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._unit
async def async_update(self):
"""Fetch new state data for the sensor."""
await super().async_update()
if self._speed:
self._state = round(self._speed[0] / 125000, 2)
class AsuswrtTXSensor(AsuswrtSensor):
"""Representation of a asuswrt upload speed sensor."""
_name = "Asuswrt Upload Speed"
_unit = DATA_RATE_MEGABITS_PER_SECOND
@property
def icon(self):
"""Return the icon."""
return UPLOAD_ICON
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._unit
async def async_update(self):
"""Fetch new state data for the sensor."""
await super().async_update()
if self._speed:
self._state = round(self._speed[1] / 125000, 2)
class AsuswrtTotalRXSensor(AsuswrtSensor):
"""Representation of a asuswrt total download sensor."""
_name = "Asuswrt Download"
_unit = DATA_GIGABYTES
@property
def icon(self):
"""Return the icon."""
return DOWNLOAD_ICON
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._unit
async def async_update(self):
"""Fetch new state data for the sensor."""
await super().async_update()
if self._rates:
self._state = round(self._rates[0] / 1000000000, 1)
class AsuswrtTotalTXSensor(AsuswrtSensor):
"""Representation of a asuswrt total upload sensor."""
_name = "Asuswrt Upload"
_unit = DATA_GIGABYTES
@property
def icon(self):
"""Return the icon."""
return UPLOAD_ICON
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._unit
async def async_update(self):
"""Fetch new state data for the sensor."""
await super().async_update()
if self._rates:
self._state = round(self._rates[1] / 1000000000, 1)
def icon(self) -> Optional[str]:
"""Return the icon to use in the frontend."""
return self._type.icon

View File

@@ -1,5 +1,8 @@
{
"config": {
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {

View File

@@ -0,0 +1,7 @@
{
"config": {
"error": {
"cannot_connect": "\u10db\u10d0\u10e0\u10ea\u10ee\u10d8 \u10e8\u10d4\u10d4\u10e0\u10d7\u10d4\u10d1\u10d8\u10e1\u10d0\u10e1"
}
}
}

View File

@@ -2,7 +2,7 @@
"config": {
"abort": {
"already_configured": "Konto on juba seadistatud",
"reauth_successful": "Taasautentimine \u00f5nnestus"
"reauth_successful": "Taastuvastamine \u00f5nnestus"
},
"error": {
"cannot_connect": "\u00dchendamine nurjus",

View File

@@ -1 +1,130 @@
"""The aurora component."""
import asyncio
from datetime import timedelta
import logging
from auroranoaa import AuroraForecast
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
AURORA_API,
CONF_THRESHOLD,
COORDINATOR,
DEFAULT_POLLING_INTERVAL,
DEFAULT_THRESHOLD,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["binary_sensor"]
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Aurora component."""
hass.data.setdefault(DOMAIN, {})
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Aurora from a config entry."""
conf = entry.data
options = entry.options
session = aiohttp_client.async_get_clientsession(hass)
api = AuroraForecast(session)
longitude = conf[CONF_LONGITUDE]
latitude = conf[CONF_LATITUDE]
polling_interval = DEFAULT_POLLING_INTERVAL
threshold = options.get(CONF_THRESHOLD, DEFAULT_THRESHOLD)
name = conf[CONF_NAME]
coordinator = AuroraDataUpdateCoordinator(
hass=hass,
name=name,
polling_interval=polling_interval,
api=api,
latitude=latitude,
longitude=longitude,
threshold=threshold,
)
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
hass.data[DOMAIN][entry.entry_id] = {
COORDINATOR: coordinator,
AURORA_API: api,
}
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 AuroraDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the NOAA Aurora API."""
def __init__(
self,
hass: HomeAssistant,
name: str,
polling_interval: int,
api: str,
latitude: float,
longitude: float,
threshold: float,
):
"""Initialize the data updater."""
super().__init__(
hass=hass,
logger=_LOGGER,
name=name,
update_interval=timedelta(minutes=polling_interval),
)
self.api = api
self.name = name
self.latitude = int(latitude)
self.longitude = int(longitude)
self.threshold = int(threshold)
async def _async_update_data(self):
"""Fetch the data from the NOAA Aurora Forecast."""
try:
return await self.api.get_forecast_data(self.longitude, self.latitude)
except ConnectionError as error:
raise UpdateFailed(f"Error updating from NOAA: {error}") from error

View File

@@ -1,146 +1,75 @@
"""Support for aurora forecast data sensor."""
from datetime import timedelta
import logging
from math import floor
from aiohttp.hdrs import USER_AGENT
import requests
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.const import ATTR_NAME
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
from . import AuroraDataUpdateCoordinator
from .const import (
ATTR_IDENTIFIERS,
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTRIBUTION,
COORDINATOR,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
ATTRIBUTION = "Data provided by the National Oceanic and Atmospheric Administration"
CONF_THRESHOLD = "forecast_threshold"
DEFAULT_DEVICE_CLASS = "visible"
DEFAULT_NAME = "Aurora Visibility"
DEFAULT_THRESHOLD = 75
async def async_setup_entry(hass, entry, async_add_entries):
"""Set up the binary_sensor platform."""
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
name = coordinator.name
HA_USER_AGENT = "Home Assistant Aurora Tracker v.0.1.0"
entity = AuroraSensor(coordinator, name)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
URL = "http://services.swpc.noaa.gov/text/aurora-nowcast-map.txt"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_THRESHOLD, default=DEFAULT_THRESHOLD): cv.positive_int,
}
)
async_add_entries([entity])
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the aurora sensor."""
if None in (hass.config.latitude, hass.config.longitude):
_LOGGER.error("Lat. or long. not set in Home Assistant config")
return False
name = config[CONF_NAME]
threshold = config[CONF_THRESHOLD]
try:
aurora_data = AuroraData(hass.config.latitude, hass.config.longitude, threshold)
aurora_data.update()
except requests.exceptions.HTTPError as error:
_LOGGER.error("Connection to aurora forecast service failed: %s", error)
return False
add_entities([AuroraSensor(aurora_data, name)], True)
class AuroraSensor(BinarySensorEntity):
class AuroraSensor(CoordinatorEntity, BinarySensorEntity):
"""Implementation of an aurora sensor."""
def __init__(self, aurora_data, name):
"""Initialize the sensor."""
self.aurora_data = aurora_data
def __init__(self, coordinator: AuroraDataUpdateCoordinator, name):
"""Define the binary sensor for the Aurora integration."""
super().__init__(coordinator=coordinator)
self._name = name
self.coordinator = coordinator
self._unique_id = f"{self.coordinator.latitude}_{self.coordinator.longitude}"
@property
def unique_id(self):
"""Define the unique id based on the latitude and longitude."""
return self._unique_id
@property
def name(self):
"""Return the name of the sensor."""
return f"{self._name}"
return self._name
@property
def is_on(self):
"""Return true if aurora is visible."""
return self.aurora_data.is_visible if self.aurora_data else False
@property
def device_class(self):
"""Return the class of this device."""
return DEFAULT_DEVICE_CLASS
return self.coordinator.data > self.coordinator.threshold
@property
def device_state_attributes(self):
"""Return the state attributes."""
attrs = {}
return {"attribution": ATTRIBUTION}
if self.aurora_data:
attrs["visibility_level"] = self.aurora_data.visibility_level
attrs["message"] = self.aurora_data.is_visible_text
attrs[ATTR_ATTRIBUTION] = ATTRIBUTION
return attrs
@property
def icon(self):
"""Return the icon for the sensor."""
return "mdi:hazard-lights"
def update(self):
"""Get the latest data from Aurora API and updates the states."""
self.aurora_data.update()
class AuroraData:
"""Get aurora forecast."""
def __init__(self, latitude, longitude, threshold):
"""Initialize the data object."""
self.latitude = latitude
self.longitude = longitude
self.headers = {USER_AGENT: HA_USER_AGENT}
self.threshold = int(threshold)
self.is_visible = None
self.is_visible_text = None
self.visibility_level = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data from the Aurora service."""
try:
self.visibility_level = self.get_aurora_forecast()
if int(self.visibility_level) > self.threshold:
self.is_visible = True
self.is_visible_text = "visible!"
else:
self.is_visible = False
self.is_visible_text = "nothing's out"
except requests.exceptions.HTTPError as error:
_LOGGER.error("Connection to aurora forecast service failed: %s", error)
return False
def get_aurora_forecast(self):
"""Get forecast data and parse for given long/lat."""
raw_data = requests.get(URL, headers=self.headers, timeout=5).text
# We discard comment rows (#)
# We split the raw text by line (\n)
# For each line we trim leading spaces and split by spaces
forecast_table = [
row.strip().split()
for row in raw_data.split("\n")
if not row.startswith("#")
]
# Convert lat and long for data points in table
# Assumes self.latitude belongs to [-90;90[ (South to North)
# Assumes self.longitude belongs to [-180;180[ (West to East)
# No assumptions made regarding the number of rows and columns
converted_latitude = floor((self.latitude + 90) * len(forecast_table) / 180)
converted_longitude = floor(
(self.longitude + 180) * len(forecast_table[converted_latitude]) / 360
)
return forecast_table[converted_latitude][converted_longitude]
@property
def device_info(self):
"""Define the device based on name."""
return {
ATTR_IDENTIFIERS: {(DOMAIN, self._unique_id)},
ATTR_NAME: self.coordinator.name,
ATTR_MANUFACTURER: "NOAA",
ATTR_MODEL: "Aurora Visibility Sensor",
}

View File

@@ -0,0 +1,110 @@
"""Config flow for SpaceX Launches and Starman."""
import logging
from auroranoaa import AuroraForecast
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
from .const import CONF_THRESHOLD, DEFAULT_NAME, DEFAULT_THRESHOLD, DOMAIN
_LOGGER = logging.getLogger(__name__)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for NOAA Aurora Integration."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
name = user_input[CONF_NAME]
longitude = user_input[CONF_LONGITUDE]
latitude = user_input[CONF_LATITUDE]
session = aiohttp_client.async_get_clientsession(self.hass)
api = AuroraForecast(session=session)
try:
await api.get_forecast_data(longitude, latitude)
except ConnectionError:
errors["base"] = "cannot_connect"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(
f"{DOMAIN}_{user_input[CONF_LONGITUDE]}_{user_input[CONF_LATITUDE]}"
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=f"Aurora - {name}", data=user_input
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_NAME, default=DEFAULT_NAME): str,
vol.Required(
CONF_LONGITUDE,
default=self.hass.config.longitude,
): vol.All(
vol.Coerce(float),
vol.Range(min=-180, max=180),
),
vol.Required(
CONF_LATITUDE,
default=self.hass.config.latitude,
): vol.All(
vol.Coerce(float),
vol.Range(min=-90, max=90),
),
}
),
errors=errors,
)
class OptionsFlowHandler(config_entries.OptionsFlow):
"""Handle options flow changes."""
def __init__(self, config_entry):
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
"""Manage options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
CONF_THRESHOLD,
default=self.config_entry.options.get(
CONF_THRESHOLD, DEFAULT_THRESHOLD
),
): vol.All(
vol.Coerce(int),
vol.Range(min=0, max=100),
),
}
),
)

View File

@@ -0,0 +1,13 @@
"""Constants for the Aurora integration."""
DOMAIN = "aurora"
COORDINATOR = "coordinator"
AURORA_API = "aurora_api"
ATTR_IDENTIFIERS = "identifiers"
ATTR_MANUFACTURER = "manufacturer"
ATTR_MODEL = "model"
DEFAULT_POLLING_INTERVAL = 5
CONF_THRESHOLD = "forecast_threshold"
DEFAULT_THRESHOLD = 75
ATTRIBUTION = "Data provided by the National Oceanic and Atmospheric Administration"
DEFAULT_NAME = "Aurora Visibility"

View File

@@ -2,5 +2,7 @@
"domain": "aurora",
"name": "Aurora",
"documentation": "https://www.home-assistant.io/integrations/aurora",
"codeowners": []
"config_flow": true,
"codeowners": ["@djtimca"],
"requirements": ["auroranoaa==0.0.1"]
}

View File

@@ -0,0 +1,26 @@
{
"title": "NOAA Aurora Sensor",
"config": {
"step": {
"user": {
"data": {
"name": "[%key:common::config_flow::data::name%]",
"longitude": "[%key:common::config_flow::data::longitude%]",
"latitude": "[%key:common::config_flow::data::latitude%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "Threshold (%)"
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "Failed to connect"
},
"step": {
"user": {
"data": {
"latitude": "Latitude",
"longitude": "Longitude",
"name": "Name"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "Threshold (%)"
}
}
}
},
"title": "NOAA Aurora Sensor"
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "Nem siker\u00fclt csatlakozni"
},
"step": {
"user": {
"data": {
"latitude": "Sz\u00e9less\u00e9g",
"longitude": "Hossz\u00fas\u00e1g",
"name": "N\u00e9v"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "K\u00fcsz\u00f6b (%)"
}
}
}
},
"title": "Nemzeti \u00d3ce\u00e1n- \u00e9s L\u00e9gk\u00f6rkutat\u00e1si Hivatal (NOAA) Aurora \u00e9rz\u00e9kel\u0151"
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "Impossibile connettersi"
},
"step": {
"user": {
"data": {
"latitude": "Latitudine",
"longitude": "Logitudine",
"name": "Nome"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "Soglia (%)"
}
}
}
},
"title": "Sensore NOAA Aurora"
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "\u10d9\u10d0\u10d5\u10e8\u10d8\u10e0\u10d8 \u10d5\u10d4\u10e0 \u10d3\u10d0\u10db\u10e7\u10d0\u10e0\u10d3\u10d0"
},
"step": {
"user": {
"data": {
"latitude": "\u10d2\u10d0\u10dc\u10d4\u10d3\u10d8",
"longitude": "\u10d2\u10e0\u10eb\u10d4\u10d3\u10d8",
"name": "\u10e1\u10d0\u10ee\u10d4\u10da\u10d8"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "\u10d6\u10e6\u10d5\u10d0\u10e0\u10d8 (%)"
}
}
}
},
"title": "NOAA Aurora \u10e1\u10d4\u10dc\u10e1\u10dd\u10e0\u10d8"
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "Feeler beim verbannen"
},
"step": {
"user": {
"data": {
"latitude": "L\u00e4ngregraad",
"longitude": "Breedegrad",
"name": "Numm"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "Grenzw\u00e4ert (%)"
}
}
}
},
"title": "NOAA Aurora Sensor"
}

View File

@@ -0,0 +1,13 @@
{
"config": {
"step": {
"user": {
"data": {
"latitude": "Latitude",
"longitude": "Longitude",
"name": "Nome"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"config": {
"step": {
"user": {
"data": {
"name": "Ime"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"config": {
"step": {
"user": {
"data": {
"name": "\u540d\u79f0"
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
{
"config": {
"error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557"
},
"step": {
"user": {
"data": {
"latitude": "\u7def\u5ea6",
"longitude": "\u7d93\u5ea6",
"name": "\u540d\u7a31"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"threshold": "\u95a5\u503c (%)"
}
}
}
},
"title": "NOAA Aurora \u50b3\u611f\u5668"
}

View File

@@ -43,40 +43,37 @@ from homeassistant.helpers.script import (
ATTR_MODE,
CONF_MAX,
CONF_MAX_EXCEEDED,
SCRIPT_MODE_SINGLE,
Script,
make_script_schema,
)
from homeassistant.helpers.script_variables import ScriptVariables
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.helpers.singleton import singleton
from homeassistant.helpers.trigger import async_initialize_triggers
from homeassistant.helpers.typing import TemplateVarsType
from homeassistant.loader import bind_hass
from homeassistant.util.dt import parse_datetime
# Not used except by packages to check config structure
from .config import PLATFORM_SCHEMA # noqa
from .config import async_validate_config_item
from .const import (
CONF_ACTION,
CONF_CONDITION,
CONF_INITIAL_STATE,
CONF_TRIGGER,
DEFAULT_INITIAL_STATE,
DOMAIN,
LOGGER,
)
from .helpers import async_get_blueprints
# mypy: allow-untyped-calls, allow-untyped-defs
# mypy: no-check-untyped-defs, no-warn-return-any
DOMAIN = "automation"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
DATA_BLUEPRINTS = "automation_blueprints"
CONF_DESCRIPTION = "description"
CONF_HIDE_ENTITY = "hide_entity"
CONF_CONDITION = "condition"
CONF_ACTION = "action"
CONF_TRIGGER = "trigger"
CONF_CONDITION_TYPE = "condition_type"
CONF_INITIAL_STATE = "initial_state"
CONF_SKIP_CONDITION = "skip_condition"
CONF_STOP_ACTIONS = "stop_actions"
CONF_BLUEPRINT = "blueprint"
CONF_INPUT = "input"
DEFAULT_INITIAL_STATE = True
DEFAULT_STOP_ACTIONS = True
EVENT_AUTOMATION_RELOADED = "automation_reloaded"
@@ -87,38 +84,8 @@ ATTR_SOURCE = "source"
ATTR_VARIABLES = "variables"
SERVICE_TRIGGER = "trigger"
_LOGGER = logging.getLogger(__name__)
AutomationActionType = Callable[[HomeAssistant, TemplateVarsType], Awaitable[None]]
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.All(
cv.deprecated(CONF_HIDE_ENTITY, invalidation_version="0.110"),
make_script_schema(
{
# str on purpose
CONF_ID: str,
CONF_ALIAS: cv.string,
vol.Optional(CONF_DESCRIPTION): cv.string,
vol.Optional(CONF_INITIAL_STATE): cv.boolean,
vol.Optional(CONF_HIDE_ENTITY): cv.boolean,
vol.Required(CONF_TRIGGER): cv.TRIGGER_SCHEMA,
vol.Optional(CONF_CONDITION): _CONDITION_SCHEMA,
vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
},
SCRIPT_MODE_SINGLE,
),
)
@singleton(DATA_BLUEPRINTS)
@callback
def async_get_blueprints(hass: HomeAssistant) -> blueprint.DomainBlueprints: # type: ignore
"""Get automation blueprints."""
return blueprint.DomainBlueprints(hass, DOMAIN, _LOGGER) # type: ignore
@bind_hass
def is_on(hass, entity_id):
@@ -194,9 +161,13 @@ def devices_in_automation(hass: HomeAssistant, entity_id: str) -> List[str]:
async def async_setup(hass, config):
"""Set up the automation."""
hass.data[DOMAIN] = component = EntityComponent(_LOGGER, DOMAIN, hass)
hass.data[DOMAIN] = component = EntityComponent(LOGGER, DOMAIN, hass)
await _async_process_config(hass, config, component)
# To register the automation blueprints
async_get_blueprints(hass)
if not await _async_process_config(hass, config, component):
await async_get_blueprints(hass).async_populate()
async def trigger_service_handler(entity, service_call):
"""Handle automation triggers."""
@@ -263,7 +234,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
self._is_enabled = False
self._referenced_entities: Optional[Set[str]] = None
self._referenced_devices: Optional[Set[str]] = None
self._logger = _LOGGER
self._logger = LOGGER
self._variables: ScriptVariables = variables
@property
@@ -517,12 +488,13 @@ async def _async_process_config(
hass: HomeAssistant,
config: Dict[str, Any],
component: EntityComponent,
) -> None:
) -> bool:
"""Process config and add automations.
This method is a coroutine.
Returns if blueprints were used.
"""
entities = []
blueprints_used = False
for config_key in extract_domain_configs(config, DOMAIN):
conf: List[Union[Dict[str, Any], blueprint.BlueprintInputs]] = config[ # type: ignore
@@ -531,15 +503,18 @@ async def _async_process_config(
for list_no, config_block in enumerate(conf):
if isinstance(config_block, blueprint.BlueprintInputs): # type: ignore
blueprints_used = True
blueprint_inputs = config_block
try:
config_block = cast(
Dict[str, Any],
PLATFORM_SCHEMA(blueprint_inputs.async_substitute()),
await async_validate_config_item(
hass, blueprint_inputs.async_substitute()
),
)
except vol.Invalid as err:
_LOGGER.error(
LOGGER.error(
"Blueprint %s generated invalid automation with inputs %s: %s",
blueprint_inputs.blueprint.name,
blueprint_inputs.inputs,
@@ -561,7 +536,7 @@ async def _async_process_config(
script_mode=config_block[CONF_MODE],
max_runs=config_block[CONF_MAX],
max_exceeded=config_block[CONF_MAX_EXCEEDED],
logger=_LOGGER,
logger=LOGGER,
# We don't pass variables here
# Automation will already render them to use them in the condition
# and so will pass them on to the script.
@@ -590,6 +565,8 @@ async def _async_process_config(
if entities:
await component.async_add_entities(entities)
return blueprints_used
async def _async_process_if(hass, config, p_config):
"""Process if checks."""
@@ -600,7 +577,7 @@ async def _async_process_if(hass, config, p_config):
try:
checks.append(await condition.async_from_config(hass, if_config, False))
except HomeAssistantError as ex:
_LOGGER.warning("Invalid condition: %s", ex)
LOGGER.warning("Invalid condition: %s", ex)
return None
def if_action(variables=None):

View File

@@ -0,0 +1,49 @@
blueprint:
name: Motion-activated Light
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
input:
motion_entity:
name: Motion Sensor
selector:
entity:
domain: binary_sensor
device_class: motion
light_target:
name: Light
selector:
target:
entity:
domain: light
no_motion_wait:
name: Wait time
description: Time to wait until the light should be turned off.
default: 120
selector:
number:
min: 0
max: 3600
unit_of_measurement: seconds
# If motion is detected within the delay,
# we restart the script.
mode: restart
max_exceeded: silent
trigger:
platform: state
entity_id: !input motion_entity
from: "off"
to: "on"
action:
- service: light.turn_on
target: !input light_target
- wait_for_trigger:
platform: state
entity_id: !input motion_entity
from: "on"
to: "off"
- delay: !input no_motion_wait
- service: light.turn_off
target: !input light_target

View File

@@ -0,0 +1,41 @@
blueprint:
name: Send notification when a person leaves a zone
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml
input:
person_entity:
name: Person
selector:
entity:
domain: person
zone_entity:
name: Zone
selector:
entity:
domain: zone
notify_device:
name: Device to notify
description: Device needs to run the official Home Assistant app to receive notifications.
selector:
device:
integration: mobile_app
trigger:
platform: state
entity_id: !input person_entity
variables:
zone_entity: !input zone_entity
zone_state: "{{ states[zone_entity].name }}"
person_entity: !input person_entity
person_name: "{{ states[person_entity].name }}"
condition:
condition: template
value_template: "{{ trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}"
action:
domain: mobile_app
type: notify
device_id: !input notify_device
message: "{{ person_name }} has left {{ zone_state }}"

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