Compare commits

...

198 Commits

Author SHA1 Message Date
Paulus Schoutsen e0873493e2 Merge pull request #54676 from home-assistant/rc 2021-08-15 20:49:10 -07:00
Paulus Schoutsen 700f149ef8 Bumped version to 2021.8.7 2021-08-15 20:06:46 -07:00
Jesse Hills 848885c658 Send color_brightness to ESPHome devices on 1.20 (pre-color_mode) (#54670) 2021-08-15 20:05:59 -07:00
jan iversen 5af94c42db Solve switch/verify register type convert problem in modbus (#54645) 2021-08-15 20:05:59 -07:00
Paulus Schoutsen dd1ef7fa55 Guard partial upgrade (#54617) 2021-08-15 20:05:58 -07:00
Michael 8d1bd55b68 Bump py-synologydsm-api to 1.0.4 (#54610) 2021-08-15 20:05:57 -07:00
J. Nick Koston 3c3a6e6cb4 Bump zeroconf to 0.35.0 (#54604)
Fixes https://github.com/home-assistant/core/issues/54531
Fixes https://github.com/home-assistant/core/issues/54434
Fixes https://github.com/home-assistant/core/issues/54487
2021-08-15 20:05:56 -07:00
Oxan van Leeuwen 2f2038c147 Clamp color temperature to supported range in ESPHome light (#54595)
ESPHome devices initially report a color temperature of 0 or 1 until it
has been changed by the user. This broke the conversion from RGBWW to
an RGB color.

Fixes #54293.
2021-08-15 20:05:56 -07:00
Daniel Hjelseth Høyer 5a5bbef1b8 Adax, update requirements (#54587)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-08-15 20:05:55 -07:00
Daniel Hjelseth Høyer 0f60a5a8c0 Fix Tibber last reset (#54582)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-08-15 20:05:54 -07:00
Daniel Hjelseth Høyer f43151081b Fix bug in ambiclimate (#54579)
* Fix ambiclimate

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Fix ambiclimate

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-08-15 20:05:53 -07:00
Colin O'Dell ab82369320 Upgrade qnapstats library to 0.4.0 (#54571) 2021-08-15 20:05:53 -07:00
Gerard 1c503244c6 Fix attributes not showing after using entity class attributes (#54558) 2021-08-15 20:05:52 -07:00
Alan Tse 482e00a071 Treat temporary errors as warnings for Tesla (#54515)
* Treat temporary errors as warnings for Tesla
closes #53391

* Apply suggestions from code review

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

* Black

Co-authored-by: J. Nick Koston <nick@koston.org>
2021-08-15 20:05:51 -07:00
Daniel Hjelseth Høyer 6f3879fc92 Add missing PRESSURE_BAR conversion (#54497)
* Add missing PRESSURE_BAR

* style

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* valid units

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-08-15 20:05:50 -07:00
ehendrix23 6382061b57 Updates to bump MyQ to 3.1.2 (#54488) 2021-08-15 20:05:50 -07:00
Robert Hillis 1d45a80a92 Bump notifications-android-tv to 0.1.3 (#54462)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-08-15 20:05:49 -07:00
Felix 4d01dd3c0c Strip attributes whitespace in universal media_player (#54451)
When using whitespace in attributes the lookup of the state attribute fails because an entity with whitespace in its name cannot be found.

Works:
entity_id|state_attribute

Does not work:
entity_id | state_attribute

Fixes #53804
2021-08-15 20:05:48 -07:00
Ville Skyttä 72e548de5f Fix Huawei LTE entity state updating (#54447)
Since 91a2b96, we no longer key this by the router URL, but the relevant
config entry unique id.

Closes https://github.com/home-assistant/core/issues/54243
2021-08-15 20:05:47 -07:00
Phil Cole 7d67caba44 Use pycarwings2 2.11 (#54424) 2021-08-15 20:05:47 -07:00
J. Nick Koston 5fc5b53c01 Ensure HomeKit passes min/max mireds as ints - adapted from #54372 2021-08-15 20:05:29 -07:00
Paulus Schoutsen 745afc8fe2 Merge pull request #54443 from home-assistant/rc 2021-08-10 21:05:42 -07:00
Paulus Schoutsen c6fc5e35fe Bumped version to 2021.8.6 2021-08-10 20:29:32 -07:00
Aaron Bach b2b0cc9a22 Bump pyopenuv to 2.1.0 (#54436) 2021-08-10 20:29:22 -07:00
Paulus Schoutsen ebab5c5d92 Bump hass_nabucasa to 0.46.0 (#54421) 2021-08-10 20:29:21 -07:00
Daniel Hjelseth Høyer 56d51404b7 Re-add Tibber notify service name (#54401)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2021-08-10 20:29:21 -07:00
Joakim Sørensen bb3769c84f Fix Canary sensor state (#54380) 2021-08-10 20:29:20 -07:00
Paulus Schoutsen 3effe94a27 Handle CO2Signal response value being None (#54377) 2021-08-10 20:29:19 -07:00
Paulus Schoutsen a21e3aed77 Merge pull request #54368 from home-assistant/rc 2021-08-09 20:50:56 -07:00
Brett 747eb92a4a Fix race condition in Advantage Air (#53439) 2021-08-09 20:21:38 -07:00
Raman Gupta 5ac5b41a11 Update Climacell rate limit (#54373) 2021-08-09 20:16:36 -07:00
Paulus Schoutsen 0b532c139c Bumped version to 2021.8.5 2021-08-09 17:14:06 -07:00
Paulus Schoutsen d4290d1e03 Revert "Use entity class attributes for Bluesound (#53033)" (#54365) 2021-08-09 17:12:52 -07:00
Paulus Schoutsen b4d466f87c Do not process forwarded for headers for cloud requests (#54364) 2021-08-09 17:12:51 -07:00
Aaron Bach 4f3cf5a61c Cast SimpliSafe version number as a string in device info (#54356) 2021-08-09 17:12:51 -07:00
dailow cfa6040d55 Fix aqualogic state attribute update (#54354) 2021-08-09 17:12:50 -07:00
Jan Bouwhuis 3fcbde3b9c Fix Xiaomi-miio turn fan on with speed, percentage or preset (#54353) 2021-08-09 17:12:49 -07:00
Bram Kragten 84f6586058 Update frontend to 20210809.0 (#54350) 2021-08-09 17:12:48 -07:00
Jan Bouwhuis cb14acd606 Fix xiaomi air fresh fan preset modes (#54342) 2021-08-09 17:12:48 -07:00
Diogo Gomes 59c882a0f5 Restores unit_of_measurement (#54335) 2021-08-09 17:12:47 -07:00
Chris b21f319b0a Remove zwave_js transition on individual color channels (#54303) 2021-08-09 17:12:46 -07:00
J. Nick Koston 2a1d2b77a1 Ensure hunterdouglas_powerview model type is a string (#54299) 2021-08-09 17:12:45 -07:00
J. Nick Koston 9caad5b2c7 Bump zeroconf to 0.34.3 (#54294) 2021-08-09 17:12:45 -07:00
Marc Mueller 5402173e98 Fix ondilo_ico name attribute (#54290) 2021-08-09 17:12:44 -07:00
jjlawren 985dab6bdf Bump soco to 0.23.3 (#54288) 2021-08-09 17:12:43 -07:00
Robert Hillis cf92d45f07 Use correct state attribute for alarmdecoder binary sensor (#54286)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-08-09 17:12:42 -07:00
ZeGuigui 955a72080f Fix atom integration for long term statistics (#54285)
* Fix atom integration for long term statistics

* Remove commented code

* Fix last_reset syntax

* last_reset not an extra attribute

* last_reset as utc

* black formatting

* isort fix
2021-08-09 17:12:42 -07:00
J. Nick Koston 3edd505468 Always set interfaces explicitly when IPv6 is present (#54268) 2021-08-09 17:12:41 -07:00
rikroe 746bb2997e Fix login to BMW services for rest_of_world and north_america (#54261)
Co-authored-by: rikroe <rikroe@users.noreply.github.com>
2021-08-09 17:12:40 -07:00
Reuben Gow 1809b7a98b Force an attempted subscribe on speaker reboot (#54100)
* Force an attempted subscribe on speaker reboot

* Recreate subscriptions and timers explicitly on speaker reboot

* only create poll timer if there is not one already

Co-authored-by: jjlawren <jjlawren@users.noreply.github.com>

* Black

Co-authored-by: jjlawren <jjlawren@users.noreply.github.com>
2021-08-09 17:12:39 -07:00
Robert Hillis 903e2243e7 Fix camera state and attributes for agent_dvr (#54049)
* Fix camera state and attributes for agent_dvr

* tweak

* tweak
2021-08-09 17:12:39 -07:00
Paulus Schoutsen f3de8b9f28 Merge pull request #54249 from home-assistant/rc 2021-08-07 23:19:11 -07:00
Paulus Schoutsen fe9808926e Fix formatting (#54247) 2021-08-07 22:04:03 -07:00
Paulus Schoutsen f458f330a5 Bumped version to 2021.8.4 2021-08-07 21:34:56 -07:00
Allen Porter fb6aca4f8b Pin google-cloud-pubsub to an older version (#54239)
Pin google-cloud-pubsub to an older version, since newer versions have a
pin that is incompatible with the existing grpcio pin already in package_constraints.txt
2021-08-07 21:34:52 -07:00
Dermot Duffy 724f11bb0d Don't block motionEye setup on NoURLAvailableError (#54225)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-08-07 21:34:52 -07:00
carstenschroeder 13ded1e5b2 Bugfix: Bring back unique IDs for ADS covers after #52488 (#54212) 2021-08-07 21:34:51 -07:00
jan iversen 5dcf5edae0 Add parameter to delay sending of requests in modbus (#54203) 2021-08-07 21:34:50 -07:00
Mk4242 56d0ef34fd Update const.py (#54195)
Remove extra attribute for FlowTemperature sensor, which prevents the ebusd integration from initialising
2021-08-07 21:34:50 -07:00
jan iversen 94e26df6d3 Solve missing automatic update of struct configuration in modbus (#54193) 2021-08-07 21:34:49 -07:00
Robert Hillis 3a17e22982 Fix androidtv media_image_hash (#54188) 2021-08-07 21:34:48 -07:00
Trinnik 1934159fd0 Fix update entity prior to adding (#54015) 2021-08-07 21:34:48 -07:00
Maciej Bieniek 4fbe713079 Add missing motor_speed sensor for Xiaomi Miio humidifier CA1 and CB1 (#54202) 2021-08-07 20:55:01 -07:00
Paulus Schoutsen 9155274c6e Merge pull request #54175 from home-assistant/rc 2021-08-06 13:27:37 -07:00
Paulus Schoutsen cb775029e0 Bumped version to 2021.8.3 2021-08-06 12:55:59 -07:00
J. Nick Koston 011cda5fc4 Bump zeroconf to 0.33.4 to ensure zeroconf can startup when ipv6 is disabled (#54165)
Changelog: https://github.com/jstasiak/python-zeroconf/compare/0.33.3...0.33.4
2021-08-06 12:55:56 -07:00
J. Nick Koston d580036dfb Fetch interface index from network integration instead of socket.if_nametoindex in zeroconf (#54152) 2021-08-06 12:55:55 -07:00
Michael 15e9310a01 Fix sensor PLATFORM_SCHEMA for ebox and enphase_envoy (#54142)
* Fix sensor PLATFORM_SCHEMA

* fix pylint
2021-08-06 12:55:55 -07:00
Niccolo Zapponi 93a2e2849c Handle software version being None when setting up HomeKit accessories (#54130)
* Convert all HomeKit service info to string prior to checking for max length

* Added check for None software version

* Added test case for numeric version number

* Update tests/components/homekit/test_accessories.py

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

* Fix style & none version test

* Fix test

* revert other change since it should be covered by the format_sw_version fix

Co-authored-by: J. Nick Koston <nick@koston.org>
2021-08-06 12:55:39 -07:00
Oscar Calvo 07f8236e6f Gracefully handle additional GSM errors (#54114) 2021-08-06 12:53:53 -07:00
Paulus Schoutsen a07048aacf Merge pull request #54117 from home-assistant/rc 2021-08-05 23:18:48 -07:00
Paulus Schoutsen 890d190612 Fix constant 2021-08-05 23:15:20 -07:00
Paulus Schoutsen b00173705e Bumped version to 2021.8.2 2021-08-05 20:49:03 -07:00
J. Nick Koston 374ccaae47 Bump zeroconf to 0.33.3 (#54108) 2021-08-05 20:48:58 -07:00
J. Nick Koston fa4ec926ce Increase time before scene and script HomeKit entities are reset (#54105) 2021-08-05 20:48:57 -07:00
Simone Chemelli e37bf733bd Two fixes (#54102) 2021-08-05 20:48:56 -07:00
Shay Levy 52f7f203d2 Fix Shelly last_reset (#54101) 2021-08-05 20:48:55 -07:00
Paulus Schoutsen 557e1862d5 Packages to support config platforms (#54085) 2021-08-05 20:48:54 -07:00
puddly fbfb77b900 Bump up ZHA dependencies (#54079) 2021-08-05 20:48:53 -07:00
J. Nick Koston bc548050f1 Handle empty software version when setting up HomeKit (#54068)
Fixes #54059 Fixes #54024
2021-08-05 20:48:43 -07:00
Franck Nijhof 5c9d7edadd Add missing device class to SAJ energy sensors (#54048) 2021-08-05 20:47:38 -07:00
Nathan Spencer 6544a32319 Bump pylitterbot to 2021.8.0 (#54000) 2021-08-05 20:47:37 -07:00
Paulus Schoutsen 5e9081e323 Merge pull request #54012 from home-assistant/rc 2021-08-04 20:22:17 -07:00
Paulus Schoutsen 436d0e0fb4 Bumped version to 2021.8.1 2021-08-04 17:05:41 -07:00
Bram Kragten 72032edaec Update frontend to 20210804.0 (#53997) 2021-08-04 17:05:12 -07:00
jjlawren 14621fc445 Fix empty sonos_group entity attribute on startup (#53985) 2021-08-04 17:05:11 -07:00
Simone Chemelli ef55a8d2e2 Fix divider for Fritz sensors (#53980) 2021-08-04 17:05:10 -07:00
G Johansson 82b2ae8e91 Fix coordinator not defined in yale_smart_alarm (#53973)
* Bugfix coordinator not defined

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-08-04 17:05:09 -07:00
Michael 01299ddd20 Fix attr_unit_of_measurement in update of apcupsd entity (#53947) 2021-08-04 17:05:09 -07:00
jan iversen fcc82d26a4 Add temporary fix to modbus to solve upstream problem (#53857) 2021-08-04 17:05:07 -07:00
Alex Henry 16b5544104 Fix Panasonic Viera TV going unavailable when turned off (#53788) 2021-08-04 17:05:06 -07:00
Franck Nijhof f0f4c13cbe Merge pull request #53930 from home-assistant/rc 2021-08-04 11:21:42 +02:00
Franck Nijhof 4de3f031cc Bumped version to 2021.8.0 2021-08-04 10:46:20 +02:00
Joakim Sørensen 9ec516e1d4 Address review comments for 53918 (#53927) 2021-08-04 10:46:00 +02:00
Paulus Schoutsen 38df475936 Bumped version to 2021.8.0b10 2021-08-03 21:04:22 -07:00
Bram Kragten af81dda1e2 Update frontend to 20210803.2 (#53923) 2021-08-03 21:03:43 -07:00
Martin Hjelmare 4fdd354745 Limit zwave_js meter sensor last reset (#53921) 2021-08-03 21:03:42 -07:00
Joakim Sørensen 0342c0da33 Limit API usage for Uptime Robot (#53918) 2021-08-03 21:03:42 -07:00
Paulus Schoutsen 7a8676dc83 Handle Shelly get name on uninitialized device (#53917) 2021-08-03 21:03:41 -07:00
J. Nick Koston 54ac889362 Enforce maximum length for HomeKit characteristics (#53913) 2021-08-03 21:03:01 -07:00
Aaron Bach 4e2c174741 Handle scenario where SimpliSafe code is falsey (#53912) 2021-08-03 20:59:54 -07:00
Maciej Bieniek e4fd43ed7c Use SelectEntityDescription for Xiaomi Miio integration (#53907)
* Use SelectEntityDescription

* Use SelectEntityDescription

* Remove service field from XiaomiMiioSelectDescription class

* Fix typo

* Use lowercase for options
2021-08-03 20:58:48 -07:00
Greg Dowling 07604e60e5 Bump pyroon to 0.0.38 (#53906) 2021-08-03 20:50:46 -07:00
Jesse Hills 922c0dc8be Bump aioesphomeapi to 6.0.1 (#53905) 2021-08-03 20:50:45 -07:00
Joakim Sørensen 30497eff0e Add user to homeassistant system health (#53902) 2021-08-03 20:50:45 -07:00
Paulus Schoutsen 5536da18a6 Bumped version to 2021.8.0b9 2021-08-02 21:41:32 -07:00
Paulus Schoutsen 120122ffe2 Bump frontend to 20210803.0 (#53897) 2021-08-02 21:41:24 -07:00
Aaron Bach 8b7bdc9b67 Only show a SimpliSafe code entry when one exists (#53894) 2021-08-02 21:41:23 -07:00
Chris d74ca25291 Handle powerConsumption reports with null value (#53888) 2021-08-02 21:41:22 -07:00
Paulus Schoutsen be6cb2e792 Bump aiohue to 2.6.1 (#53887) 2021-08-02 21:41:22 -07:00
Otto Winter f7e448c8b2 ESPHome implement light color modes (#53854) 2021-08-02 21:41:21 -07:00
Paulus Schoutsen 2e1f42937d Bumped version to 2021.8.0b8 2021-08-02 10:53:52 -07:00
Jc2k 92cc51370d Fix watts unit for homekit_controller power sensors (#53877) 2021-08-02 10:53:44 -07:00
Paulus Schoutsen a97e480d82 Bump frontend to 20210802.0 (#53876) 2021-08-02 10:53:44 -07:00
Joakim Sørensen 66711219c7 Fix issue when data is None (#53875) 2021-08-02 10:53:43 -07:00
Franck Nijhof b0f6e8c40a Fix growat server config entry missing URL key (#53867) 2021-08-02 10:53:42 -07:00
Franck Nijhof df20d69fd2 Add measurement state class to ZHA power devices (#53866) 2021-08-02 10:53:42 -07:00
Franck Nijhof 249fb51d2f Fix cloud accountlinking replacing token data (#53865) 2021-08-02 10:53:41 -07:00
Franck Nijhof e35b5dd7c1 Add RPi.GPIO dependency to rpi_rf integration (#53858) 2021-08-02 10:53:40 -07:00
jan iversen bebd495e74 Allow combinations write_coil/read_coils and write_coils/read_coil for modbus switch (#53856) 2021-08-02 10:53:40 -07:00
Joakim Sørensen d56636ed37 Add base energy analytics (#53855) 2021-08-02 10:53:39 -07:00
Michael e9b672c0b4 Fix crash when AVM FRITZ!SmartHome devices are unreachable (#53809) 2021-08-02 10:53:38 -07:00
Daniel Hjelseth Høyer 5f8f1ae695 Add STATE_CLASS_MEASUREMENT to Tibber (#53802) 2021-08-02 10:53:38 -07:00
Michael 2e441d8b7c Fix TP-Link smart strip devices (#53799) 2021-08-02 10:53:37 -07:00
René Klomp 31af17f7f7 Bump pysma to 0.6.5 (#53792) 2021-08-02 10:53:37 -07:00
Felipe Martins Diel 91af3b0502 Fix entry setup for Broadlink SP4 sensors (#53765) 2021-08-02 10:53:36 -07:00
Emilv2 1a1efecdba Fix missing default reconnect interval in dsmr (#53760) 2021-08-02 10:53:35 -07:00
Vinny Furia 1c30967f6f Fix Radiothermostat hold value updates (#53656) 2021-08-02 10:53:34 -07:00
Paulus Schoutsen ab4ed128cc Bumped version to 2021.8.0b7 2021-08-01 21:00:14 -07:00
Jc2k 3d6ba793f7 Fix error in homekit_controller causing some entities to get an incorrect unique id (#53848) 2021-08-01 21:00:09 -07:00
Chris 9cee9d9d8a Add energy consumption sensors to smartthings devices (#53759) 2021-08-01 21:00:08 -07:00
GitHub Action e093e0bf10 [ci skip] Translation update 2021-08-01 20:41:03 -07:00
Paulus Schoutsen 1019bb059d Bumped version to 2021.8.0b6 2021-08-01 15:07:34 -07:00
Bram Kragten ec35b92052 Update frontend to 20210801.0 (#53841) 2021-08-01 15:07:23 -07:00
Robert Hillis cb2103d96c Fix file path error in nfandroidtv (#53814) 2021-08-01 15:07:22 -07:00
Maciej Bieniek 3f7ddb4706 Clean Xiaomi Miio humidifier services (#53806) 2021-08-01 15:07:22 -07:00
B-Hartley fd0ae7ab36 ForecastSolar - power production now w not k w (#53797) 2021-08-01 15:07:21 -07:00
Maciej Bieniek fc5c30775d Remove led from Xiaomi Miio humidifier features (#53796) 2021-08-01 15:07:20 -07:00
Andreas Brett 0948eafb93 Fix onkyo UnboundLocalError (#53793)
audio_information_raw and video_information_raw were in some cases used before being assigned

error: UnboundLocalError: local variable 'video_information_raw' referenced before assignment
2021-08-01 15:07:20 -07:00
Maciej Bieniek 31869cbb12 Fix name migration of the Xiaomi Miio humidifier (#53790) 2021-08-01 15:07:19 -07:00
Michael 2297c0b58b Do not block setup of TP-Link when device unreachable (#53770) 2021-08-01 15:07:18 -07:00
Paulus Schoutsen e0fc14f82c Bumped version to 2021.8.0b5 2021-07-30 14:15:42 -07:00
Paulus Schoutsen cd3390e012 Bump Hue and only fire events for button presses (#53781)
* Bump Hue and only fire events for button presses

* Fix tests
2021-07-30 14:15:32 -07:00
Bram Kragten af96c5d60c Update frontend to 20210730.0 (#53778) 2021-07-30 14:15:31 -07:00
Aaron Bach 9f0f40dac6 Fix parsing of non-string values in Slack data (#53775) 2021-07-30 14:15:31 -07:00
Jan Bouwhuis 958df580a9 Fix Xiaomi-miio humidifier write the state back when turning on or off (#53771) 2021-07-30 14:15:30 -07:00
Maciej Bieniek 0aee659ee9 Fix Xiaomi Miio humidifier mode change (#53757) 2021-07-30 14:15:29 -07:00
J. Nick Koston 423fb910b5 Bump HAP-python to 3.6.0 (#53754) 2021-07-30 14:15:28 -07:00
Joakim Sørensen fc8af9af8e Revert "Rename snapshot -> backup" (#53751)
This reverts commit 9806bda272.
2021-07-30 14:15:27 -07:00
Paulus Schoutsen a75c7d52c9 Cost sensor handle consumption sensor in Wh (#53746) 2021-07-30 14:15:27 -07:00
uvjustin dac968bf32 Fix non monotonic dts error in stream (#53712)
* Use defaultdict for TimestampValidator._last_dts

* Combine filters

* Allow PeekIterator to be updated while preserving buffer

* Fix peek edge case

* Readd is_valid filter to video only iterator
2021-07-30 14:15:26 -07:00
Paulus Schoutsen 447901c223 Bumped version to 2021.8.0b4 2021-07-29 23:35:09 -07:00
Paulus Schoutsen 9dcd3f6626 Add energy attributes to Fronius (#53741)
* Add energy attributes to Fronius

* Add solar

* Add power

* Only add last reset for total meter entities

* Only add last reset for total solar entities

* Create different entity descriptions per key

* only return the entity description for energy total

* Use correct key

* Meter devices keep it real

* keys start with energy_real

* Also device key starts with

* Lint
2021-07-29 23:35:03 -07:00
Jan Bouwhuis d34bd8ad1e Fix Xiaomi-miio switch platform setup (#53739) 2021-07-29 23:35:02 -07:00
Jan Bouwhuis 83e4e4f769 Fix Xiaomi humidifier name migration (#53738) 2021-07-29 23:35:01 -07:00
Michael 128dc07fa5 Apply left suggestions #53596 for TP-Link (#53737) 2021-07-29 23:35:00 -07:00
Brandon Rothweiler d2dfdd81ad Only allow one Mazda vehicle status request at a time (#53736) 2021-07-29 23:35:00 -07:00
Michael 0442827b9e Fix exception handling in DataUpdateCoordinator in TP-Link (#53734)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-07-29 23:34:59 -07:00
Alexei Chetroi 37c3062874 Bump up ZHA dependencies (#53732) 2021-07-29 23:34:58 -07:00
Robert Svensson bfacff5d78 Add energy device class to deCONZ consumption sensors (#53731) 2021-07-29 23:34:57 -07:00
Michael d54621e778 Extract smartthings switch energy attributes into sensors (#53719) 2021-07-29 23:34:57 -07:00
Ryan Johnson 716c3f69ca Bump pyatv to 0.8.2 (#53659) 2021-07-29 23:34:56 -07:00
Paulus Schoutsen 630a1fb36c Bumped version to 2021.8.0b3 2021-07-29 14:27:55 -07:00
Paulus Schoutsen 54eeebfd20 Revert "Allow uploading large snapshots (#53528)" (#53729)
This reverts commit cdce14d63d.
2021-07-29 14:27:47 -07:00
Franck Nijhof a671a0ccac Fix effect selector of light.turn_on service (#53726) 2021-07-29 14:27:46 -07:00
Martin Hjelmare 8cf0182f2f Fix zwave_js current and voltage meter sensor device class (#53723) 2021-07-29 14:27:45 -07:00
Franck Nijhof f1400b03bb Fix DSMR reconnecting loop without timeout (#53722) 2021-07-29 14:27:45 -07:00
Paulus Schoutsen 6dc00d3d87 Bumped version to 2021.8.0b2 2021-07-29 12:35:50 -07:00
Franck Nijhof bf6133534d Fix SolarEdge statistics; missing device_class (#53720) 2021-07-29 12:35:25 -07:00
Paulus Schoutsen b1758e1fcc Bump frontend to 20210729.0 (#53717) 2021-07-29 12:35:24 -07:00
Martin Hjelmare cc0aa32f3e Fix zwave_js meter sensor state class (#53716) 2021-07-29 12:35:23 -07:00
Franck Nijhof dc2494f0a0 Add state class support to DSMR Reader (#53715) 2021-07-29 12:35:23 -07:00
Simone Chemelli 4b2a1ec694 Add last reset to Shelly's energy entities (#53710) 2021-07-29 12:35:22 -07:00
Michael b2187022c4 Set state class measurement also for Total Energy for AVM Fritz!Smarthome devices (#53707) 2021-07-29 12:35:21 -07:00
Martin Hjelmare c6f588fc08 Revert "Add Automate Pulse Hub v2 support (#39501)" (#53704) 2021-07-29 12:35:20 -07:00
Daniel Hjelseth Høyer 462e3a3d0d Integration. Add device class, last_reset, state_class (#53698)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-07-29 12:35:20 -07:00
Daniel Hjelseth Høyer aa179a1ad9 Energy round (#53696)
* Energy. Round cost

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Energy. Round cost

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>

* Update homeassistant/components/energy/sensor.py

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

Co-authored-by: Franck Nijhof <git@frenck.dev>
2021-07-29 12:35:19 -07:00
Daniel Hjelseth Høyer 1117158bd0 Surepetcare, bug fix (#53695) 2021-07-29 12:35:18 -07:00
Andrew55529 6ced0153df Fix problem with telegram_bot (#53690) 2021-07-29 12:35:17 -07:00
Gerard 7f314e17de Bump bimmer_connected to 0.7.16 to fix parking light issue (#53687) 2021-07-29 12:35:17 -07:00
Robert Svensson 9ad29ae75c Only disable a device if all associated config entries are disabled (#53681) 2021-07-29 12:35:16 -07:00
Maciej Bieniek 43a89dc452 Fix last_reset_topic config replaces state_topic for sensor platform (#53677) 2021-07-29 12:35:15 -07:00
Allen Porter 268f0dd62f Bump nest to version 0.3.5 (#53672)
Fix runtime type assertions, Fixes Issue #53652
2021-07-29 12:35:15 -07:00
Eric Severance d7768f13c1 pyWeMo version bump (0.6.6) (#53671) 2021-07-29 12:35:14 -07:00
J. Nick Koston db8aa4658a Skip each ssdp listener that fails to bind (#53670) 2021-07-29 12:35:13 -07:00
Raman Gupta d19d487b21 Add energy support for zwave_js meter CC entities (#53665)
* Add energy support for zwave_js meter CC entities

* shrink

* comments

* comments

* comments

* Move attributes

* Add tests

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2021-07-29 12:35:13 -07:00
Aaron Bach 2aeecba64c Fix unhandled exception with Guardian paired sensor coordinators (#53663) 2021-07-29 12:35:12 -07:00
Diogo Gomes b3367d8b3f Prosegur code quality improvements (#53647) 2021-07-29 12:35:11 -07:00
Raman Gupta 7e6856ace8 Add enabled attribute to zwave_js discovery model (#53645)
* Add attribute to zwave_js discovery model

* Fix binary sensor entity enabled logic

* Add tests
2021-07-29 12:35:10 -07:00
Michael b5f0c2cef4 Move TP-Link power and energy switch attributes to sensors (#53596)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-07-29 12:35:10 -07:00
Stephen Beechen 2ffc779f3d Allow uploading large snapshots (#53528)
Co-authored-by: Pascal Vizeli <pascal.vizeli@syshack.ch>
2021-07-29 12:35:09 -07:00
Paulus Schoutsen 75dc55418b Bumped version to 2021.8.0b1 2021-07-28 22:06:24 -07:00
J. Nick Koston 384ddbafab Add device class energy and last reset to sense (#53667) 2021-07-28 22:06:11 -07:00
J. Nick Koston 5483300668 Bump aiolip to 1.1.6 to fix timeout with ident (#53660) 2021-07-28 22:06:11 -07:00
Paulus Schoutsen c9d355a8a4 Add last reset to Shelly (#53654) 2021-07-28 22:06:10 -07:00
Paulus Schoutsen fce7417ed1 Add last reset to enphase sensors (#53653) 2021-07-28 22:06:09 -07:00
J. Nick Koston f13d7f189a Fix invalid homekit state when arming (#53646)
- Maybe fixes #48538
2021-07-28 22:06:09 -07:00
Raman Gupta 3265c7b8d8 Add zwave_js.reset_meter service (#53390)
* Add zwave_js.meter_reset service

* fix log statement

* Add endpoint attribute to service call and rename service

* Make service an entity service

* remove endpoint from service description
2021-07-28 22:06:08 -07:00
482 changed files with 7333 additions and 3025 deletions
+1 -6
View File
@@ -75,12 +75,6 @@ omit =
homeassistant/components/asuswrt/router.py
homeassistant/components/aten_pe/*
homeassistant/components/atome/*
homeassistant/components/automate/__init__.py
homeassistant/components/automate/base.py
homeassistant/components/automate/const.py
homeassistant/components/automate/cover.py
homeassistant/components/automate/helpers.py
homeassistant/components/automate/hub.py
homeassistant/components/aurora/__init__.py
homeassistant/components/aurora/binary_sensor.py
homeassistant/components/aurora/const.py
@@ -678,6 +672,7 @@ omit =
homeassistant/components/mystrom/light.py
homeassistant/components/mystrom/switch.py
homeassistant/components/myq/__init__.py
homeassistant/components/myq/cover.py
homeassistant/components/nad/media_player.py
homeassistant/components/nanoleaf/light.py
homeassistant/components/neato/__init__.py
-1
View File
@@ -56,7 +56,6 @@ homeassistant/components/august/* @bdraco
homeassistant/components/aurora/* @djtimca
homeassistant/components/aurora_abb_powerone/* @davet2001
homeassistant/components/auth/* @home-assistant/core
homeassistant/components/automate/* @sillyfrog
homeassistant/components/automation/* @home-assistant/core
homeassistant/components/avea/* @pattyland
homeassistant/components/awair/* @ahayworth @danielsjf
@@ -1,9 +1,12 @@
{
"config": {
"abort": {
"reauth_successful": "La reautenticaci\u00f3n fue exitosa",
"single_instance_allowed": "Solo se permite una \u00fanica configuraci\u00f3n de Abode."
},
"error": {
"cannot_connect": "No se pudo conectar",
"invalid_auth": "Autenticaci\u00f3n inv\u00e1lida",
"invalid_mfa_code": "C\u00f3digo MFA no v\u00e1lido"
},
"step": {
@@ -15,7 +18,8 @@
},
"reauth_confirm": {
"data": {
"password": "Contrase\u00f1a"
"password": "Contrase\u00f1a",
"username": "Correo electr\u00f3nico"
},
"title": "Complete su informaci\u00f3n de inicio de sesi\u00f3n de Abode"
},
@@ -1,6 +1,11 @@
{
"config": {
"abort": {
"single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n."
},
"error": {
"cannot_connect": "No se pudo conectar",
"invalid_api_key": "Clave de API no v\u00e1lida",
"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": {
+1 -1
View File
@@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/adax",
"requirements": [
"adax==0.0.1"
"adax==0.1.1"
],
"codeowners": [
"@danielhiversen"
@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno"
},
"error": {
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
},
"step": {
"user": {
"data": {
"host": "Hostitel",
"password": "Heslo"
}
}
}
}
}
@@ -1,11 +1,17 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification invalide"
},
"step": {
"user": {
"data": {
"account_id": "identifiant de compte",
"host": "H\u00f4te",
"password": "Mot de passe"
}
}
@@ -0,0 +1,20 @@
{
"config": {
"abort": {
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato"
},
"error": {
"cannot_connect": "Impossibile connettersi",
"invalid_auth": "Autenticazione non valida"
},
"step": {
"user": {
"data": {
"account_id": "ID account",
"host": "Host",
"password": "Password"
}
}
}
}
}
+4 -4
View File
@@ -91,13 +91,13 @@ class AdsCover(AdsEntity, CoverEntity):
):
"""Initialize AdsCover entity."""
super().__init__(ads_hub, name, ads_var_is_closed)
if self._ads_var is None:
if self._attr_unique_id is None:
if ads_var_position is not None:
self._unique_id = ads_var_position
self._attr_unique_id = ads_var_position
elif ads_var_pos_set is not None:
self._unique_id = ads_var_pos_set
self._attr_unique_id = ads_var_pos_set
elif ads_var_open is not None:
self._unique_id = ads_var_open
self._attr_unique_id = ads_var_open
self._state_dict[STATE_KEY_POSITION] = None
self._ads_var_position = ads_var_position
@@ -15,7 +15,6 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
from homeassistant.core import callback
from homeassistant.helpers import entity_platform
from .const import (
@@ -166,19 +165,22 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-{zone_key}'
)
async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
@callback
def _update_callback(self) -> None:
"""Load data from integration."""
self._attr_current_temperature = self._zone["measuredTemp"]
self._attr_target_temperature = self._zone["setTemp"]
self._attr_hvac_mode = HVAC_MODE_OFF
@property
def hvac_mode(self):
"""Return the current state as HVAC mode."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
self._attr_hvac_mode = HVAC_MODE_FAN_ONLY
self.async_write_ha_state()
return HVAC_MODE_FAN_ONLY
return HVAC_MODE_OFF
@property
def current_temperature(self):
"""Return the current temperature."""
return self._zone["measuredTemp"]
@property
def target_temperature(self):
"""Return the target temperature."""
return self._zone["setTemp"]
async def async_set_hvac_mode(self, hvac_mode):
"""Set the HVAC Mode and State."""
@@ -15,7 +15,7 @@
"name": "Name der Integration"
},
"description": "Richte die AEMET OpenData Integration ein. Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario",
"title": "[void]"
"title": "AEMET OpenData"
}
}
},
@@ -18,5 +18,14 @@
"title": "AEMET OpenData"
}
}
},
"options": {
"step": {
"init": {
"data": {
"station_updates": "Recueillir les donn\u00e9es des stations m\u00e9t\u00e9orologiques AEMET"
}
}
}
}
}
+11 -4
View File
@@ -67,8 +67,6 @@ async def async_setup_entry(
class AgentCamera(MjpegCamera):
"""Representation of an Agent Device Stream."""
_attr_supported_features = SUPPORT_ON_OFF
def __init__(self, device):
"""Initialize as a subclass of MjpegCamera."""
device_info = {
@@ -80,7 +78,6 @@ class AgentCamera(MjpegCamera):
self._removed = False
self._attr_name = f"{device.client.name} {device.name}"
self._attr_unique_id = f"{device._client.unique}_{device.typeID}_{device.id}"
self._attr_should_poll = True
super().__init__(device_info)
self._attr_device_info = {
"identifiers": {(AGENT_DOMAIN, self.unique_id)},
@@ -102,10 +99,10 @@ class AgentCamera(MjpegCamera):
if self.device.client.is_available and not self._removed:
_LOGGER.error("%s lost", self.name)
self._removed = True
self._attr_available = self.device.client.is_available
self._attr_icon = "mdi:camcorder-off"
if self.is_on:
self._attr_icon = "mdi:camcorder"
self._attr_available = self.device.client.is_available
self._attr_extra_state_attributes = {
ATTR_ATTRIBUTION: ATTRIBUTION,
"editable": False,
@@ -117,6 +114,11 @@ class AgentCamera(MjpegCamera):
"alerts_enabled": self.device.alerts_active,
}
@property
def should_poll(self) -> bool:
"""Update the state periodically."""
return True
@property
def is_recording(self) -> bool:
"""Return whether the monitor is recording."""
@@ -137,6 +139,11 @@ class AgentCamera(MjpegCamera):
"""Return True if entity is connected."""
return self.device.connected
@property
def supported_features(self) -> int:
"""Return supported features."""
return SUPPORT_ON_OFF
@property
def is_on(self) -> bool:
"""Return true if on."""
@@ -1,11 +1,20 @@
{
"state": {
"airvisual__pollutant_label": {
"co": "Monoxyde de carbone"
"co": "Monoxyde de carbone",
"n2": "Dioxyde d'azote",
"o3": "Ozone",
"p1": "PM10",
"p2": "PM2.5",
"s2": "Dioxyde de soufre"
},
"airvisual__pollutant_level": {
"good": "Bon",
"hazardous": "Hasardeux"
"hazardous": "Hasardeux",
"moderate": "Mod\u00e9rer",
"unhealthy": "Malsain",
"unhealthy_sensitive": "Malsain pour les groupes sensibles",
"very_unhealthy": "Tr\u00e8s malsain"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"airvisual__pollutant_level": {
"good": "\u05d8\u05d5\u05d1",
"unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0"
}
}
}
@@ -0,0 +1,20 @@
{
"state": {
"airvisual__pollutant_label": {
"co": "Monossido di carbonio",
"n2": "Anidride nitrosa",
"o3": "Ozono",
"p1": "PM10",
"p2": "PM2.5",
"s2": "Anidride solforosa"
},
"airvisual__pollutant_level": {
"good": "Buono",
"hazardous": "Pericoloso",
"moderate": "Moderato",
"unhealthy": "Malsano",
"unhealthy_sensitive": "Malsano per gruppi sensibili",
"very_unhealthy": "Molto malsano"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"airvisual__pollutant_label": {
"p1": "PM10",
"p2": "PM2.5"
}
}
}
@@ -49,7 +49,10 @@ def setup_platform(
try:
if not acc.login():
raise ValueError("Username or Password is incorrect")
add_entities(AladdinDevice(acc, door) for door in acc.get_doors())
add_entities(
(AladdinDevice(acc, door) for door in acc.get_doors()),
update_before_add=True,
)
except (TypeError, KeyError, NameError, ValueError) as ex:
_LOGGER.error("%s", ex)
hass.components.persistent_notification.create(
@@ -4,6 +4,7 @@
"arm_away": "Armer {entity_name} en mode \"sortie\"",
"arm_home": "Armer {entity_name} en mode \"maison\"",
"arm_night": "Armer {entity_name} en mode \"nuit\"",
"arm_vacation": "Armer {entity_name} vacances",
"disarm": "D\u00e9sarmer {entity_name}",
"trigger": "D\u00e9clencheur {entity_name}"
},
@@ -29,6 +30,7 @@
"armed_custom_bypass": "Arm\u00e9 avec exception personnalis\u00e9e",
"armed_home": "Enclench\u00e9e (pr\u00e9sent)",
"armed_night": "Enclench\u00e9 (nuit)",
"armed_vacation": "Arm\u00e9es vacances",
"arming": "Activation",
"disarmed": "D\u00e9sactiv\u00e9e",
"disarming": "D\u00e9sactivation",
@@ -111,13 +111,13 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
def _fault_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or int(zone) == self._zone_number:
self._attr_state = 1
self._attr_is_on = True
self.schedule_update_ha_state()
def _restore_callback(self, zone):
"""Update the zone's state, if needed."""
if zone is None or (int(zone) == self._zone_number and not self._loop):
self._attr_state = 0
self._attr_is_on = False
self.schedule_update_ha_state()
def _rfx_message_callback(self, message):
@@ -125,7 +125,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
if self._rfid and message and message.serial_number == self._rfid:
rfstate = message.value
if self._loop:
self._attr_state = 1 if message.loop[self._loop - 1] else 0
self._attr_is_on = bool(message.loop[self._loop - 1])
attr = {CONF_ZONE_NUMBER: self._zone_number}
if self._rfid and rfstate is not None:
attr[ATTR_RF_BIT0] = bool(rfstate & 0x01)
@@ -150,5 +150,5 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
message.channel,
message.value,
)
self._attr_state = message.value
self._attr_is_on = bool(message.value)
self.schedule_update_ha_state()
@@ -0,0 +1,28 @@
{
"config": {
"abort": {
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_api_key": "Cl\u00e9 API non valide"
},
"step": {
"reauth_confirm": {
"data": {
"api_key": "cl\u00e9 API",
"description": "R\u00e9-authentifiez-vous avec votre compte Ambee."
}
},
"user": {
"data": {
"api_key": "cl\u00e9 API",
"latitude": "Latitude",
"longitude": "Longitude",
"name": "Nom"
},
"description": "Configurer Ambee pour l'int\u00e9grer \u00e0 Home Assistant."
}
}
}
}
@@ -0,0 +1,10 @@
{
"state": {
"ambee__risk": {
"high": "Haute",
"low": "Faible",
"moderate": "Mod\u00e9rer",
"very high": "Tr\u00e8s \u00e9lev\u00e9"
}
}
}
@@ -0,0 +1,9 @@
{
"state": {
"ambee__risk": {
"high": "\u05d2\u05d1\u05d5\u05d4",
"low": "\u05e0\u05de\u05d5\u05da",
"very high": "\u05d2\u05d1\u05d5\u05d4 \u05de\u05d0\u05d5\u05d3"
}
}
}
@@ -154,8 +154,6 @@ class AmbiclimateEntity(ClimateEntity):
"name": self.name,
"manufacturer": "Ambiclimate",
}
self._attr_min_temp = heater.get_min_temp()
self._attr_max_temp = heater.get_max_temp()
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
@@ -184,6 +182,8 @@ class AmbiclimateEntity(ClimateEntity):
await self._store.async_save(token_info)
data = await self._heater.update_device()
self._attr_min_temp = self._heater.get_min_temp()
self._attr_max_temp = self._heater.get_max_temp()
self._attr_target_temperature = data.get("target_temperature")
self._attr_current_temperature = data.get("temperature")
self._attr_current_humidity = data.get("humidity")
@@ -8,6 +8,10 @@ import async_timeout
from homeassistant.components import hassio
from homeassistant.components.api import ATTR_INSTALLATION_TYPE
from homeassistant.components.automation.const import DOMAIN as AUTOMATION_DOMAIN
from homeassistant.components.energy import (
DOMAIN as ENERGY_DOMAIN,
is_configured as energy_is_configured,
)
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -26,8 +30,10 @@ from .const import (
ATTR_AUTOMATION_COUNT,
ATTR_BASE,
ATTR_BOARD,
ATTR_CONFIGURED,
ATTR_CUSTOM_INTEGRATIONS,
ATTR_DIAGNOSTICS,
ATTR_ENERGY,
ATTR_HEALTHY,
ATTR_INTEGRATION_COUNT,
ATTR_INTEGRATIONS,
@@ -222,6 +228,11 @@ class Analytics:
if supervisor_info is not None:
payload[ATTR_ADDONS] = addons
if ENERGY_DOMAIN in integrations:
payload[ATTR_ENERGY] = {
ATTR_CONFIGURED: await energy_is_configured(self.hass)
}
if self.preferences.get(ATTR_STATISTICS, False):
payload[ATTR_STATE_COUNT] = len(self.hass.states.async_all())
payload[ATTR_AUTOMATION_COUNT] = len(
@@ -21,8 +21,10 @@ ATTR_AUTO_UPDATE = "auto_update"
ATTR_AUTOMATION_COUNT = "automation_count"
ATTR_BASE = "base"
ATTR_BOARD = "board"
ATTR_CONFIGURED = "configured"
ATTR_CUSTOM_INTEGRATIONS = "custom_integrations"
ATTR_DIAGNOSTICS = "diagnostics"
ATTR_ENERGY = "energy"
ATTR_HEALTHY = "healthy"
ATTR_INSTALLATION_TYPE = "installation_type"
ATTR_INTEGRATION_COUNT = "integration_count"
@@ -2,8 +2,17 @@
"domain": "analytics",
"name": "Analytics",
"documentation": "https://www.home-assistant.io/integrations/analytics",
"codeowners": ["@home-assistant/core", "@ludeeus"],
"dependencies": ["api", "websocket_api"],
"codeowners": [
"@home-assistant/core",
"@ludeeus"
],
"dependencies": [
"api",
"websocket_api"
],
"after_dependencies": [
"energy"
],
"quality_scale": "internal",
"iot_class": "cloud_push"
}
}
@@ -449,6 +449,11 @@ class ADBDevice(MediaPlayerEntity):
ATTR_HDMI_INPUT: None,
}
@property
def media_image_hash(self):
"""Hash value for media image."""
return f"{datetime.now().timestamp()}" if self._screencap else None
@adb_decorator()
async def _adb_screencap(self):
"""Take a screen capture from the device."""
@@ -458,9 +463,6 @@ class ADBDevice(MediaPlayerEntity):
"""Fetch current playing image."""
if not self._screencap or self.state in [STATE_OFF, None] or not self.available:
return None, None
self._attr_media_image_hash = (
f"{datetime.now().timestamp()}" if self._screencap else None
)
media_data = await self._adb_screencap()
if media_data:
+46 -47
View File
@@ -26,72 +26,72 @@ _LOGGER = logging.getLogger(__name__)
SENSOR_PREFIX = "UPS "
SENSOR_TYPES = {
"alarmdel": ["Alarm Delay", "", "mdi:alarm", None],
"ambtemp": ["Ambient Temperature", "", "mdi:thermometer", None],
"apc": ["Status Data", "", "mdi:information-outline", None],
"apcmodel": ["Model", "", "mdi:information-outline", None],
"badbatts": ["Bad Batteries", "", "mdi:information-outline", None],
"battdate": ["Battery Replaced", "", "mdi:calendar-clock", None],
"battstat": ["Battery Status", "", "mdi:information-outline", None],
"alarmdel": ["Alarm Delay", None, "mdi:alarm", None],
"ambtemp": ["Ambient Temperature", None, "mdi:thermometer", None],
"apc": ["Status Data", None, "mdi:information-outline", None],
"apcmodel": ["Model", None, "mdi:information-outline", None],
"badbatts": ["Bad Batteries", None, "mdi:information-outline", None],
"battdate": ["Battery Replaced", None, "mdi:calendar-clock", None],
"battstat": ["Battery Status", None, "mdi:information-outline", None],
"battv": ["Battery Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"bcharge": ["Battery", PERCENTAGE, "mdi:battery", None],
"cable": ["Cable Type", "", "mdi:ethernet-cable", None],
"cumonbatt": ["Total Time on Battery", "", "mdi:timer-outline", None],
"date": ["Status Date", "", "mdi:calendar-clock", None],
"dipsw": ["Dip Switch Settings", "", "mdi:information-outline", None],
"dlowbatt": ["Low Battery Signal", "", "mdi:clock-alert", None],
"driver": ["Driver", "", "mdi:information-outline", None],
"dshutd": ["Shutdown Delay", "", "mdi:timer-outline", None],
"dwake": ["Wake Delay", "", "mdi:timer-outline", None],
"endapc": ["Date and Time", "", "mdi:calendar-clock", None],
"extbatts": ["External Batteries", "", "mdi:information-outline", None],
"firmware": ["Firmware Version", "", "mdi:information-outline", None],
"cable": ["Cable Type", None, "mdi:ethernet-cable", None],
"cumonbatt": ["Total Time on Battery", None, "mdi:timer-outline", None],
"date": ["Status Date", None, "mdi:calendar-clock", None],
"dipsw": ["Dip Switch Settings", None, "mdi:information-outline", None],
"dlowbatt": ["Low Battery Signal", None, "mdi:clock-alert", None],
"driver": ["Driver", None, "mdi:information-outline", None],
"dshutd": ["Shutdown Delay", None, "mdi:timer-outline", None],
"dwake": ["Wake Delay", None, "mdi:timer-outline", None],
"endapc": ["Date and Time", None, "mdi:calendar-clock", None],
"extbatts": ["External Batteries", None, "mdi:information-outline", None],
"firmware": ["Firmware Version", None, "mdi:information-outline", None],
"hitrans": ["Transfer High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"hostname": ["Hostname", "", "mdi:information-outline", None],
"hostname": ["Hostname", None, "mdi:information-outline", None],
"humidity": ["Ambient Humidity", PERCENTAGE, "mdi:water-percent", None],
"itemp": ["Internal Temperature", TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE],
"lastxfer": ["Last Transfer", "", "mdi:transfer", None],
"linefail": ["Input Voltage Status", "", "mdi:information-outline", None],
"lastxfer": ["Last Transfer", None, "mdi:transfer", None],
"linefail": ["Input Voltage Status", None, "mdi:information-outline", None],
"linefreq": ["Line Frequency", FREQUENCY_HERTZ, "mdi:information-outline", None],
"linev": ["Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"loadpct": ["Load", PERCENTAGE, "mdi:gauge", None],
"loadapnt": ["Load Apparent Power", PERCENTAGE, "mdi:gauge", None],
"lotrans": ["Transfer Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"mandate": ["Manufacture Date", "", "mdi:calendar", None],
"masterupd": ["Master Update", "", "mdi:information-outline", None],
"mandate": ["Manufacture Date", None, "mdi:calendar", None],
"masterupd": ["Master Update", None, "mdi:information-outline", None],
"maxlinev": ["Input Voltage High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"maxtime": ["Battery Timeout", "", "mdi:timer-off-outline", None],
"maxtime": ["Battery Timeout", None, "mdi:timer-off-outline", None],
"mbattchg": ["Battery Shutdown", PERCENTAGE, "mdi:battery-alert", None],
"minlinev": ["Input Voltage Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"mintimel": ["Shutdown Time", "", "mdi:timer-outline", None],
"model": ["Model", "", "mdi:information-outline", None],
"mintimel": ["Shutdown Time", None, "mdi:timer-outline", None],
"model": ["Model", None, "mdi:information-outline", None],
"nombattv": ["Battery Nominal Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"nominv": ["Nominal Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"nomoutv": ["Nominal Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"nompower": ["Nominal Output Power", POWER_WATT, "mdi:flash", None],
"nomapnt": ["Nominal Apparent Power", POWER_VOLT_AMPERE, "mdi:flash", None],
"numxfers": ["Transfer Count", "", "mdi:counter", None],
"numxfers": ["Transfer Count", None, "mdi:counter", None],
"outcurnt": ["Output Current", ELECTRIC_CURRENT_AMPERE, "mdi:flash", None],
"outputv": ["Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
"reg1": ["Register 1 Fault", "", "mdi:information-outline", None],
"reg2": ["Register 2 Fault", "", "mdi:information-outline", None],
"reg3": ["Register 3 Fault", "", "mdi:information-outline", None],
"reg1": ["Register 1 Fault", None, "mdi:information-outline", None],
"reg2": ["Register 2 Fault", None, "mdi:information-outline", None],
"reg3": ["Register 3 Fault", None, "mdi:information-outline", None],
"retpct": ["Restore Requirement", PERCENTAGE, "mdi:battery-alert", None],
"selftest": ["Last Self Test", "", "mdi:calendar-clock", None],
"sense": ["Sensitivity", "", "mdi:information-outline", None],
"serialno": ["Serial Number", "", "mdi:information-outline", None],
"starttime": ["Startup Time", "", "mdi:calendar-clock", None],
"statflag": ["Status Flag", "", "mdi:information-outline", None],
"status": ["Status", "", "mdi:information-outline", None],
"stesti": ["Self Test Interval", "", "mdi:information-outline", None],
"timeleft": ["Time Left", "", "mdi:clock-alert", None],
"tonbatt": ["Time on Battery", "", "mdi:timer-outline", None],
"upsmode": ["Mode", "", "mdi:information-outline", None],
"upsname": ["Name", "", "mdi:information-outline", None],
"version": ["Daemon Info", "", "mdi:information-outline", None],
"xoffbat": ["Transfer from Battery", "", "mdi:transfer", None],
"xoffbatt": ["Transfer from Battery", "", "mdi:transfer", None],
"xonbatt": ["Transfer to Battery", "", "mdi:transfer", None],
"selftest": ["Last Self Test", None, "mdi:calendar-clock", None],
"sense": ["Sensitivity", None, "mdi:information-outline", None],
"serialno": ["Serial Number", None, "mdi:information-outline", None],
"starttime": ["Startup Time", None, "mdi:calendar-clock", None],
"statflag": ["Status Flag", None, "mdi:information-outline", None],
"status": ["Status", None, "mdi:information-outline", None],
"stesti": ["Self Test Interval", None, "mdi:information-outline", None],
"timeleft": ["Time Left", None, "mdi:clock-alert", None],
"tonbatt": ["Time on Battery", None, "mdi:timer-outline", None],
"upsmode": ["Mode", None, "mdi:information-outline", None],
"upsname": ["Name", None, "mdi:information-outline", None],
"version": ["Daemon Info", None, "mdi:information-outline", None],
"xoffbat": ["Transfer from Battery", None, "mdi:transfer", None],
"xoffbatt": ["Transfer from Battery", None, "mdi:transfer", None],
"xonbatt": ["Transfer to Battery", None, "mdi:transfer", None],
}
SPECIFIC_UNITS = {"ITEMP": TEMP_CELSIUS}
@@ -165,8 +165,7 @@ class APCUPSdSensor(SensorEntity):
self.type = sensor_type
self._attr_name = SENSOR_PREFIX + SENSOR_TYPES[sensor_type][0]
self._attr_icon = SENSOR_TYPES[self.type][2]
if SENSOR_TYPES[sensor_type][1]:
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._attr_device_class = SENSOR_TYPES[sensor_type][3]
def update(self):
@@ -3,7 +3,7 @@
"name": "Apple TV",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
"requirements": ["pyatv==0.8.1"],
"requirements": ["pyatv==0.8.2"],
"zeroconf": ["_mediaremotetv._tcp.local.", "_touch-able._tcp.local."],
"after_dependencies": ["discovery"],
"codeowners": ["@postlund"],
+3 -2
View File
@@ -93,9 +93,10 @@ class AquaLogicSensor(SensorEntity):
if panel is not None:
if panel.is_metric:
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
self._attr_state = getattr(panel, self._type)
self.async_write_ha_state()
else:
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
self._attr_state = getattr(panel, self._type)
self.async_write_ha_state()
else:
self._attr_unit_of_measurement = None
@@ -5,10 +5,16 @@
"already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea",
"cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4"
},
"error": {
"many": "\u05e8\u05d9\u05e7\u05d9\u05dd",
"one": "\u05e8\u05d9\u05e7",
"other": "\u05e8\u05d9\u05e7\u05d9\u05dd",
"two": "\u05e8\u05d9\u05e7\u05d9\u05dd"
},
"flow_title": "{host}",
"step": {
"confirm": {
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea Arcam FMJ \u05d1- '{host}' \u05dc-Home Assistant?"
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea Arcam FMJ \u05d1-`{host}` \u05dc-Home Assistant?"
},
"user": {
"data": {
@@ -24,7 +24,7 @@
"username": "Benutzername"
},
"description": "Einstellen der erforderlichen Parameter f\u00fcr die Verbindung mit deinem Router.",
"title": ""
"title": "AsusWRT"
}
}
},
+36 -4
View File
@@ -14,12 +14,13 @@ from homeassistant.const import (
CONF_NAME,
CONF_PASSWORD,
CONF_USERNAME,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
ENERGY_KILO_WATT_HOUR,
POWER_WATT,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
from homeassistant.util import Throttle, dt as dt_util
_LOGGER = logging.getLogger(__name__)
@@ -87,12 +88,16 @@ class AtomeData:
self._is_connected = None
self._day_usage = None
self._day_price = None
self._day_last_reset = None
self._week_usage = None
self._week_price = None
self._week_last_reset = None
self._month_usage = None
self._month_price = None
self._month_last_reset = None
self._year_usage = None
self._year_price = None
self._year_last_reset = None
@property
def live_power(self):
@@ -137,6 +142,11 @@ class AtomeData:
"""Return latest daily usage value."""
return self._day_price
@property
def day_last_reset(self):
"""Return latest daily last reset."""
return self._day_last_reset
@Throttle(DAILY_SCAN_INTERVAL)
def update_day_usage(self):
"""Return current daily power usage."""
@@ -144,6 +154,7 @@ class AtomeData:
values = self.atome_client.get_consumption(DAILY_TYPE)
self._day_usage = values["total"] / 1000
self._day_price = values["price"]
self._day_last_reset = dt_util.parse_datetime(values["startPeriod"])
_LOGGER.debug("Updating Atome daily data. Got: %d", self._day_usage)
except KeyError as error:
@@ -159,6 +170,11 @@ class AtomeData:
"""Return latest weekly usage value."""
return self._week_price
@property
def week_last_reset(self):
"""Return latest weekly last reset value."""
return self._week_last_reset
@Throttle(WEEKLY_SCAN_INTERVAL)
def update_week_usage(self):
"""Return current weekly power usage."""
@@ -166,6 +182,7 @@ class AtomeData:
values = self.atome_client.get_consumption(WEEKLY_TYPE)
self._week_usage = values["total"] / 1000
self._week_price = values["price"]
self._week_last_reset = dt_util.parse_datetime(values["startPeriod"])
_LOGGER.debug("Updating Atome weekly data. Got: %d", self._week_usage)
except KeyError as error:
@@ -181,6 +198,11 @@ class AtomeData:
"""Return latest monthly usage value."""
return self._month_price
@property
def month_last_reset(self):
"""Return latest monthly last reset value."""
return self._month_last_reset
@Throttle(MONTHLY_SCAN_INTERVAL)
def update_month_usage(self):
"""Return current monthly power usage."""
@@ -188,6 +210,7 @@ class AtomeData:
values = self.atome_client.get_consumption(MONTHLY_TYPE)
self._month_usage = values["total"] / 1000
self._month_price = values["price"]
self._month_last_reset = dt_util.parse_datetime(values["startPeriod"])
_LOGGER.debug("Updating Atome monthly data. Got: %d", self._month_usage)
except KeyError as error:
@@ -203,6 +226,11 @@ class AtomeData:
"""Return latest yearly usage value."""
return self._year_price
@property
def year_last_reset(self):
"""Return latest yearly last reset value."""
return self._year_last_reset
@Throttle(YEARLY_SCAN_INTERVAL)
def update_year_usage(self):
"""Return current yearly power usage."""
@@ -210,6 +238,7 @@ class AtomeData:
values = self.atome_client.get_consumption(YEARLY_TYPE)
self._year_usage = values["total"] / 1000
self._year_price = values["price"]
self._year_last_reset = dt_util.parse_datetime(values["startPeriod"])
_LOGGER.debug("Updating Atome yearly data. Got: %d", self._year_usage)
except KeyError as error:
@@ -219,19 +248,19 @@ class AtomeData:
class AtomeSensor(SensorEntity):
"""Representation of a sensor entity for Atome."""
_attr_device_class = DEVICE_CLASS_POWER
def __init__(self, data, name, sensor_type):
"""Initialize the sensor."""
self._attr_name = name
self._data = data
self._sensor_type = sensor_type
self._attr_state_class = STATE_CLASS_MEASUREMENT
if sensor_type == LIVE_TYPE:
self._attr_device_class = DEVICE_CLASS_POWER
self._attr_unit_of_measurement = POWER_WATT
self._attr_state_class = STATE_CLASS_MEASUREMENT
else:
self._attr_device_class = DEVICE_CLASS_ENERGY
self._attr_unit_of_measurement = ENERGY_KILO_WATT_HOUR
def update(self):
@@ -247,6 +276,9 @@ class AtomeSensor(SensorEntity):
}
else:
self._attr_state = getattr(self._data, f"{self._sensor_type}_usage")
self._attr_last_reset = dt_util.as_utc(
getattr(self._data, f"{self._sensor_type}_last_reset")
)
self._attr_extra_state_attributes = {
"price": getattr(self._data, f"{self._sensor_type}_price")
}
@@ -25,7 +25,7 @@
}
},
"validation": {
"description": "\u05d0\u05e0\u05d0 \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea {login_method} \u05e9\u05dc\u05da ({\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9}) \u05d5\u05d4\u05d6\u05df \u05d0\u05ea \u05e7\u05d5\u05d3 \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05dc\u05d4\u05dc\u05df"
"description": "\u05e0\u05d0 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea {login_method} ( {username} ) \u05d5\u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e7\u05d5\u05d3 \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05dc\u05d4\u05dc\u05df"
}
}
}
@@ -2,18 +2,18 @@
"mfa_setup": {
"notify": {
"abort": {
"no_available_service": "\u05d0\u05d9\u05df \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 notify \u05d6\u05de\u05d9\u05e0\u05d9\u05dd."
"no_available_service": "\u05d0\u05d9\u05df \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 \u05d4\u05ea\u05e8\u05d0\u05d5\u05ea \u05d6\u05de\u05d9\u05e0\u05d9\u05dd."
},
"error": {
"invalid_code": "\u05e7\u05d5\u05d3 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d0\u05e0\u05d0 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1."
},
"step": {
"init": {
"description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d0\u05d7\u05d3 \u05de\u05e9\u05e8\u05d5\u05ea\u05d9 notify",
"description": "\u05e0\u05d0 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d0\u05d7\u05d3 \u05de\u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 \u05d4\u05d4\u05d5\u05d3\u05e2\u05d5\u05ea:",
"title": "\u05d4\u05d2\u05d3\u05e8 \u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05d4\u05e0\u05e9\u05dc\u05d7\u05ea \u05e2\u05dc \u05d9\u05d3\u05d9 \u05e8\u05db\u05d9\u05d1 notify"
},
"setup": {
"description": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05e0\u05e9\u05dc\u05d7\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 **{notify_service}**. \u05d4\u05d6\u05df \u05d0\u05d5\u05ea\u05d4 \u05dc\u05de\u05d8\u05d4:",
"description": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05e0\u05e9\u05dc\u05d7\u05d4 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea ** Notify. {notify_service} **. \u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05d5\u05ea\u05d5 \u05dc\u05de\u05d8\u05d4:",
"title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4"
}
},
@@ -1,36 +0,0 @@
"""The Automate Pulse Hub v2 integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
from .hub import PulseHub
PLATFORMS = ["cover"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Automate Pulse Hub v2 from a config entry."""
hub = PulseHub(hass, entry)
if not await hub.async_setup():
return False
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = hub
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
hub = hass.data[DOMAIN][entry.entry_id]
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
if not await hub.async_reset():
return False
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
-93
View File
@@ -1,93 +0,0 @@
"""Base class for Automate Roller Blinds."""
import logging
import aiopulse2
from homeassistant.core import callback
from homeassistant.helpers import entity
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_registry import async_get_registry as get_ent_reg
from .const import AUTOMATE_ENTITY_REMOVE, DOMAIN
_LOGGER = logging.getLogger(__name__)
class AutomateBase(entity.Entity):
"""Base representation of an Automate roller."""
def __init__(self, roller: aiopulse2.Roller) -> None:
"""Initialize the roller."""
self.roller = roller
@property
def available(self) -> bool:
"""Return True if roller and hub is available."""
return self.roller.online and self.roller.hub.connected
async def async_remove_and_unregister(self):
"""Unregister from entity and device registry and call entity remove function."""
_LOGGER.info("Removing %s %s", self.__class__.__name__, self.unique_id)
ent_registry = await get_ent_reg(self.hass)
if self.entity_id in ent_registry.entities:
ent_registry.async_remove(self.entity_id)
dev_registry = await get_dev_reg(self.hass)
device = dev_registry.async_get_device(
identifiers={(DOMAIN, self.unique_id)}, connections=set()
)
if device is not None:
dev_registry.async_update_device(
device.id, remove_config_entry_id=self.registry_entry.config_entry_id
)
await self.async_remove()
async def async_added_to_hass(self):
"""Entity has been added to hass."""
self.roller.callback_subscribe(self.notify_update)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
AUTOMATE_ENTITY_REMOVE.format(self.roller.id),
self.async_remove_and_unregister,
)
)
async def async_will_remove_from_hass(self):
"""Entity being removed from hass."""
self.roller.callback_unsubscribe(self.notify_update)
@callback
def notify_update(self, roller: aiopulse2.Roller):
"""Write updated device state information."""
_LOGGER.debug(
"Device update notification received: %s (%r)", roller.id, roller.name
)
self.async_write_ha_state()
@property
def should_poll(self):
"""Report that Automate entities do not need polling."""
return False
@property
def unique_id(self):
"""Return the unique ID of this roller."""
return self.roller.id
@property
def name(self):
"""Return the name of roller."""
return self.roller.name
@property
def device_info(self):
"""Return the device info."""
attrs = {
"identifiers": {(DOMAIN, self.roller.id)},
}
return attrs
@@ -1,37 +0,0 @@
"""Config flow for Automate Pulse Hub v2 integration."""
import logging
import aiopulse2
import voluptuous as vol
from homeassistant import config_entries
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema({vol.Required("host"): str})
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Automate Pulse Hub v2."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle the initial step once we have info from the user."""
if user_input is not None:
try:
hub = aiopulse2.Hub(user_input["host"])
await hub.test()
title = hub.name
except Exception: # pylint: disable=broad-except
return self.async_show_form(
step_id="user",
data_schema=DATA_SCHEMA,
errors={"base": "cannot_connect"},
)
return self.async_create_entry(title=title, data=user_input)
return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA)
@@ -1,6 +0,0 @@
"""Constants for the Automate Pulse Hub v2 integration."""
DOMAIN = "automate"
AUTOMATE_HUB_UPDATE = "automate_hub_update_{}"
AUTOMATE_ENTITY_REMOVE = "automate_entity_remove_{}"
-147
View File
@@ -1,147 +0,0 @@
"""Support for Automate Roller Blinds."""
import aiopulse2
from homeassistant.components.cover import (
ATTR_POSITION,
DEVICE_CLASS_SHADE,
SUPPORT_CLOSE,
SUPPORT_CLOSE_TILT,
SUPPORT_OPEN,
SUPPORT_OPEN_TILT,
SUPPORT_SET_POSITION,
SUPPORT_SET_TILT_POSITION,
SUPPORT_STOP,
SUPPORT_STOP_TILT,
CoverEntity,
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .base import AutomateBase
from .const import AUTOMATE_HUB_UPDATE, DOMAIN
from .helpers import async_add_automate_entities
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Automate Rollers from a config entry."""
hub = hass.data[DOMAIN][config_entry.entry_id]
current = set()
@callback
def async_add_automate_covers():
async_add_automate_entities(
hass, AutomateCover, config_entry, current, async_add_entities
)
hub.cleanup_callbacks.append(
async_dispatcher_connect(
hass,
AUTOMATE_HUB_UPDATE.format(config_entry.entry_id),
async_add_automate_covers,
)
)
class AutomateCover(AutomateBase, CoverEntity):
"""Representation of a Automate cover device."""
@property
def current_cover_position(self):
"""Return the current position of the roller blind.
None is unknown, 0 is closed, 100 is fully open.
"""
position = None
if self.roller.closed_percent is not None:
position = 100 - self.roller.closed_percent
return position
@property
def current_cover_tilt_position(self):
"""Return the current tilt of the roller blind.
None is unknown, 0 is closed, 100 is fully open.
"""
return None
@property
def supported_features(self):
"""Flag supported features."""
supported_features = 0
if self.current_cover_position is not None:
supported_features |= (
SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION
)
if self.current_cover_tilt_position is not None:
supported_features |= (
SUPPORT_OPEN_TILT
| SUPPORT_CLOSE_TILT
| SUPPORT_STOP_TILT
| SUPPORT_SET_TILT_POSITION
)
return supported_features
@property
def device_info(self):
"""Return the device info."""
attrs = super().device_info
attrs["manufacturer"] = "Automate"
attrs["model"] = self.roller.devicetype
attrs["sw_version"] = self.roller.version
attrs["via_device"] = (DOMAIN, self.roller.hub.id)
attrs["name"] = self.name
return attrs
@property
def device_class(self):
"""Class of the cover, a shade."""
return DEVICE_CLASS_SHADE
@property
def is_opening(self):
"""Is cover opening/moving up."""
return self.roller.action == aiopulse2.MovingAction.up
@property
def is_closing(self):
"""Is cover closing/moving down."""
return self.roller.action == aiopulse2.MovingAction.down
@property
def is_closed(self):
"""Return if the cover is closed."""
return self.roller.closed_percent == 100
async def async_close_cover(self, **kwargs):
"""Close the roller."""
await self.roller.move_down()
async def async_open_cover(self, **kwargs):
"""Open the roller."""
await self.roller.move_up()
async def async_stop_cover(self, **kwargs):
"""Stop the roller."""
await self.roller.move_stop()
async def async_set_cover_position(self, **kwargs):
"""Move the roller shutter to a specific position."""
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
async def async_close_cover_tilt(self, **kwargs):
"""Close the roller."""
await self.roller.move_down()
async def async_open_cover_tilt(self, **kwargs):
"""Open the roller."""
await self.roller.move_up()
async def async_stop_cover_tilt(self, **kwargs):
"""Stop the roller."""
await self.roller.move_stop()
async def async_set_cover_tilt(self, **kwargs):
"""Tilt the roller shutter to a specific position."""
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
@@ -1,46 +0,0 @@
"""Helper functions for Automate Pulse."""
import logging
from homeassistant.core import callback
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@callback
def async_add_automate_entities(
hass, entity_class, config_entry, current, async_add_entities
):
"""Add any new entities."""
hub = hass.data[DOMAIN][config_entry.entry_id]
_LOGGER.debug("Looking for new %s on: %s", entity_class.__name__, hub.host)
api = hub.api.rollers
new_items = []
for unique_id, roller in api.items():
if unique_id not in current:
_LOGGER.debug("New %s %s", entity_class.__name__, unique_id)
new_item = entity_class(roller)
current.add(unique_id)
new_items.append(new_item)
async_add_entities(new_items)
async def update_devices(hass, config_entry, api):
"""Tell hass that device info has been updated."""
dev_registry = await get_dev_reg(hass)
for api_item in api.values():
# Update Device name
device = dev_registry.async_get_device(
identifiers={(DOMAIN, api_item.id)}, connections=set()
)
if device is not None:
dev_registry.async_update_device(
device.id,
name=api_item.name,
)
-89
View File
@@ -1,89 +0,0 @@
"""Code to handle a Pulse Hub."""
from __future__ import annotations
import asyncio
import logging
import aiopulse2
from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import AUTOMATE_ENTITY_REMOVE, AUTOMATE_HUB_UPDATE
from .helpers import update_devices
_LOGGER = logging.getLogger(__name__)
class PulseHub:
"""Manages a single Pulse Hub."""
def __init__(self, hass, config_entry):
"""Initialize the system."""
self.config_entry = config_entry
self.hass = hass
self.api: aiopulse2.Hub | None = None
self.tasks = []
self.current_rollers = {}
self.cleanup_callbacks = []
@property
def title(self):
"""Return the title of the hub shown in the integrations list."""
return f"{self.api.name} ({self.api.host})"
@property
def host(self):
"""Return the host of this hub."""
return self.config_entry.data["host"]
async def async_setup(self):
"""Set up a hub based on host parameter."""
host = self.host
hub = aiopulse2.Hub(host, propagate_callbacks=True)
self.api = hub
hub.callback_subscribe(self.async_notify_update)
self.tasks.append(asyncio.create_task(hub.run()))
_LOGGER.debug("Hub setup complete")
return True
async def async_reset(self):
"""Reset this hub to default state."""
for cleanup_callback in self.cleanup_callbacks:
cleanup_callback()
# If not setup
if self.api is None:
return False
self.api.callback_unsubscribe(self.async_notify_update)
await self.api.stop()
del self.api
self.api = None
# Wait for any running tasks to complete
await asyncio.wait(self.tasks)
return True
async def async_notify_update(self, hub=None):
"""Evaluate entities when hub reports that update has occurred."""
_LOGGER.debug("Hub {self.title} updated")
await update_devices(self.hass, self.config_entry, self.api.rollers)
self.hass.config_entries.async_update_entry(self.config_entry, title=self.title)
async_dispatcher_send(
self.hass, AUTOMATE_HUB_UPDATE.format(self.config_entry.entry_id)
)
for unique_id in list(self.current_rollers):
if unique_id not in self.api.rollers:
_LOGGER.debug("Notifying remove of %s", unique_id)
self.current_rollers.pop(unique_id)
async_dispatcher_send(
self.hass, AUTOMATE_ENTITY_REMOVE.format(unique_id)
)
@@ -1,13 +0,0 @@
{
"domain": "automate",
"name": "Automate Pulse Hub v2",
"config_flow": true,
"iot_class": "local_push",
"documentation": "https://www.home-assistant.io/integrations/automate",
"requirements": [
"aiopulse2==0.6.0"
],
"codeowners": [
"@sillyfrog"
]
}
@@ -1,19 +0,0 @@
{
"config": {
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "El dispositiu ja est\u00e0 configurat"
},
"error": {
"cannot_connect": "Ha fallat la connexi\u00f3",
"invalid_auth": "Autenticaci\u00f3 inv\u00e0lida",
"unknown": "Error inesperat"
},
"step": {
"user": {
"data": {
"host": "Amfitri\u00f3"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Ger\u00e4t ist bereits konfiguriert"
},
"error": {
"cannot_connect": "Verbindung fehlgeschlagen",
"invalid_auth": "Ung\u00fcltige Authentifizierung",
"unknown": "Unerwarteter Fehler"
},
"step": {
"user": {
"data": {
"host": "Host"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication",
"unknown": "Unexpected error"
},
"step": {
"user": {
"data": {
"host": "Host"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Seade on juba h\u00e4\u00e4lestatud"
},
"error": {
"cannot_connect": "\u00dchendamine nurjus",
"invalid_auth": "Tuvastamine nurjus",
"unknown": "Ootamatu t\u00f5rge"
},
"step": {
"user": {
"data": {
"host": "Host"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Apparaat is al geconfigureerd"
},
"error": {
"cannot_connect": "Kan geen verbinding maken",
"invalid_auth": "Ongeldige authenticatie",
"unknown": "Onverwachte fout"
},
"step": {
"user": {
"data": {
"host": "Host"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
},
"error": {
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
"invalid_auth": "Niepoprawne uwierzytelnienie",
"unknown": "Nieoczekiwany b\u0142\u0105d"
},
"step": {
"user": {
"data": {
"host": "Nazwa hosta lub adres IP"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant."
},
"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": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.",
"unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."
},
"step": {
"user": {
"data": {
"host": "\u0425\u043e\u0441\u0442"
}
}
}
}
}
@@ -1,19 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
},
"error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557",
"invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548",
"unknown": "\u672a\u9810\u671f\u932f\u8aa4"
},
"step": {
"user": {
"data": {
"host": "\u4e3b\u6a5f\u7aef"
}
}
}
}
}
@@ -71,9 +71,6 @@ from homeassistant.loader import bind_hass
from homeassistant.util.dt import parse_datetime
from .config import AutomationConfig, async_validate_config_item
# Not used except by packages to check config structure
from .config import PLATFORM_SCHEMA # noqa: F401
from .const import (
CONF_ACTION,
CONF_INITIAL_STATE,
@@ -37,6 +37,8 @@ from .helpers import async_get_blueprints
# mypy: allow-untyped-calls, allow-untyped-defs
# mypy: no-check-untyped-defs, no-warn-return-any
PACKAGE_MERGE_HINT = "list"
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
PLATFORM_SCHEMA = vol.All(
@@ -35,19 +35,19 @@
"on": "\u05de\u05d7\u05d5\u05d1\u05e8"
},
"door": {
"off": "\u05e1\u05d2\u05d5\u05e8\u05d4",
"off": "\u05e1\u05d2\u05d5\u05e8",
"on": "\u05e4\u05ea\u05d5\u05d7"
},
"garage_door": {
"off": "\u05e1\u05d2\u05d5\u05e8\u05d4",
"on": "\u05e4\u05ea\u05d5\u05d7\u05d4"
"off": "\u05e1\u05d2\u05d5\u05e8",
"on": "\u05e4\u05ea\u05d5\u05d7"
},
"gas": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"heat": {
"off": "\u05e8\u05d2\u05d9\u05dc",
"off": "\u05e0\u05d5\u05e8\u05de\u05dc\u05d9",
"on": "\u05d7\u05dd"
},
"light": {
@@ -64,7 +64,7 @@
},
"motion": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"moving": {
"off": "\u05dc\u05d0 \u05d6\u05d6",
@@ -72,10 +72,10 @@
},
"occupancy": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"opening": {
"off": "\u05e0\u05e1\u05d2\u05e8",
"off": "\u05e1\u05d2\u05d5\u05e8",
"on": "\u05e4\u05ea\u05d5\u05d7"
},
"plug": {
@@ -95,18 +95,18 @@
},
"smoke": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"sound": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"vibration": {
"off": "\u05e0\u05e7\u05d9",
"on": "\u05d0\u05d5\u05ea\u05e8"
"on": "\u05d6\u05d5\u05d4\u05d4"
},
"window": {
"off": "\u05e0\u05e1\u05d2\u05e8",
"off": "\u05e1\u05d2\u05d5\u05e8",
"on": "\u05e4\u05ea\u05d5\u05d7"
}
},
@@ -203,29 +203,33 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
class BluesoundPlayer(MediaPlayerEntity):
"""Representation of a Bluesound Player."""
_attr_media_content_type = MEDIA_TYPE_MUSIC
def __init__(self, hass, host, port=DEFAULT_PORT, name=None, init_callback=None):
def __init__(self, hass, host, port=None, name=None, init_callback=None):
"""Initialize the media player."""
self.host = host
self._hass = hass
self.port = port
self._polling_session = async_get_clientsession(hass)
self._polling_task = None # The actual polling task.
self._attr_name = name
self._name = name
self._icon = None
self._capture_items = []
self._services_items = []
self._preset_items = []
self._sync_status = {}
self._status = None
self._is_online = None
self._last_status_update = None
self._is_online = False
self._retry_remove = None
self._muted = False
self._master = None
self._group_name = None
self._bluesound_device_name = None
self._is_master = False
self._group_name = None
self._group_list = []
self._bluesound_device_name = None
self._init_callback = init_callback
if self.port is None:
self.port = DEFAULT_PORT
class _TimeoutException(Exception):
pass
@@ -248,12 +252,12 @@ class BluesoundPlayer(MediaPlayerEntity):
return None
self._sync_status = resp["SyncStatus"].copy()
if not self.name:
self._attr_name = self._sync_status.get("@name", self.host)
if not self._name:
self._name = self._sync_status.get("@name", self.host)
if not self._bluesound_device_name:
self._bluesound_device_name = self._sync_status.get("@name", self.host)
if not self.icon:
self._attr_icon = self._sync_status.get("@icon", self.host)
if not self._icon:
self._icon = self._sync_status.get("@icon", self.host)
master = self._sync_status.get("master")
if master is not None:
@@ -287,14 +291,14 @@ class BluesoundPlayer(MediaPlayerEntity):
await self.async_update_status()
except (asyncio.TimeoutError, ClientError, BluesoundPlayer._TimeoutException):
_LOGGER.info("Node %s is offline, retrying later", self.name)
_LOGGER.info("Node %s is offline, retrying later", self._name)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
self.start_polling()
except CancelledError:
_LOGGER.debug("Stopping the polling of node %s", self.name)
_LOGGER.debug("Stopping the polling of node %s", self._name)
except Exception:
_LOGGER.exception("Unexpected error in %s", self.name)
_LOGGER.exception("Unexpected error in %s", self._name)
raise
def start_polling(self):
@@ -398,7 +402,7 @@ class BluesoundPlayer(MediaPlayerEntity):
if response.status == HTTP_OK:
result = await response.text()
self._is_online = True
self._attr_media_position_updated_at = dt_util.utcnow()
self._last_status_update = dt_util.utcnow()
self._status = xmltodict.parse(result)["status"].copy()
group_name = self._status.get("groupName")
@@ -434,58 +438,11 @@ class BluesoundPlayer(MediaPlayerEntity):
except (asyncio.TimeoutError, ClientError):
self._is_online = False
self._attr_media_position_updated_at = None
self._last_status_update = None
self._status = None
self.async_write_ha_state()
_LOGGER.info("Client connection error, marking %s as offline", self.name)
_LOGGER.info("Client connection error, marking %s as offline", self._name)
raise
self.update_state_attr()
def update_state_attr(self):
"""Update state attributes."""
if self._status is None:
self._attr_state = STATE_OFF
self._attr_supported_features = 0
elif self.is_grouped and not self.is_master:
self._attr_state = STATE_GROUPED
self._attr_supported_features = (
SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
)
else:
status = self._status.get("state")
self._attr_state = STATE_IDLE
if status in ("pause", "stop"):
self._attr_state = STATE_PAUSED
elif status in ("stream", "play"):
self._attr_state = STATE_PLAYING
supported = SUPPORT_CLEAR_PLAYLIST
if self._status.get("indexing", "0") == "0":
supported = (
supported
| SUPPORT_PAUSE
| SUPPORT_PREVIOUS_TRACK
| SUPPORT_NEXT_TRACK
| SUPPORT_PLAY_MEDIA
| SUPPORT_STOP
| SUPPORT_PLAY
| SUPPORT_SELECT_SOURCE
| SUPPORT_SHUFFLE_SET
)
if self.volume_level is not None and self.volume_level >= 0:
supported = (
supported
| SUPPORT_VOLUME_STEP
| SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_MUTE
)
if self._status.get("canSeek", "") == "1":
supported = supported | SUPPORT_SEEK
self._attr_supported_features = supported
self._attr_extra_state_attributes = {}
if self._group_list:
self._attr_extra_state_attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
self._attr_extra_state_attributes[ATTR_MASTER] = self._is_master
self._attr_shuffle = self._status.get("shuffle", "0") == "1"
async def async_trigger_sync_on_all(self):
"""Trigger sync status update on all devices."""
@@ -585,6 +542,27 @@ class BluesoundPlayer(MediaPlayerEntity):
return self._services_items
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_MUSIC
@property
def state(self):
"""Return the state of the device."""
if self._status is None:
return STATE_OFF
if self.is_grouped and not self.is_master:
return STATE_GROUPED
status = self._status.get("state")
if status in ("pause", "stop"):
return STATE_PAUSED
if status in ("stream", "play"):
return STATE_PLAYING
return STATE_IDLE
@property
def media_title(self):
"""Title of current playing media."""
@@ -639,7 +617,7 @@ class BluesoundPlayer(MediaPlayerEntity):
return None
mediastate = self.state
if self.media_position_updated_at is None or mediastate == STATE_IDLE:
if self._last_status_update is None or mediastate == STATE_IDLE:
return None
position = self._status.get("secs")
@@ -648,9 +626,7 @@ class BluesoundPlayer(MediaPlayerEntity):
position = float(position)
if mediastate == STATE_PLAYING:
position += (
dt_util.utcnow() - self.media_position_updated_at
).total_seconds()
position += (dt_util.utcnow() - self._last_status_update).total_seconds()
return position
@@ -665,6 +641,11 @@ class BluesoundPlayer(MediaPlayerEntity):
return None
return float(duration)
@property
def media_position_updated_at(self):
"""Last time status was updated."""
return self._last_status_update
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
@@ -687,11 +668,21 @@ class BluesoundPlayer(MediaPlayerEntity):
mute = bool(int(mute))
return mute
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def bluesound_device_name(self):
"""Return the device name as returned by the device."""
return self._bluesound_device_name
@property
def icon(self):
"""Return the icon of the device."""
return self._icon
@property
def source_list(self):
"""List of available input sources."""
@@ -787,15 +778,58 @@ class BluesoundPlayer(MediaPlayerEntity):
return None
@property
def is_master(self) -> bool:
def supported_features(self):
"""Flag of media commands that are supported."""
if self._status is None:
return 0
if self.is_grouped and not self.is_master:
return SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
supported = SUPPORT_CLEAR_PLAYLIST
if self._status.get("indexing", "0") == "0":
supported = (
supported
| SUPPORT_PAUSE
| SUPPORT_PREVIOUS_TRACK
| SUPPORT_NEXT_TRACK
| SUPPORT_PLAY_MEDIA
| SUPPORT_STOP
| SUPPORT_PLAY
| SUPPORT_SELECT_SOURCE
| SUPPORT_SHUFFLE_SET
)
current_vol = self.volume_level
if current_vol is not None and current_vol >= 0:
supported = (
supported
| SUPPORT_VOLUME_STEP
| SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_MUTE
)
if self._status.get("canSeek", "") == "1":
supported = supported | SUPPORT_SEEK
return supported
@property
def is_master(self):
"""Return true if player is a coordinator."""
return self._is_master
@property
def is_grouped(self) -> bool:
def is_grouped(self):
"""Return true if player is a coordinator."""
return self._master is not None or self._is_master
@property
def shuffle(self):
"""Return true if shuffle is active."""
return self._status.get("shuffle", "0") == "1"
async def async_join(self, master):
"""Join the player to a group."""
master_device = [
@@ -815,6 +849,17 @@ class BluesoundPlayer(MediaPlayerEntity):
else:
_LOGGER.error("Master not found %s", master_device)
@property
def extra_state_attributes(self):
"""List members in group."""
attributes = {}
if self._group_list:
attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
attributes[ATTR_MASTER] = self._is_master
return attributes
def rebuild_bluesound_group(self):
"""Rebuild the list of entities in speaker group."""
if self._group_name is None:
@@ -335,11 +335,6 @@ class BMWConnectedDriveBaseEntity(Entity):
"manufacturer": vehicle.attributes.get("brand"),
}
@property
def extra_state_attributes(self):
"""Return the state attributes of the sensor."""
return self._attrs
def update_callback(self):
"""Schedule a state update."""
self.schedule_update_ha_state(True)
@@ -85,54 +85,38 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
def update(self):
"""Read new state data from the library."""
vehicle_state = self._vehicle.state
result = self._attrs.copy()
# device class opening: On means open, Off means closed
if self._attribute == "lids":
_LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
self._attr_state = not vehicle_state.all_lids_closed
if self._attribute == "windows":
self._attr_state = not vehicle_state.all_windows_closed
# device class lock: On means unlocked, Off means locked
if self._attribute == "door_lock_state":
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
self._attr_state = vehicle_state.door_lock_state not in [
LockState.LOCKED,
LockState.SECURED,
]
# device class light: On means light detected, Off means no light
if self._attribute == "lights_parking":
self._attr_state = vehicle_state.are_parking_lights_on
# device class problem: On means problem detected, Off means no problem
if self._attribute == "condition_based_services":
self._attr_state = not vehicle_state.are_all_cbs_ok
if self._attribute == "check_control_messages":
self._attr_state = vehicle_state.has_check_control_messages
# device class power: On means power detected, Off means no power
if self._attribute == "charging_status":
self._attr_state = vehicle_state.charging_status in [ChargingState.CHARGING]
# device class plug: On means device is plugged in,
# Off means device is unplugged
if self._attribute == "connection_status":
self._attr_state = vehicle_state.connection_status == "CONNECTED"
vehicle_state = self._vehicle.state
result = self._attrs.copy()
if self._attribute == "lids":
self._attr_is_on = not vehicle_state.all_lids_closed
for lid in vehicle_state.lids:
result[lid.name] = lid.state.value
elif self._attribute == "windows":
self._attr_is_on = not vehicle_state.all_windows_closed
for window in vehicle_state.windows:
result[window.name] = window.state.value
# device class lock: On means unlocked, Off means locked
elif self._attribute == "door_lock_state":
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
self._attr_is_on = vehicle_state.door_lock_state not in [
LockState.LOCKED,
LockState.SECURED,
]
result["door_lock_state"] = vehicle_state.door_lock_state.value
result["last_update_reason"] = vehicle_state.last_update_reason
# device class light: On means light detected, Off means no light
elif self._attribute == "lights_parking":
self._attr_is_on = vehicle_state.are_parking_lights_on
result["lights_parking"] = vehicle_state.parking_lights.value
# device class problem: On means problem detected, Off means no problem
elif self._attribute == "condition_based_services":
self._attr_is_on = not vehicle_state.are_all_cbs_ok
for report in vehicle_state.condition_based_services:
result.update(self._format_cbs_report(report))
elif self._attribute == "check_control_messages":
self._attr_is_on = vehicle_state.has_check_control_messages
check_control_messages = vehicle_state.check_control_messages
has_check_control_messages = vehicle_state.has_check_control_messages
if has_check_control_messages:
@@ -142,13 +126,18 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
result["check_control_messages"] = cbs_list
else:
result["check_control_messages"] = "OK"
# device class power: On means power detected, Off means no power
elif self._attribute == "charging_status":
self._attr_is_on = vehicle_state.charging_status in [ChargingState.CHARGING]
result["charging_status"] = vehicle_state.charging_status.value
result["last_charging_end_result"] = vehicle_state.last_charging_end_result
# device class plug: On means device is plugged in,
# Off means device is unplugged
elif self._attribute == "connection_status":
self._attr_is_on = vehicle_state.connection_status == "CONNECTED"
result["connection_status"] = vehicle_state.connection_status
self._attr_extra_state_attributes = sorted(result.items())
self._attr_extra_state_attributes = result
def _format_cbs_report(self, report):
result = {}
@@ -59,6 +59,7 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity):
def update(self):
"""Update state of the decvice tracker."""
self._attr_extra_state_attributes = self._attrs
self._location = (
self._vehicle.state.gps_position
if self._vehicle.state.is_vehicle_tracking_enabled
@@ -2,7 +2,7 @@
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"requirements": ["bimmer_connected==0.7.15"],
"requirements": ["bimmer_connected==0.7.18"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true,
"iot_class": "cloud_polling"
@@ -0,0 +1,38 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification incorrecte",
"pairing_failed": "L'appairage a \u00e9chou\u00e9\u00a0; veuillez v\u00e9rifier que le Bosch Smart Home Controller est en mode d'appairage (voyant clignotant) et que votre mot de passe est correct.",
"session_error": "Erreur de session\u00a0: l'API renvoie un r\u00e9sultat non-OK.",
"unknown": "Erreur inattendue"
},
"flow_title": "Bosch SHC: {name}",
"step": {
"confirm_discovery": {
"description": "Veuillez appuyer sur le bouton situ\u00e9 \u00e0 l'avant du Bosch Smart Home Controller jusqu'\u00e0 ce que le voyant commence \u00e0 clignoter.\n Pr\u00eat \u00e0 continuer \u00e0 configurer {model} @ {host} avec Home Assistant\u00a0?"
},
"credentials": {
"data": {
"password": "Mot de passe du contr\u00f4leur Smart Home"
}
},
"reauth_confirm": {
"description": "L'int\u00e9gration bosch_shc doit r\u00e9-authentifier votre compte",
"title": "R\u00e9authentification de l'int\u00e9gration"
},
"user": {
"data": {
"host": "H\u00f4te"
},
"description": "Configurez votre Bosch Smart Home Controller pour permettre la surveillance et le contr\u00f4le avec Home Assistant.",
"title": "Param\u00e8tres d'authentification SHC"
}
}
},
"title": "Bosch SHC"
}
+7 -1
View File
@@ -69,7 +69,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensors = [
BroadlinkSensor(device, monitored_condition)
for monitored_condition in sensor_data
if sensor_data[monitored_condition] != 0 or device.api.type == "A1"
if monitored_condition in SENSOR_TYPES
and (
# These devices have optional sensors.
# We don't create entities if the value is 0.
sensor_data[monitored_condition] != 0
or device.api.type not in {"RM4PRO", "RM4MINI"}
)
]
async_add_entities(sensors)
@@ -7,7 +7,7 @@
"error": {
"cannot_connect": "Verbindung fehlgeschlagen",
"snmp_error": "SNMP-Server deaktiviert oder Drucker nicht unterst\u00fctzt.",
"wrong_host": " Ung\u00fcltiger Hostname oder IP-Adresse"
"wrong_host": "Ung\u00fcltiger Hostname oder IP-Adresse"
},
"flow_title": "{model} {serial_number}",
"step": {
@@ -1,5 +1,11 @@
{
"config": {
"abort": {
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
},
"step": {
"user": {
"data": {
@@ -5,5 +5,5 @@
"on": "\u05de\u05d5\u05e4\u05e2\u05dc"
}
},
"title": "\u05dc\u05d5\u05bc\u05d7\u05b7 \u05e9\u05c1\u05b8\u05e0\u05b8\u05d4"
"title": "\u05dc\u05d5\u05d7 \u05e9\u05e0\u05d4"
}
+5 -1
View File
@@ -113,7 +113,6 @@ class CanarySensor(CoordinatorEntity, SensorEntity):
canary_sensor_type = SensorType.BATTERY
self._canary_type = canary_sensor_type
self._attr_state = self.reading
self._attr_unique_id = f"{device.device_id}_{sensor_type[0]}"
self._attr_device_info = {
"identifiers": {(DOMAIN, str(device.device_id))},
@@ -144,6 +143,11 @@ class CanarySensor(CoordinatorEntity, SensorEntity):
return None
@property
def state(self) -> float | None:
"""Return the state of the sensor."""
return self.reading
@property
def extra_state_attributes(self) -> dict[str, str] | None:
"""Return the state attributes."""
@@ -1,7 +1,7 @@
{
"config": {
"abort": {
"single_instance_allowed": "\u05e8\u05e7 \u05d4\u05d2\u05d3\u05e8\u05d4 \u05d0\u05d7\u05ea \u05e9\u05dc Google Cast \u05e0\u05d7\u05d5\u05e6\u05d4."
"single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea."
},
"error": {
"invalid_known_hosts": "\u05de\u05d0\u05e8\u05d7\u05d9\u05dd \u05d9\u05d3\u05d5\u05e2\u05d9\u05dd \u05d7\u05d9\u05d9\u05d1\u05d9\u05dd \u05dc\u05d4\u05d9\u05d5\u05ea \u05e8\u05e9\u05d9\u05de\u05ea \u05de\u05d0\u05e8\u05d7\u05d9\u05dd \u05d4\u05de\u05d5\u05e4\u05e8\u05d3\u05ea \u05d1\u05e4\u05e1\u05d9\u05e7\u05d9\u05dd."
+1 -1
View File
@@ -66,7 +66,7 @@ DEFAULT_FORECAST_TYPE = DAILY
DOMAIN = "climacell"
ATTRIBUTION = "Powered by ClimaCell"
MAX_REQUESTS_PER_DAY = 500
MAX_REQUESTS_PER_DAY = 100
CLEAR_CONDITIONS = {"night": ATTR_CONDITION_CLEAR_NIGHT, "day": ATTR_CONDITION_SUNNY}
@@ -25,7 +25,7 @@
"data": {
"timestep": "Minuten zwischen den Kurzvorhersagen"
},
"description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.",
"description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.",
"title": "Aktualisiere ClimaCell-Optionen"
}
}
@@ -143,6 +143,7 @@ class CloudOAuth2Implementation(config_entry_oauth2_flow.AbstractOAuth2Implement
async def _async_refresh_token(self, token: dict) -> dict:
"""Refresh a token."""
return await account_link.async_fetch_access_token(
new_token = await account_link.async_fetch_access_token(
self.hass.data[DOMAIN], self.service, token["refresh_token"]
)
return {**token, **new_token}
+1 -1
View File
@@ -2,7 +2,7 @@
"domain": "cloud",
"name": "Home Assistant Cloud",
"documentation": "https://www.home-assistant.io/integrations/cloud",
"requirements": ["hass-nabucasa==0.44.0"],
"requirements": ["hass-nabucasa==0.46.0"],
"dependencies": ["http", "webhook"],
"after_dependencies": ["google_assistant", "alexa"],
"codeowners": ["@home-assistant/cloud"],
@@ -1,6 +1,7 @@
{
"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.",
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
},
@@ -11,6 +12,11 @@
},
"flow_title": "Cloudflare: {name}",
"step": {
"reauth_confirm": {
"data": {
"api_token": "API token"
}
},
"records": {
"data": {
"records": "Z\u00e1znamy"
@@ -1,6 +1,7 @@
{
"config": {
"abort": {
"reauth_successful": "R\u00e9-authentification r\u00e9ussie",
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.",
"unknown": "Erreur inattendue"
},
@@ -11,6 +12,12 @@
},
"flow_title": "Cloudflare: {name}",
"step": {
"reauth_confirm": {
"data": {
"api_token": "Jeton API",
"description": "R\u00e9-authentifiez-vous avec votre compte Cloudflare."
}
},
"records": {
"data": {
"records": "Enregistrements"
+2 -1
View File
@@ -118,7 +118,8 @@ class CO2Sensor(update_coordinator.CoordinatorEntity[CO2SignalResponse], SensorE
def available(self) -> bool:
"""Return True if entity is available."""
return (
super().available and self._description.key in self.coordinator.data["data"]
super().available
and self.coordinator.data["data"].get(self._description.key) is not None
)
@property
@@ -0,0 +1,30 @@
{
"config": {
"abort": {
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno",
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
},
"error": {
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
},
"step": {
"coordinates": {
"data": {
"latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka",
"longitude": "Zem\u011bpisn\u00e1 d\u00e9lka"
}
},
"country": {
"data": {
"country_code": "K\u00f3d zem\u011b"
}
},
"user": {
"data": {
"api_key": "P\u0159\u00edstupov\u00fd token"
}
}
}
}
}
@@ -1,6 +1,8 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"api_ratelimit": "Limite de d\u00e9bit de l\u2019API d\u00e9pass\u00e9e",
"unknown": "Erreur inattendue"
},
"error": {
@@ -22,8 +24,10 @@
},
"user": {
"data": {
"api_key": "Token d'acc\u00e8s"
}
"api_key": "Token d'acc\u00e8s",
"location": "Obtenir des donn\u00e9es pour"
},
"description": "Visitez https://co2signal.com/ pour demander un jeton."
}
}
}
@@ -0,0 +1,34 @@
{
"config": {
"abort": {
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato",
"api_ratelimit": "Limite di frequenza API superato",
"unknown": "Errore imprevisto"
},
"error": {
"api_ratelimit": "Limite di frequenza API superato",
"invalid_auth": "Autenticazione non valida",
"unknown": "Errore imprevisto"
},
"step": {
"coordinates": {
"data": {
"latitude": "Latitudine",
"longitude": "Logitudine"
}
},
"country": {
"data": {
"country_code": "Prefisso internazionale"
}
},
"user": {
"data": {
"api_key": "Token di accesso",
"location": "Ottieni dati per"
},
"description": "Visita https://co2signal.com/ per richiedere un token."
}
}
}
}
@@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno"
},
"error": {
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
},
"step": {
"user": {
"data": {
"api_key": "Kl\u00ed\u010d API"
}
}
}
}
}
@@ -16,7 +16,7 @@
"currencies": "Kontostand W\u00e4hrungen",
"exchange_rates": "Wechselkurse"
},
"description": "Bitte gib die Details deines API-Schl\u00fcssels ein, wie von Coinbase bereitgestellt. Trenne mehrere W\u00e4hrungen mit einem Komma (z. B. \"BTC, EUR\")",
"description": "Bitte gib die Details deines API-Schl\u00fcssels ein, wie von Coinbase bereitgestellt.",
"title": "Coinbase API Schl\u00fcssel Details"
}
}
@@ -0,0 +1,41 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification incorrecte",
"unknown": "Erreur inattendue"
},
"step": {
"user": {
"data": {
"api_key": "cl\u00e9 API",
"api_token": "API secr\u00e8te",
"currencies": "Devises du solde du compte",
"exchange_rates": "Taux d'\u00e9change"
},
"description": "Veuillez saisir les d\u00e9tails de votre cl\u00e9 API tels que fournis par Coinbase.",
"title": "D\u00e9tails de la cl\u00e9 de l'API Coinbase"
}
}
},
"options": {
"error": {
"currency_unavaliable": "Un ou plusieurs des soldes de devises demand\u00e9s ne sont pas fournis par votre API Coinbase.",
"exchange_rate_unavaliable": "Un ou plusieurs des taux de change demand\u00e9s ne sont pas fournis par Coinbase.",
"unknown": "Erreur inattendue"
},
"step": {
"init": {
"data": {
"account_balance_currencies": "Soldes du portefeuille \u00e0 d\u00e9clarer.",
"exchange_base": "Devise de base pour les capteurs de taux de change.",
"exchange_rate_currencies": "Taux de change \u00e0 d\u00e9clarer."
},
"description": "Ajuster les options de Coinbase"
}
}
}
}
@@ -16,7 +16,7 @@
"currencies": "Valuta's van rekeningsaldo",
"exchange_rates": "Wisselkoersen"
},
"description": "Voer de gegevens van uw API-sleutel in zoals verstrekt door Coinbase. Scheidt meerdere valuta's met een komma (bijv. \"BTC, EUR\")",
"description": "Voer de gegevens van uw API-sleutel in zoals verstrekt door Coinbase.",
"title": "Coinbase API Sleutel Details"
}
}
@@ -1,9 +1,9 @@
{
"state": {
"_": {
"configure": "\u05d4\u05d2\u05d3\u05e8",
"configured": "\u05d4\u05d5\u05d2\u05d3\u05e8"
"configure": "\u05d4\u05d2\u05d3\u05e8\u05d4",
"configured": "\u05de\u05d5\u05d2\u05d3\u05e8"
}
},
"title": "\u05e7\u05d5\u05e0\u05e4\u05d9\u05d2\u05d5\u05e8\u05d8\u05d5\u05e8"
"title": "\u05e7\u05d5\u05d1\u05e2 \u05ea\u05e6\u05d5\u05e8\u05d4"
}
@@ -1,3 +1,3 @@
{
"title": "\u05e9\u05c2\u05b4\u05d9\u05d7\u05b8\u05d4"
"title": "\u05e9\u05d9\u05d7\u05d4"
}
@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Ce pays est d\u00e9j\u00e0 configur\u00e9."
"already_configured": "Ce pays est d\u00e9j\u00e0 configur\u00e9.",
"cannot_connect": "\u00c9chec de connexion"
},
"step": {
"user": {
@@ -6,11 +6,11 @@
},
"state": {
"_": {
"closed": "\u05e0\u05e1\u05d2\u05e8",
"closed": "\u05e1\u05d2\u05d5\u05e8",
"closing": "\u05e1\u05d5\u05d2\u05e8",
"open": "\u05e4\u05ea\u05d5\u05d7",
"opening": "\u05e4\u05d5\u05ea\u05d7",
"stopped": "\u05e2\u05e6\u05d5\u05e8"
"stopped": "\u05e2\u05e6\u05e8"
}
},
"title": "\u05d5\u05d9\u05dc\u05d5\u05df"
@@ -23,6 +23,7 @@ from homeassistant.const import (
ATTR_TEMPERATURE,
ATTR_VOLTAGE,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_POWER,
@@ -40,6 +41,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.util import dt as dt_util
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
from .deconz_device import DeconzDevice
@@ -51,6 +53,7 @@ ATTR_DAYLIGHT = "daylight"
ATTR_EVENT_ID = "event_id"
DEVICE_CLASS = {
Consumption: DEVICE_CLASS_ENERGY,
Humidity: DEVICE_CLASS_HUMIDITY,
LightLevel: DEVICE_CLASS_ILLUMINANCE,
Power: DEVICE_CLASS_POWER,
@@ -65,6 +68,7 @@ ICON = {
}
STATE_CLASS = {
Consumption: STATE_CLASS_MEASUREMENT,
Humidity: STATE_CLASS_MEASUREMENT,
Pressure: STATE_CLASS_MEASUREMENT,
Temperature: STATE_CLASS_MEASUREMENT,
@@ -158,6 +162,9 @@ class DeconzSensor(DeconzDevice, SensorEntity):
self._attr_state_class = STATE_CLASS.get(type(self._device))
self._attr_unit_of_measurement = UNIT_OF_MEASUREMENT.get(type(self._device))
if device.type in Consumption.ZHATYPE:
self._attr_last_reset = dt_util.utc_from_timestamp(0)
@callback
def async_update_callback(self, force_update=False):
"""Update the sensor's state."""
@@ -0,0 +1,9 @@
{
"state": {
"demo__speed": {
"light_speed": "Vitesse de la lumi\u00e8re",
"ludicrous_speed": "Vitesse ridicule",
"ridiculous_speed": "Vitesse ridicule"
}
}
}
@@ -5,5 +5,5 @@
"not_home": "\u05dc\u05d0 \u05d1\u05d1\u05d9\u05ea"
}
},
"title": "\u05de\u05e2\u05e7\u05d1 \u05de\u05db\u05e9\u05d9\u05e8"
"title": "\u05de\u05e2\u05e7\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd"
}
@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "\u00da\u010det je ji\u017e nastaven"
"already_configured": "\u00da\u010det je ji\u017e nastaven",
"reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9"
},
"error": {
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
@@ -1,10 +1,12 @@
{
"config": {
"abort": {
"already_configured": "Cette unit\u00e9 centrale Home Control est d\u00e9j\u00e0 utilis\u00e9e."
"already_configured": "Cette unit\u00e9 centrale Home Control est d\u00e9j\u00e0 utilis\u00e9e.",
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
},
"error": {
"invalid_auth": "Authentification invalide"
"invalid_auth": "Authentification invalide",
"reauth_failed": "Veuillez utiliser le m\u00eame utilisateur mydevolo que pr\u00e9c\u00e9demment."
},
"step": {
"user": {
@@ -11,7 +11,7 @@
"user": {
"data": {
"password": "\u05e1\u05d9\u05e1\u05de\u05d4",
"username": "\u05d3\u05d5\u05d0\u05e8 \u05d0\u05dc\u05e7\u05d8\u05e8\u05d5\u05e0\u05d9/ \u05de\u05d6\u05d4\u05d4 devolo"
"username": "\u05d3\u05d5\u05d0\"\u05dc / \u05de\u05d6\u05d4\u05d4 devolo"
}
},
"zeroconf_confirm": {
@@ -9,6 +9,14 @@
},
"flow_title": "{name}",
"step": {
"ssdp_confirm": {
"data": {
"many": "\u05e8\u05d9\u05e7\u05d9\u05dd",
"one": "\u05e8\u05d9\u05e7",
"other": "",
"two": "\u05e8\u05d9\u05e7\u05d9\u05dd"
}
},
"user": {
"data": {
"host": "\u05de\u05d0\u05e8\u05d7"
+8 -1
View File
@@ -157,7 +157,9 @@ async def async_setup_entry(
update_entities_telegram({})
# throttle reconnect attempts
await asyncio.sleep(entry.data[CONF_RECONNECT_INTERVAL])
await asyncio.sleep(
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
)
except (serial.serialutil.SerialException, OSError):
# Log any error while establishing connection and drop to retry
@@ -165,6 +167,11 @@ async def async_setup_entry(
LOGGER.exception("Error connecting to DSMR")
transport = None
protocol = None
# throttle reconnect attempts
await asyncio.sleep(
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
)
except CancelledError:
if stop_listener:
stop_listener() # pylint: disable=not-callable
@@ -1,23 +1,46 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"cannot_communicate": "\u00c9chec de la communication",
"cannot_connect": "\u00c9chec de connexion"
},
"error": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"cannot_communicate": "\u00c9chec de la communication",
"cannot_connect": "\u00c9chec de connexion",
"one": "Vide",
"other": "Vide"
},
"step": {
"one": "",
"other": "Autre",
"setup_network": {
"data": {
"dsmr_version": "S\u00e9lectionner la version DSMR",
"host": "H\u00f4te",
"port": "Port"
},
"title": "S\u00e9lectionner l'adresse de connexion"
},
"setup_serial": {
"data": {
"dsmr_version": "S\u00e9lectionner la version DSMR",
"port": "S\u00e9lectionner un appareil"
},
"title": "Appareil"
},
"setup_serial_manual_path": {
"data": {
"port": "Chemin du p\u00e9riph\u00e9rique USB"
},
"title": "Chemin"
},
"user": {
"data": {
"type": "Type de connexion"
},
"title": "S\u00e9lectionner le type de connexion"
}
}
},
File diff suppressed because it is too large Load Diff
+14 -60
View File
@@ -4,39 +4,27 @@ from homeassistant.components.sensor import SensorEntity
from homeassistant.core import callback
from homeassistant.util import slugify
from .definitions import DEFINITIONS
from .definitions import SENSORS, DSMRReaderSensorEntityDescription
DOMAIN = "dsmr_reader"
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up DSMR Reader sensors."""
sensors = []
for topic in DEFINITIONS:
sensors.append(DSMRSensor(topic))
async_add_entities(sensors)
async_add_entities(DSMRSensor(description) for description in SENSORS)
class DSMRSensor(SensorEntity):
"""Representation of a DSMR sensor that is updated via MQTT."""
def __init__(self, topic):
entity_description: DSMRReaderSensorEntityDescription
def __init__(self, description: DSMRReaderSensorEntityDescription) -> None:
"""Initialize the sensor."""
self.entity_description = description
self._definition = DEFINITIONS[topic]
self._entity_id = slugify(topic.replace("/", "_"))
self._topic = topic
self._name = self._definition.get("name", topic.split("/")[-1])
self._device_class = self._definition.get("device_class")
self._enable_default = self._definition.get("enable_default")
self._unit_of_measurement = self._definition.get("unit")
self._icon = self._definition.get("icon")
self._transform = self._definition.get("transform")
self._state = None
slug = slugify(description.key.replace("/", "_"))
self.entity_id = f"sensor.{slug}"
async def async_added_to_hass(self):
"""Subscribe to MQTT events."""
@@ -44,47 +32,13 @@ class DSMRSensor(SensorEntity):
@callback
def message_received(message):
"""Handle new MQTT messages."""
if self._transform is not None:
self._state = self._transform(message.payload)
if self.entity_description.state is not None:
self._attr_state = self.entity_description.state(message.payload)
else:
self._state = message.payload
self._attr_state = message.payload
self.async_write_ha_state()
await mqtt.async_subscribe(self.hass, self._topic, message_received, 1)
@property
def name(self):
"""Return the name of the sensor supplied in constructor."""
return self._name
@property
def entity_id(self):
"""Return the entity ID for this sensor."""
return f"sensor.{self._entity_id}"
@property
def state(self):
"""Return the current state of the entity."""
return self._state
@property
def device_class(self):
"""Return the device_class of this sensor."""
return self._device_class
@property
def unit_of_measurement(self):
"""Return the unit_of_measurement of this sensor."""
return self._unit_of_measurement
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return self._enable_default
@property
def icon(self):
"""Return the icon of this sensor."""
return self._icon
await mqtt.async_subscribe(
self.hass, self.entity_description.key, message_received, 1
)
+3 -1
View File
@@ -122,10 +122,12 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
)
SENSOR_TYPE_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_MONITORED_VARIABLES): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]
cv.ensure_list, [vol.In(SENSOR_TYPE_KEYS)]
),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
-1
View File
@@ -223,7 +223,6 @@ SENSOR_TYPES = {
None,
4,
DEVICE_CLASS_TEMPERATURE,
None,
],
"Flame": ["Flame", None, "mdi:toggle-switch", 2, None],
"PowerEnergyConsumptionHeatingCircuit": [
@@ -10,7 +10,7 @@
"flow_title": "{name}",
"step": {
"confirm": {
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {model} ({host})?"
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name} ({host})?"
},
"user": {
"data": {

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