Compare commits

...

187 Commits

Author SHA1 Message Date
Franck Nijhof
267dfac737 2024.7.3 (#122194) 2024-07-19 19:38:08 +02:00
Franck Nijhof
a08ffdc8d3 Bump version to 2024.7.3 2024-07-19 18:50:21 +02:00
Mr. Bubbles
1ef4332af6 Fix KeyError in config flow of Bring integration (#122136) 2024-07-19 18:49:45 +02:00
Marc Mueller
d0d2fd7918 Update yt-dlp to 2024.07.16 (#122124) 2024-07-19 18:49:41 +02:00
Steven B.
c518c4756b Bump tplink dependency python-kasa to 0.7.0.5 (#122119) 2024-07-19 18:49:38 +02:00
Shay Levy
a3a99cc631 Prevent connecting to a Shelly device that is already connected (#122105) 2024-07-19 18:49:35 +02:00
Steven B.
977a55e3b8 Update tplink device config during reauth flow (#122089) 2024-07-19 18:49:31 +02:00
Harry Martland
002db3c3e9 Fix hive not updating when boosting (#122042)
* fixes issue where hive does not update when boosting

* formats files
2024-07-19 18:49:28 +02:00
Robert Svensson
d9e44bab69 Mark UniFi power cycle button as unavailable if PoE is not enabled on port (#122035) 2024-07-19 18:49:25 +02:00
G Johansson
4b93fc61b5 Bump python-holidays to 0.53 (#122021) 2024-07-19 18:49:21 +02:00
J. Nick Koston
214b5efd72 Narrow sqlite database corruption check to ensure disk image is malformed (#121947)
* Narrow sqlite database corruption check to ensure disk image is malformed

The database corruption check would also replace the database when it
locked externally instead of only when its malformed.

This was discovered in https://github.com/home-assistant/core/issues/121909#issuecomment-2227409124
when a user did a manual index creation while HA was online

* tweak

* tweak

* fix

* fix
2024-07-19 18:49:16 +02:00
Maciej Bieniek
9bd822d693 Fix configuration_url for Shelly device using IPv6 (#121939)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-07-19 18:48:32 +02:00
Tomasz Gorochowik
bf89eaae25 Fix enigma2 mute (#121928) 2024-07-19 18:42:44 +02:00
Scott K Logan
f9b359ae30 Fix rainforest_raven closing device due to timeout (#121905) 2024-07-19 18:42:41 +02:00
mvn23
24ed003471 Fix opentherm_gw availability (#121892) 2024-07-19 18:42:38 +02:00
J. Nick Koston
a835750252 Log add/remove index complete at the same level as when it starts (#121852) 2024-07-19 18:42:35 +02:00
Avi Miller
ad07bdb62b Bump aiolifx to 1.0.5 (#121824) 2024-07-19 18:42:32 +02:00
Avi Miller
41104324ec Bump aiolifx to 1.0.4 (#121267) 2024-07-19 18:42:28 +02:00
ollo69
e9344ae101 Bump PySwitchbot to 0.48.1 (#121804) 2024-07-19 18:40:25 +02:00
starkillerOG
56a9167ed2 Reolink media second lens (#121800)
DUO lens camera distinguish between lenses for media playback
2024-07-19 18:40:22 +02:00
Jan Bouwhuis
e0b90c4b36 Fix alexa does to check current_position correctly when handling cover range changes (#121798) 2024-07-19 18:40:19 +02:00
Jan-Philipp Benecke
976902f22c Add missing translations to Roborock (#121796) 2024-07-19 18:40:16 +02:00
Steven B
0f69c58ba9 Bump python-kasa to 0.7.0.4 (#121791) 2024-07-19 18:40:13 +02:00
Glenn Waters
1e6c96c6eb Use async_connect in newly bumped 0.5.8 UPB library (#121789) 2024-07-19 18:40:09 +02:00
J. Nick Koston
63b14d14c1 Add some missing tplink ouis (#121785) 2024-07-19 18:40:06 +02:00
Josef Zweck
6aaaba6419 Bump pytedee_async to 0.2.20 (#121783) 2024-07-19 18:40:03 +02:00
Allen Porter
3b8e736fe3 Pin mashumaro version >= 3.13.1 for python 3.12.4 compatibility. (#121782)
Pin mashumaro version for python 3.12.4 compatibility.
2024-07-19 18:40:00 +02:00
tronikos
68841b3d8a Bump opower to 0.5.2 to fix 403 forbidden errors for users with multiple accounts (#121762) 2024-07-19 18:39:56 +02:00
Abílio Costa
3d8afe7cb8 Update Idasen Desk library to 2.6.2 (#121729) 2024-07-19 18:29:45 +02:00
Robert Svensson
8595242142 Fix bad access to UniFi runtime_data when not assigned (#121725)
* Fix bad access to runtime_data when not assigned

* Fix review comment

* Clean up if statements
2024-07-19 18:29:42 +02:00
Joost Lekkerkerker
ebe7bc0686 Bump knocki to 0.3.1 (#121717) 2024-07-19 18:29:39 +02:00
J. Nick Koston
4ab180f016 Fix update happening too early in unifiprotect (#121714) 2024-07-19 18:29:36 +02:00
Mr. Bubbles
372649069e Bump pyloadapi to v1.3.2 (#121709) 2024-07-19 18:29:33 +02:00
Joost Lekkerkerker
98df46f3ea Bump knocki to 0.3.0 (#121704) 2024-07-19 18:29:30 +02:00
Steven B
269fb23527 Fix tplink bug changing color temp on bulbs with light effects (#121696) 2024-07-19 18:29:27 +02:00
Sid
ad5cbf0da6 Allow enigma2 devices to use different source bouquets (#121686) 2024-07-19 18:29:24 +02:00
Lucas Mindêllo de Andrade
10cdf64f90 Bump sunweg 3.0.2 (#121684) 2024-07-19 18:29:21 +02:00
Tomek Porozynski
ec8e639804 Update Supla async_set_cover_position to use "REVEAL_PARTIALLY" (#121663) 2024-07-19 18:29:17 +02:00
Jan Stienstra
37f37f7287 Retain Jellyfin config flow input on connection issue (#121618) 2024-07-19 18:29:13 +02:00
Mr. Bubbles
ef7d68bfd6 Fix reauth error and exception in ista EcoTrend integration (#121482) 2024-07-19 18:29:08 +02:00
Franck Nijhof
058b012e6c 2024.7.2 (#121671) 2024-07-10 13:18:48 +02:00
Franck Nijhof
71370758a8 Bump version to 2024.7.2 2024-07-10 12:06:02 +02:00
Franck Nijhof
38a44676eb Block variable <=3.4.4 custom integration from breaking the recorder (#121670) 2024-07-10 12:01:11 +02:00
Marcel van der Veldt
05ce3d35b3 Matter lock state follow-up (#121669) 2024-07-10 12:01:08 +02:00
Marcel van der Veldt
2151086b0a Fix state for Matter Locks (including optional door sensor) (#121665) 2024-07-10 12:01:05 +02:00
Franck Nijhof
9c83af3789 Block places <=2.7.0 custom integration from breaking the recorder (#121662) 2024-07-10 12:01:01 +02:00
tronikos
ac3eecc879 Handle errors in Fully Kiosk camera (#121659) 2024-07-10 12:00:58 +02:00
Franck Nijhof
ec0910e3da Block icloud3 custom integration from breaking the recorder (#121658) 2024-07-10 12:00:55 +02:00
Maikel Punie
fd0c26cd56 Small fix in velbus cover for the assumed states (#121656) 2024-07-10 12:00:52 +02:00
Paul Bottein
a4c5dee082 Update frontend to 20240710.0 (#121651) 2024-07-10 12:00:48 +02:00
Joakim Plate
37c09dbdb6 Allow ambilight when we have connection (philips_js) (#121620) 2024-07-10 12:00:45 +02:00
Arie Catsman
73d1973625 Bump pyenphase to 1.20.6 (#121583)
bump pyenphase to 1.20.6
2024-07-10 12:00:42 +02:00
Glenn Waters
5a04a886cf Fix upb config flow connect (#121571) 2024-07-10 12:00:39 +02:00
Franck Nijhof
50802f84f0 Update tailscale to 0.6.1 (#121557) 2024-07-10 12:00:36 +02:00
Franck Nijhof
138b68ecc0 Update vehicle to 2.2.2 (#121556) 2024-07-10 12:00:33 +02:00
Christoph
e0b01ee94e Remove homematic state_class from GAS_POWER sensor (#121533) 2024-07-10 12:00:30 +02:00
J. Nick Koston
4f2c3df518 Fix person tracking in unifiprotect (#121528) 2024-07-10 12:00:26 +02:00
Paulus Schoutsen
51a6bb1c22 Include hass device ID in mobile app get_config webhook (#121496) 2024-07-10 12:00:23 +02:00
Ovidiu D. Nițan
6bf9ec69f3 Bump xiaomi-ble to 0.30.2 (#121471) 2024-07-10 12:00:19 +02:00
Joost Lekkerkerker
21309eeb5d Bump xiaomi-ble to 0.30.1 (#120743) 2024-07-10 12:00:14 +02:00
J. Nick Koston
0a1b46c52f Bump yalexs to 6.4.2 (#121467) 2024-07-10 11:52:56 +02:00
Jason R. Coombs
9512f9eec3 Bump jaraco.abode to 5.2.1 (#121446)
Bump dependency on jaraco.abode to 5.2.1.

Closes #121300
2024-07-10 11:52:53 +02:00
jan iversen
ab94422c18 Bump pymodbus to 3.6.9 (#121445)
Bump pymodbus 3.6.9.
2024-07-10 11:52:50 +02:00
Joost Lekkerkerker
ec105e5265 Fix Mealie URL field (#121434) 2024-07-10 11:52:47 +02:00
Joost Lekkerkerker
cadd8521ae Sort mealie mealplans (#121433) 2024-07-10 11:52:43 +02:00
Joost Lekkerkerker
8825c50671 Fix MPD config flow (#121431) 2024-07-10 11:52:40 +02:00
Michael
a72cc3c248 Allow current empty feeds to be configured in Feedreader (#121421) 2024-07-10 11:52:37 +02:00
G Johansson
780f7254c1 Fix feature flag in climate (#121398) 2024-07-10 11:52:34 +02:00
G Johansson
37621e77ae Fix timezone issue in smhi weather (#121389) 2024-07-10 11:52:31 +02:00
G Johansson
8017386c73 Fix unnecessary logging of turn on/off feature flags in Climate (#121387) 2024-07-10 11:52:28 +02:00
G Johansson
a5f4c25a2c Bump psutil to 6.0.0 (#121385) 2024-07-10 11:52:25 +02:00
Brett Adams
1d7bddf449 Fix initial Wall Connector values in Tessie (#121353) 2024-07-10 11:52:21 +02:00
Luke Lashley
711bdaf373 Bump anova-wifi to 0.17.0 (#121337)
* Bump anova-wifi to 0.16.0

* Bump to .17
2024-07-10 11:52:18 +02:00
Jan Temešinko
803d9c5a8e Fix ombi configuration validation (#121314) 2024-07-10 11:52:15 +02:00
Rasmus Lundsgaard
1133c41fa8 Fix empty list in kodi media_player (#121250)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2024-07-10 11:52:12 +02:00
Alan
a06af7ee93 LLM to handle int attributes (#121037) 2024-07-10 11:52:08 +02:00
Robert C. Maehl
c54717707e Direct Users to App-Specific Passwords for iCloud integration to prevent MFA spam (#120945)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2024-07-10 11:52:05 +02:00
J. Nick Koston
440d83d754 Remove legacy foreign key constraint from sqlite states table (#120779) 2024-07-10 11:51:59 +02:00
Franck Nijhof
1cf62916a7 2024.7.1 (#121289) 2024-07-05 17:25:40 +02:00
Bram Kragten
e3958d4adb Update frontend to 20240705.0 (#121295) 2024-07-05 15:04:01 +02:00
Franck Nijhof
dfccd4abf9 Bump version to 2024.7.1 2024-07-05 11:21:36 +02:00
Steven B
994d6f552c Fix tplink light effect behaviour when activating a scene (#121288) 2024-07-05 11:21:07 +02:00
G Johansson
b015611a2a Bump python-holidays to 0.52 (#121283) 2024-07-05 11:21:04 +02:00
Shay Levy
f4e362c5d0 Bump aiowebostv to 0.4.2 (#121258) 2024-07-05 11:21:00 +02:00
Jordi
a542236614 Bump aioaquacell to 0.1.8 (#121253) 2024-07-05 11:20:57 +02:00
Shay Levy
651439ea06 Fix WebOS TV media player status when OFF after IDLE (#121251) 2024-07-05 11:20:54 +02:00
Robert Resch
eda450838e Bump deebot-client to 8.1.1 (#121241) 2024-07-05 11:20:50 +02:00
hahn-th
b906daa493 Revert Homematic IP Cloud unique ID changes (#121231) 2024-07-05 11:20:47 +02:00
Thomas55555
ac668dce7d Fix work area sensor in Husqvarna Automower (#121228) 2024-07-05 11:20:44 +02:00
Luke Lashley
1bb4d62a3e Bump anova-wifi to 0.15.0 (#121222) 2024-07-05 11:20:40 +02:00
Marcel van der Veldt
0b970f9a85 Listen for attribute changes of OnOff cluster in appliances (#121198) 2024-07-05 11:20:37 +02:00
Marcel van der Veldt
d2b695e7b5 Fix Matter light discovery schema for DimmerSwitch (#121185) 2024-07-05 11:20:34 +02:00
Steven B
b2f23c1a5e Bump python-kasa to 0.7.0.3 (#121183) 2024-07-05 11:20:31 +02:00
Gerben Jongerius
f403afb012 Bump youless library version 2.1.2 (#121181) 2024-07-05 11:20:27 +02:00
Maciej Bieniek
ee276aff44 Fix pulse counter frequency sensors for Shelly Plus Uni (#121178)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-07-05 11:20:23 +02:00
Maikel Punie
0acd1dc5d1 Bump velbusaio to 2024.7.5 (#121156)
* Bump velbusaio to 2024.7.4

* bump to 2024.7.5 to remove print functions
2024-07-05 11:20:20 +02:00
Martin Weinelt
21815e1621 Fix broken pathlib import in august integration (#121135) 2024-07-05 11:20:17 +02:00
J. Nick Koston
15933bb16f Bump inkbird-ble to 0.5.8 (#121134) 2024-07-05 11:20:13 +02:00
Pavel Skuratovich
930cd0dc50 Starline: Fix "Error updating SLNet token" message in Log (#121122)
Fixes https://github.com/home-assistant/core/issues/116715
2024-07-05 11:20:10 +02:00
Christoph
fc4af48179 Fix HmIP-ESI GAS sensor DeviceClass (#121106)
should be SensorDeviceClass:GAS instead of SensorDeviceClass:VOLUME to be supported in the Energy Dashboard
2024-07-05 11:20:07 +02:00
Marcel van der Veldt
ba1cf84ea5 Fix locking/unlocking transition state in Matter lock platform (#121099) 2024-07-05 11:20:04 +02:00
dougiteixeira
59cf01e252 Add device class translations in Random (#120890) 2024-07-05 11:20:00 +02:00
Allen Porter
46e681f4fc Improve redaction for stream error messages (#120867) 2024-07-05 11:19:56 +02:00
Franck Nijhof
2b64f6f2ab 2024.7.0 (#120579) 2024-07-03 18:52:01 +02:00
Franck Nijhof
1080a4ef1e Bump version to 2024.7.0 2024-07-03 17:55:58 +02:00
Franck Nijhof
d94b36cfbb Bump version to 2024.7.0b11 2024-07-03 17:29:08 +02:00
Marcel van der Veldt
85168239cd Matter fix Energy sensor discovery schemas (#121080) 2024-07-03 17:28:59 +02:00
Robert Resch
547b24ce58 Bump deebot-client to 8.1.0 (#121078) 2024-07-03 17:28:56 +02:00
Michael Hansen
e8bcb3e11e Bump intents to 2024.7.3 (#121076) 2024-07-03 17:28:53 +02:00
Marcel van der Veldt
c89a9b5ce0 Bump python-matter-server to 6.2.2 (#121072) 2024-07-03 17:28:49 +02:00
Robert Svensson
13631250b4 Bump axis to v62 (#121070) 2024-07-03 17:28:46 +02:00
Bram Kragten
6621cf475a Update frontend to 20240703.0 (#121063) 2024-07-03 17:28:43 +02:00
Anton Tolchanov
36e74cd9a6 Generate Prometheus metrics in an executor job (#121058) 2024-07-03 17:28:38 +02:00
Kevin Stillhammer
16827ea09e Bump here-transit to 1.2.1 (#120900) 2024-07-03 17:27:10 +02:00
Kevin Stillhammer
c4956b66b0 Bump here-routing to 1.0.1 (#120877) 2024-07-03 17:27:06 +02:00
Franck Nijhof
84204c38be Bump version to 2024.7.0b10 2024-07-03 08:59:52 +02:00
J. Nick Koston
febd1a3772 Bump inkbird-ble to 0.5.7 (#121039)
changelog: https://github.com/Bluetooth-Devices/inkbird-ble/compare/v0.5.6...v0.5.7
2024-07-03 08:59:45 +02:00
Allen Porter
1665cb40ac Bump gcal_sync to 6.1.4 (#120941) 2024-07-03 08:59:41 +02:00
Franck Nijhof
1b9f27fab7 Bump version to 2024.7.0b9 2024-07-02 22:15:17 +02:00
Bram Kragten
d1e76d5c3c Update frontend to 20240702.0 (#121032) 2024-07-02 22:15:09 +02:00
Jan-Philipp Benecke
4377f4cbea Temporarily set apprise log level to debug in tests (#121029)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-07-02 22:15:05 +02:00
Franck Nijhof
6b045a7d7b Bump version to 2024.7.0b8 2024-07-02 21:09:55 +02:00
Marcel van der Veldt
1fa6972a66 Handle mains power for Matter appliances (#121023) 2024-07-02 21:09:39 +02:00
Marcel van der Veldt
b3e833f677 Fix setting target temperature for single setpoint Matter thermostat (#121011) 2024-07-02 21:09:36 +02:00
starkillerOG
807ed0ce10 Do not hold core startup with reolink firmware check task (#120985) 2024-07-02 21:09:32 +02:00
starkillerOG
5cb41106b5 Reolink replace automatic removal of devices by manual removal (#120981)
Co-authored-by: Robert Resch <robert@resch.dev>
2024-07-02 21:09:28 +02:00
Joost Lekkerkerker
98a2e46d4a Remove Aladdin Connect integration (#120980) 2024-07-02 21:08:14 +02:00
Joost Lekkerkerker
24afbde79e Bump yt-dlp to 2024.07.01 (#120978) 2024-07-02 21:05:52 +02:00
starkillerOG
65d2ca53cb Bump reolink-aio to 0.9.4 (#120964) 2024-07-02 21:05:49 +02:00
Joost Lekkerkerker
23b905b422 Bump airgradient to 0.6.1 (#120962) 2024-07-02 21:05:46 +02:00
Joost Lekkerkerker
de458493f8 Fix missing airgradient string (#120957) 2024-07-02 21:05:42 +02:00
Erik Montnemery
efd3252849 Create log files in an executor thread (#120912) 2024-07-02 21:05:39 +02:00
Jesse Hills
3b6acd5380 [ESPHome] Disable dashboard based update entities by default (#120907)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2024-07-02 21:05:36 +02:00
Teemu R
1e6dc74812 Minor polishing for tplink (#120868) 2024-07-02 21:05:22 +02:00
Franck Nijhof
74687f3b60 Bump version to 2024.7.0b7 2024-07-01 19:44:51 +02:00
Markus Jacobsen
2f307d6a8a Fix Bang & Olufsen jumping volume bar (#120946) 2024-07-01 19:44:37 +02:00
J. Nick Koston
d8f55763c5 Downgrade logging previously reported asyncio block to debug (#120942) 2024-07-01 19:44:34 +02:00
Steven B
4b2be448f0 Bump python-kasa to 0.7.0.2 (#120940) 2024-07-01 19:44:31 +02:00
Marcel van der Veldt
8a7e2c05a5 Mark dry/fan-only climate modes as supported for Panasonic room air conditioner (#120939) 2024-07-01 19:44:28 +02:00
Paulus Schoutsen
887ab1dc58 Bump openai to 1.35.1 (#120926)
Bump openai to 1.35.7
2024-07-01 19:44:25 +02:00
Jan Bouwhuis
a787ce8633 Bump incomfort-client dependency to 0.6.3 (#120913) 2024-07-01 19:44:22 +02:00
Robert Resch
88ed43c779 Improve add user error messages (#120909) 2024-07-01 19:44:19 +02:00
dougiteixeira
16d7764f18 Add missing translations for device class in Template (#120893) 2024-07-01 19:44:15 +02:00
dougiteixeira
a0f8012f48 Add missing translations for device class in SQL (#120892) 2024-07-01 19:44:12 +02:00
dougiteixeira
5a052feb87 Add missing translations for device class in Scrape (#120891) 2024-07-01 19:44:09 +02:00
Allen Porter
779a7ddaa2 Bump ical to 8.1.1 (#120888) 2024-07-01 19:44:06 +02:00
Shay Levy
a9740faeda Fix Shelly device shutdown (#120881) 2024-07-01 19:44:03 +02:00
Thomas55555
3a0e85beb8 Bump aioautomower to 2024.6.4 (#120875) 2024-07-01 19:43:59 +02:00
Yuxin Wang
c19fb35d02 Add handling for different STATFLAG formats in APCUPSD (#120870)
* Add handling for different STATFLAG formats

* Just use removesuffix
2024-07-01 19:43:56 +02:00
J. Nick Koston
6f716c1753 Fix publish cancellation handling in MQTT (#120826) 2024-07-01 19:43:53 +02:00
Jan Bouwhuis
40384b9acd Split mqtt client tests (#120636) 2024-07-01 19:43:50 +02:00
Jan Bouwhuis
3bbf8df6d6 Cleanup mqtt platform tests part 4 (init) (#120574) 2024-07-01 19:43:46 +02:00
Franck Nijhof
14af3661f3 Bump version to 2024.7.0b6 2024-06-30 20:42:10 +02:00
Michael
af733425c2 Bump pyfritzhome to 0.6.12 (#120861) 2024-06-30 20:41:51 +02:00
Allen Porter
4fc89e8861 Rollback PyFlume to 0.6.5 (#120846) 2024-06-30 20:39:47 +02:00
Tsvi Mostovicz
bcec268c04 Fix Jewish calendar unique id move to entity (#120842) 2024-06-30 20:39:44 +02:00
Shay Levy
becf9fcce2 Bump aiowebostv to 0.4.1 (#120838) 2024-06-30 20:39:41 +02:00
Etienne Soufflet
ad9e0ef8e4 Fix Tado fan mode (#120809) 2024-06-30 20:39:38 +02:00
Simon Lamon
f58eafe8fc Fix routes with transfer in nmbs integration (#120808) 2024-06-30 20:39:35 +02:00
mkmer
a7246400b3 Allow EM heat on from any mode in Honeywell (#120750) 2024-06-30 20:39:32 +02:00
Joost Lekkerkerker
38a30b343d Bump pizzapi to 0.0.6 (#120691) 2024-06-30 20:39:28 +02:00
Franck Nijhof
08a0eaf184 Bump version to 2024.7.0b5 2024-06-29 17:51:45 +02:00
Joost Lekkerkerker
3ee8f6edba Use meal note as fallback in Mealie (#120828) 2024-06-29 17:51:34 +02:00
Joost Lekkerkerker
e866417c01 Add icons to Airgradient (#120820) 2024-06-29 17:51:31 +02:00
Joost Lekkerkerker
05c63eb884 Bump python-opensky to 1.0.1 (#120818) 2024-06-29 17:51:28 +02:00
Joost Lekkerkerker
bb52bfd73d Add unique id to Mealie config entry (#120816) 2024-06-29 17:51:25 +02:00
Joost Lekkerkerker
7319492bf3 Bump aiomealie to 0.5.0 (#120815) 2024-06-29 17:51:21 +02:00
J. Nick Koston
66932e3d9a Fix unneeded dict values for MATCH_ALL recorder attrs exclude (#120804)
* Small cleanup to handling MATCH_ALL recorder attrs exclude

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed

* Fix unneeded dict values for MATCH_ALL recorder attrs exclude

The exclude is a set so the dict values were not needed
2024-06-29 17:51:18 +02:00
J. Nick Koston
0ec07001bd Fix blocking I/O in xmpp notify to read uploaded files (#120801)
detected by ruff in https://github.com/home-assistant/core/pull/120799
2024-06-29 17:51:15 +02:00
J. Nick Koston
0dcfd38cdc Fix missing f-string in loop util (#120800) 2024-06-29 17:51:12 +02:00
Klaas Schoute
b45eff9a2b Bump gridnet lib to v5.0.1 (#120793) 2024-06-29 17:51:09 +02:00
Klaas Schoute
ec577c7bd3 Bump odp-amsterdam lib to v6.0.2 (#120788) 2024-06-29 17:51:06 +02:00
Paul Bottein
723c4a1eb5 Update frontend to 20240628.0 (#120785)
Co-authored-by: J. Nick Koston <nick@koston.org>
2024-06-29 17:51:02 +02:00
Klaas Schoute
b30b4d5a3a Bump energyzero lib to v2.1.1 (#120783) 2024-06-29 17:50:59 +02:00
Matthew FitzGerald-Chamberlain
8165acddeb Bump pyaprilaire to 0.7.4 (#120782) 2024-06-29 17:50:56 +02:00
Joost Lekkerkerker
0f3ed3bb67 Bump aiowithings to 3.0.2 (#120765) 2024-06-29 17:50:53 +02:00
Maciej Bieniek
d1a96ef362 Do not call async_delete_issue() if there is no issue to delete in Shelly integration (#120762)
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
2024-06-29 17:50:50 +02:00
J. Nick Koston
917eeba984 Increase mqtt availablity timeout to 50s (#120760) 2024-06-29 17:50:46 +02:00
Clifford Roche
59bb8b360e Bump greeclimate to 1.4.6 (#120758) 2024-06-29 17:50:43 +02:00
Klaas Schoute
6028e5b77a Bump p1monitor lib to v3.0.1 (#120756) 2024-06-29 17:50:40 +02:00
Klaas Schoute
83df470307 Bump easyenergy lib to v2.1.2 (#120753) 2024-06-29 17:50:37 +02:00
Joost Lekkerkerker
20ac0aa7b1 Bump govee-local-api to 1.5.1 (#120747) 2024-06-29 17:50:34 +02:00
Joost Lekkerkerker
f57c942901 Bump sense-energy to 0.12.4 (#120744)
* Bump sense-energy to 0.12.4

* Fix
2024-06-29 17:50:31 +02:00
Alexey ALERT Rubashёff
8994ab1686 Add warm water remaining volume sensor to Overkiz (#120718)
* warm water remaining volume sensor

* Update homeassistant/components/overkiz/sensor.py

Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>

---------

Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com>
2024-06-29 17:50:28 +02:00
Alexey ALERT Rubashёff
b350ba9657 Add electrical consumption sensor to Overkiz (#120717)
electrical consumption sensor
2024-06-29 17:50:25 +02:00
wittypluck
5fd589053a Reject small uptime updates for Unifi clients (#120398)
Extend logic to reject small uptime updates to Unifi clients + add unit tests
2024-06-29 17:50:22 +02:00
Allen Porter
2d5961fa4f Bump gcal_sync to 6.1.3 (#120278) 2024-06-29 17:50:18 +02:00
264 changed files with 5291 additions and 4003 deletions

View File

@@ -58,11 +58,6 @@ omit =
homeassistant/components/airvisual/sensor.py
homeassistant/components/airvisual_pro/__init__.py
homeassistant/components/airvisual_pro/sensor.py
homeassistant/components/aladdin_connect/__init__.py
homeassistant/components/aladdin_connect/api.py
homeassistant/components/aladdin_connect/application_credentials.py
homeassistant/components/aladdin_connect/cover.py
homeassistant/components/aladdin_connect/sensor.py
homeassistant/components/alarmdecoder/__init__.py
homeassistant/components/alarmdecoder/alarm_control_panel.py
homeassistant/components/alarmdecoder/binary_sensor.py

View File

@@ -80,8 +80,6 @@ build.json @home-assistant/supervisor
/tests/components/airzone/ @Noltari
/homeassistant/components/airzone_cloud/ @Noltari
/tests/components/airzone_cloud/ @Noltari
/homeassistant/components/aladdin_connect/ @swcloudgenie
/tests/components/aladdin_connect/ @swcloudgenie
/homeassistant/components/alarm_control_panel/ @home-assistant/core
/tests/components/alarm_control_panel/ @home-assistant/core
/homeassistant/components/alert/ @home-assistant/core @frenck
@@ -779,6 +777,8 @@ build.json @home-assistant/supervisor
/tests/components/lg_netcast/ @Drafteed @splinter98
/homeassistant/components/lidarr/ @tkdrob
/tests/components/lidarr/ @tkdrob
/homeassistant/components/lifx/ @Djelibeybi
/tests/components/lifx/ @Djelibeybi
/homeassistant/components/light/ @home-assistant/core
/tests/components/light/ @home-assistant/core
/homeassistant/components/linear_garage_door/ @IceBotYT

View File

@@ -55,13 +55,6 @@ class InvalidUser(HomeAssistantError):
Will not be raised when validating authentication.
"""
class InvalidUsername(InvalidUser):
"""Raised when invalid username is specified.
Will not be raised when validating authentication.
"""
def __init__(
self,
*args: object,
@@ -77,6 +70,13 @@ class InvalidUsername(InvalidUser):
)
class InvalidUsername(InvalidUser):
"""Raised when invalid username is specified.
Will not be raised when validating authentication.
"""
class Data:
"""Hold the user data."""
@@ -216,7 +216,7 @@ class Data:
break
if index is None:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
self.users.pop(index)
@@ -232,7 +232,7 @@ class Data:
user["password"] = self.hash_password(new_password, True).decode()
break
else:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
@callback
def _validate_new_username(self, new_username: str) -> None:
@@ -275,7 +275,7 @@ class Data:
self._async_check_for_not_normalized_usernames(self._data)
break
else:
raise InvalidUser
raise InvalidUser(translation_key="user_not_found")
async def async_save(self) -> None:
"""Save data."""

View File

@@ -8,7 +8,7 @@ import contextlib
from functools import partial
from itertools import chain
import logging
import logging.handlers
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
import mimetypes
from operator import contains, itemgetter
import os
@@ -257,12 +257,12 @@ async def async_setup_hass(
) -> core.HomeAssistant | None:
"""Set up Home Assistant."""
def create_hass() -> core.HomeAssistant:
async def create_hass() -> core.HomeAssistant:
"""Create the hass object and do basic setup."""
hass = core.HomeAssistant(runtime_config.config_dir)
loader.async_setup(hass)
async_enable_logging(
await async_enable_logging(
hass,
runtime_config.verbose,
runtime_config.log_rotate_days,
@@ -287,7 +287,7 @@ async def async_setup_hass(
async with hass.timeout.async_timeout(10):
await hass.async_stop()
hass = create_hass()
hass = await create_hass()
if runtime_config.skip_pip or runtime_config.skip_pip_packages:
_LOGGER.warning(
@@ -326,13 +326,13 @@ async def async_setup_hass(
if config_dict is None:
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
elif not basic_setup_success:
_LOGGER.warning("Unable to set up core integrations. Activating recovery mode")
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
elif any(domain not in hass.config.components for domain in CRITICAL_INTEGRATIONS):
_LOGGER.warning(
@@ -345,7 +345,7 @@ async def async_setup_hass(
recovery_mode = True
await stop_hass(hass)
hass = create_hass()
hass = await create_hass()
if old_logging:
hass.data[DATA_LOGGING] = old_logging
@@ -523,8 +523,7 @@ async def async_from_config_dict(
return hass
@core.callback
def async_enable_logging(
async def async_enable_logging(
hass: core.HomeAssistant,
verbose: bool = False,
log_rotate_days: int | None = None,
@@ -607,23 +606,9 @@ def async_enable_logging(
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
not err_path_exists and os.access(err_dir, os.W_OK)
):
err_handler: (
logging.handlers.RotatingFileHandler
| logging.handlers.TimedRotatingFileHandler
err_handler = await hass.async_add_executor_job(
_create_log_file, err_log_path, log_rotate_days
)
if log_rotate_days:
err_handler = logging.handlers.TimedRotatingFileHandler(
err_log_path, when="midnight", backupCount=log_rotate_days
)
else:
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
err_log_path, backupCount=1
)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
err_handler.setLevel(logging.INFO if verbose else logging.WARNING)
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
@@ -640,7 +625,29 @@ def async_enable_logging(
async_activate_log_queue_handler(hass)
class _RotatingFileHandlerWithoutShouldRollOver(logging.handlers.RotatingFileHandler):
def _create_log_file(
err_log_path: str, log_rotate_days: int | None
) -> RotatingFileHandler | TimedRotatingFileHandler:
"""Create log file and do roll over."""
err_handler: RotatingFileHandler | TimedRotatingFileHandler
if log_rotate_days:
err_handler = TimedRotatingFileHandler(
err_log_path, when="midnight", backupCount=log_rotate_days
)
else:
err_handler = _RotatingFileHandlerWithoutShouldRollOver(
err_log_path, backupCount=1
)
try:
err_handler.doRollover()
except OSError as err:
_LOGGER.error("Error rolling over log file: %s", err)
return err_handler
class _RotatingFileHandlerWithoutShouldRollOver(RotatingFileHandler):
"""RotatingFileHandler that does not check if it should roll over on every log."""
def shouldRollover(self, record: logging.LogRecord) -> bool:

View File

@@ -9,5 +9,5 @@
},
"iot_class": "cloud_push",
"loggers": ["jaraco.abode", "lomond"],
"requirements": ["jaraco.abode==5.1.2"]
"requirements": ["jaraco.abode==5.2.1"]
}

View File

@@ -8,6 +8,34 @@
"default": "mdi:lightbulb-on-outline"
}
},
"number": {
"led_bar_brightness": {
"default": "mdi:brightness-percent"
},
"display_brightness": {
"default": "mdi:brightness-percent"
}
},
"select": {
"configuration_control": {
"default": "mdi:cloud-cog"
},
"display_temperature_unit": {
"default": "mdi:thermometer-lines"
},
"led_bar_mode": {
"default": "mdi:led-strip"
},
"nox_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"voc_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"co2_automatic_baseline_calibration": {
"default": "mdi:molecule-co2"
}
},
"sensor": {
"total_volatile_organic_component_index": {
"default": "mdi:molecule"
@@ -17,6 +45,32 @@
},
"pm003_count": {
"default": "mdi:blur"
},
"led_bar_brightness": {
"default": "mdi:brightness-percent"
},
"display_brightness": {
"default": "mdi:brightness-percent"
},
"display_temperature_unit": {
"default": "mdi:thermometer-lines"
},
"led_bar_mode": {
"default": "mdi:led-strip"
},
"nox_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"voc_index_learning_time_offset": {
"default": "mdi:clock-outline"
},
"co2_automatic_baseline_calibration": {
"default": "mdi:molecule-co2"
}
},
"switch": {
"post_data_to_airgradient": {
"default": "mdi:cogs"
}
}
}

View File

@@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/airgradient",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["airgradient==0.6.0"],
"requirements": ["airgradient==0.6.1"],
"zeroconf": ["_airgradient._tcp.local."]
}

View File

@@ -16,6 +16,7 @@
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
},
"error": {

View File

@@ -1,94 +1,38 @@
"""The Aladdin Connect Genie integration."""
# mypy: ignore-errors
from __future__ import annotations
# from genie_partner_sdk.client import AladdinConnectClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session,
async_get_config_entry_implementation,
)
from homeassistant.helpers import issue_registry as ir
from .api import AsyncConfigEntryAuth
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
PLATFORMS: list[Platform] = [Platform.COVER, Platform.SENSOR]
type AladdinConnectConfigEntry = ConfigEntry[AladdinConnectCoordinator]
DOMAIN = "aladdin_connect"
async def async_setup_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Set up Aladdin Connect Genie from a config entry."""
implementation = await async_get_config_entry_implementation(hass, entry)
session = OAuth2Session(hass, entry, implementation)
auth = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
coordinator = AladdinConnectCoordinator(hass, AladdinConnectClient(auth))
await coordinator.async_setup()
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_remove_stale_devices(hass, entry)
return True
async def async_unload_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> bool:
"""Migrate old config."""
if config_entry.version < 2:
config_entry.async_start_reauth(hass)
hass.config_entries.async_update_entry(
config_entry,
version=2,
minor_version=1,
)
return True
def async_remove_stale_devices(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> None:
"""Remove stale devices from device registry."""
device_registry = dr.async_get(hass)
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry.entry_id
async def async_setup_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
"""Set up Aladdin Connect from a config entry."""
ir.async_create_issue(
hass,
DOMAIN,
DOMAIN,
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="integration_removed",
translation_placeholders={
"entries": "/config/integrations/integration/aladdin_connect",
},
)
all_device_ids = {door.unique_id for door in config_entry.runtime_data.doors}
for device_entry in device_entries:
device_id: str | None = None
return True
for identifier in device_entry.identifiers:
if identifier[0] == DOMAIN:
device_id = identifier[1]
break
if device_id is None or device_id not in all_device_ids:
# If device_id is None an invalid device entry was found for this config entry.
# If the device_id is not in existing device ids it's a stale device entry.
# Remove config entry from this device entry in either case.
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if all(
config_entry.state is ConfigEntryState.NOT_LOADED
for config_entry in hass.config_entries.async_entries(DOMAIN)
if config_entry.entry_id != entry.entry_id
):
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
return True

View File

@@ -1,33 +0,0 @@
"""API for Aladdin Connect Genie bound to Home Assistant OAuth."""
# mypy: ignore-errors
from typing import cast
from aiohttp import ClientSession
# from genie_partner_sdk.auth import Auth
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
API_URL = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1"
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
class AsyncConfigEntryAuth(Auth): # type: ignore[misc]
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
def __init__(
self,
websession: ClientSession,
oauth_session: OAuth2Session,
) -> None:
"""Initialize Aladdin Connect Genie auth."""
super().__init__(
websession, API_URL, oauth_session.token["access_token"], API_KEY
)
self._oauth_session = oauth_session
async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._oauth_session.async_ensure_token_valid()
return cast(str, self._oauth_session.token["access_token"])

View File

@@ -1,14 +0,0 @@
"""application_credentials platform the Aladdin Connect Genie integration."""
from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant
from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
"""Return authorization server."""
return AuthorizationServer(
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)

View File

@@ -1,70 +1,11 @@
"""Config flow for Aladdin Connect Genie."""
"""Config flow for Aladdin Connect integration."""
from collections.abc import Mapping
import logging
from typing import Any
from homeassistant.config_entries import ConfigFlow
import jwt
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .const import DOMAIN
from . import DOMAIN
class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Config flow to handle Aladdin Connect Genie OAuth2 authentication."""
class AladdinConnectConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Aladdin Connect."""
DOMAIN = DOMAIN
VERSION = 2
MINOR_VERSION = 1
reauth_entry: ConfigEntry | None = None
async def async_step_reauth(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon API auth error or upgrade from v1 to v2."""
self.reauth_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: Mapping[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Create an oauth config entry or update existing entry for reauth."""
token_payload = jwt.decode(
data[CONF_TOKEN][CONF_ACCESS_TOKEN], options={"verify_signature": False}
)
if not self.reauth_entry:
await self.async_set_unique_id(token_payload["sub"])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=token_payload["username"],
data=data,
)
if self.reauth_entry.unique_id == token_payload["username"]:
return self.async_update_reload_and_abort(
self.reauth_entry,
data=data,
unique_id=token_payload["sub"],
)
if self.reauth_entry.unique_id == token_payload["sub"]:
return self.async_update_reload_and_abort(self.reauth_entry, data=data)
return self.async_abort(reason="wrong_account")
@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
VERSION = 1

View File

@@ -1,6 +0,0 @@
"""Constants for the Aladdin Connect Genie integration."""
DOMAIN = "aladdin_connect"
OAUTH2_AUTHORIZE = "https://app.aladdinconnect.net/login.html"
OAUTH2_TOKEN = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1/oauth2/token"

View File

@@ -1,38 +0,0 @@
"""Define an object to coordinate fetching Aladdin Connect data."""
# mypy: ignore-errors
from datetime import timedelta
import logging
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class AladdinConnectCoordinator(DataUpdateCoordinator[None]):
"""Aladdin Connect Data Update Coordinator."""
def __init__(self, hass: HomeAssistant, acc: AladdinConnectClient) -> None:
"""Initialize."""
super().__init__(
hass,
logger=_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=15),
)
self.acc = acc
self.doors: list[GarageDoor] = []
async def async_setup(self) -> None:
"""Fetch initial data."""
self.doors = await self.acc.get_doors()
async def _async_update_data(self) -> None:
"""Fetch data from API endpoint."""
for door in self.doors:
await self.acc.update_door(door.device_id, door.door_number)

View File

@@ -1,84 +0,0 @@
"""Cover Entity for Genie Garage Door."""
# mypy: ignore-errors
from typing import Any
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Aladdin Connect platform."""
coordinator = config_entry.runtime_data
async_add_entities(AladdinDevice(coordinator, door) for door in coordinator.doors)
class AladdinDevice(AladdinConnectEntity, CoverEntity):
"""Representation of Aladdin Connect cover."""
_attr_device_class = CoverDeviceClass.GARAGE
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
_attr_name = None
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the Aladdin Connect cover."""
super().__init__(coordinator, device)
self._attr_unique_id = device.unique_id
async def async_open_cover(self, **kwargs: Any) -> None:
"""Issue open command to cover."""
await self.coordinator.acc.open_door(
self._device.device_id, self._device.door_number
)
async def async_close_cover(self, **kwargs: Any) -> None:
"""Issue close command to cover."""
await self.coordinator.acc.close_door(
self._device.device_id, self._device.door_number
)
@property
def is_closed(self) -> bool | None:
"""Update is closed attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closed")
@property
def is_closing(self) -> bool | None:
"""Update is closing attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closing")
@property
def is_opening(self) -> bool | None:
"""Update is opening attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "opening")

View File

@@ -1,27 +0,0 @@
"""Defines a base Aladdin Connect entity."""
# mypy: ignore-errors
# from genie_partner_sdk.model import GarageDoor
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
class AladdinConnectEntity(CoordinatorEntity[AladdinConnectCoordinator]):
"""Defines a base Aladdin Connect entity."""
_attr_has_entity_name = True
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self._device = device
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device.unique_id)},
name=device.name,
manufacturer="Overhead Door",
)

View File

@@ -1,11 +1,9 @@
{
"domain": "aladdin_connect",
"name": "Aladdin Connect",
"codeowners": ["@swcloudgenie"],
"config_flow": true,
"dependencies": ["application_credentials"],
"disabled": "This integration is disabled because it uses non-open source code to operate.",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"integration_type": "system",
"iot_class": "cloud_polling",
"requirements": ["genie-partner-sdk==1.0.2"]
"requirements": []
}

View File

@@ -1,5 +0,0 @@
extend = "../../../pyproject.toml"
lint.extend-ignore = [
"F821"
]

View File

@@ -1,80 +0,0 @@
"""Support for Aladdin Connect Garage Door sensors."""
# mypy: ignore-errors
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
@dataclass(frozen=True, kw_only=True)
class AccSensorEntityDescription(SensorEntityDescription):
"""Describes AladdinConnect sensor entity."""
value_fn: Callable[[AladdinConnectClient, str, int], float | None]
SENSORS: tuple[AccSensorEntityDescription, ...] = (
AccSensorEntityDescription(
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=AladdinConnectClient.get_battery_status,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Aladdin Connect sensor devices."""
coordinator = entry.runtime_data
async_add_entities(
AladdinConnectSensor(coordinator, door, description)
for description in SENSORS
for door in coordinator.doors
)
class AladdinConnectSensor(AladdinConnectEntity, SensorEntity):
"""A sensor implementation for Aladdin Connect devices."""
entity_description: AccSensorEntityDescription
def __init__(
self,
coordinator: AladdinConnectCoordinator,
device: GarageDoor,
description: AccSensorEntityDescription,
) -> None:
"""Initialize a sensor for an Aladdin Connect device."""
super().__init__(coordinator, device)
self.entity_description = description
self._attr_unique_id = f"{device.unique_id}-{description.key}"
@property
def native_value(self) -> float | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(
self.coordinator.acc, self._device.device_id, self._device.door_number
)

View File

@@ -1,29 +1,8 @@
{
"config": {
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Aladdin Connect needs to re-authenticate your account"
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
"issues": {
"integration_removed": {
"title": "The Aladdin Connect integration has been removed",
"description": "The Aladdin Connect integration has been removed from Home Assistant.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing Aladdin Connect integration entries]({entries})."
}
}
}

View File

@@ -1497,7 +1497,7 @@ async def async_api_adjust_range(
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
range_delta = int(range_delta * 20) if range_delta_default else int(range_delta)
service = SERVICE_SET_COVER_POSITION
if not (current := entity.attributes.get(cover.ATTR_POSITION)):
if not (current := entity.attributes.get(cover.ATTR_CURRENT_POSITION)):
msg = f"Unable to determine {entity.entity_id} current position"
raise AlexaInvalidValueError(msg)
position = response_value = min(100, max(0, range_delta + current))

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/anova",
"iot_class": "cloud_push",
"loggers": ["anova_wifi"],
"requirements": ["anova-wifi==0.14.0"]
"requirements": ["anova-wifi==0.17.0"]
}

View File

@@ -68,4 +68,8 @@ class OnlineStatus(CoordinatorEntity[APCUPSdCoordinator], BinarySensorEntity):
"""Returns true if the UPS is online."""
# Check if ONLINE bit is set in STATFLAG.
key = self.entity_description.key.upper()
return int(self.coordinator.data[key], 16) & _VALUE_ONLINE_MASK != 0
# The daemon could either report just a hex ("0x05000008"), or a hex with a "Status Flag"
# suffix ("0x05000008 Status Flag") in older versions.
# Here we trim the suffix if it exists to support both.
flag = self.coordinator.data[key].removesuffix(" Status Flag")
return int(flag, 16) & _VALUE_ONLINE_MASK != 0

View File

@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["pyaprilaire"],
"requirements": ["pyaprilaire==0.7.0"]
"requirements": ["pyaprilaire==0.7.4"]
}

View File

@@ -8,5 +8,5 @@
"integration_type": "device",
"iot_class": "cloud_polling",
"loggers": ["aioaquacell"],
"requirements": ["aioaquacell==0.1.7"]
"requirements": ["aioaquacell==0.1.8"]
}

View File

@@ -2,10 +2,10 @@
from __future__ import annotations
from pathlib import Path
from typing import cast
from aiohttp import ClientResponseError
from path import Path
from yalexs.exceptions import AugustApiAIOHTTPError
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from yalexs.manager.gateway import Config as YaleXSConfig

View File

@@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==6.4.1", "yalexs-ble==2.4.3"]
"requirements": ["yalexs==6.4.2", "yalexs-ble==2.4.3"]
}

View File

@@ -37,7 +37,10 @@
"message": "Username \"{username}\" already exists"
},
"username_not_normalized": {
"message": "Username \"{new_username}\" is not normalized"
"message": "Username \"{new_username}\" is not normalized. Please make sure the username is lowercase and does not contain any whitespace."
},
"user_not_found": {
"message": "User not found"
}
},
"issues": {

View File

@@ -30,7 +30,7 @@
"iot_class": "local_push",
"loggers": ["axis"],
"quality_scale": "platinum",
"requirements": ["axis==61"],
"requirements": ["axis==62"],
"ssdp": [
{
"manufacturer": "AXIS"

View File

@@ -366,7 +366,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property
def volume_level(self) -> float | None:
"""Volume level of the media player (0..1)."""
if self._volume.level and self._volume.level.level:
if self._volume.level and self._volume.level.level is not None:
return float(self._volume.level.level / 100)
return None

View File

@@ -58,7 +58,7 @@ class BringConfigFlow(ConfigFlow, domain=DOMAIN):
):
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.info["name"] or user_input[CONF_EMAIL], data=user_input
title=self.info.get("name") or user_input[CONF_EMAIL], data=user_input
)
return self.async_show_form(

View File

@@ -377,6 +377,14 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
# Return if integration has migrated already
return
supported_features = self.supported_features
if supported_features & (
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
):
# The entity supports both turn_on and turn_off, the backwards compatibility
# checks are not needed
return
supported_features = self.supported_features
if not supported_features & ClimateEntityFeature.TURN_OFF and (
type(self).async_turn_off is not ClimateEntity.async_turn_off

View File

@@ -53,11 +53,7 @@ async def websocket_create(
)
return
try:
await provider.async_add_auth(msg["username"], msg["password"])
except auth_ha.InvalidUser:
connection.send_error(msg["id"], "username_exists", "Username already exists")
return
await provider.async_add_auth(msg["username"], msg["password"])
credentials = await provider.async_get_or_create_credentials(
{"username": msg["username"]}
@@ -94,13 +90,7 @@ async def websocket_delete(
connection.send_result(msg["id"])
return
try:
await provider.async_remove_auth(msg["username"])
except auth_ha.InvalidUser:
connection.send_error(
msg["id"], "auth_not_found", "Given username was not found."
)
return
await provider.async_remove_auth(msg["username"])
connection.send_result(msg["id"])
@@ -187,14 +177,8 @@ async def websocket_admin_change_password(
)
return
try:
await provider.async_change_password(username, msg["password"])
connection.send_result(msg["id"])
except auth_ha.InvalidUser:
connection.send_error(
msg["id"], "credentials_not_found", "Credentials not found"
)
return
await provider.async_change_password(username, msg["password"])
connection.send_result(msg["id"])
@websocket_api.websocket_command(

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.6.26"]
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.7.3"]
}

View File

@@ -4,11 +4,11 @@ from datetime import timedelta
import logging
from pizzapi import Address, Customer, Order
from pizzapi.address import StoreException
import voluptuous as vol
from homeassistant.components import http
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
@@ -118,7 +118,7 @@ class Dominos:
self.country = conf.get(ATTR_COUNTRY)
try:
self.closest_store = self.address.closest_store()
except StoreException:
except Exception: # noqa: BLE001
self.closest_store = None
def handle_order(self, call: ServiceCall) -> None:
@@ -139,7 +139,7 @@ class Dominos:
"""Update the shared closest store (if open)."""
try:
self.closest_store = self.address.closest_store()
except StoreException:
except Exception: # noqa: BLE001
self.closest_store = None
return False
return True
@@ -219,7 +219,7 @@ class DominosOrder(Entity):
"""Update the order state and refreshes the store."""
try:
self.dominos.update_closest_store()
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
return
@@ -227,13 +227,13 @@ class DominosOrder(Entity):
order = self.order()
order.pay_with()
self._orderable = True
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
def order(self):
"""Create the order object."""
if self.dominos.closest_store is None:
raise StoreException
raise HomeAssistantError("No store available")
order = Order(
self.dominos.closest_store,
@@ -252,7 +252,7 @@ class DominosOrder(Entity):
try:
order = self.order()
order.place()
except StoreException:
except Exception: # noqa: BLE001
self._orderable = False
_LOGGER.warning(
"Attempted to order Dominos - Order invalid or store closed"

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/dominos",
"iot_class": "cloud_polling",
"loggers": ["pizzapi"],
"requirements": ["pizzapi==0.0.3"]
"requirements": ["pizzapi==0.0.6"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/easyenergy",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["easyenergy==2.1.1"]
"requirements": ["easyenergy==2.1.2"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.10", "deebot-client==8.0.0"]
"requirements": ["py-sucks==0.9.10", "deebot-client==8.1.1"]
}

View File

@@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.12.2"]
"requirements": ["sense-energy==0.12.4"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/energyzero",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["energyzero==2.1.0"]
"requirements": ["energyzero==2.1.1"]
}

View File

@@ -16,6 +16,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from .const import CONF_SOURCE_BOUQUET
type Enigma2ConfigEntry = ConfigEntry[OpenWebIfDevice]
PLATFORMS = [Platform.MEDIA_PLAYER]
@@ -35,7 +37,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: Enigma2ConfigEntry) -> b
hass, verify_ssl=entry.data[CONF_VERIFY_SSL], base_url=base_url
)
entry.runtime_data = OpenWebIfDevice(session)
entry.runtime_data = OpenWebIfDevice(
session, source_bouquet=entry.options.get(CONF_SOURCE_BOUQUET)
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["openwebif"],
"requirements": ["openwebifpy==4.2.4"]
"requirements": ["openwebifpy==4.2.5"]
}

View File

@@ -199,7 +199,8 @@ class Enigma2Device(MediaPlayerEntity):
async def async_mute_volume(self, mute: bool) -> None:
"""Mute or unmute."""
await self._device.toggle_mute()
if mute != self._device.status.muted:
await self._device.toggle_mute()
async def async_select_source(self, source: str) -> None:
"""Select input source."""

View File

@@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"requirements": ["pyenphase==1.20.3"],
"requirements": ["pyenphase==1.20.6"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."

View File

@@ -97,6 +97,7 @@ class ESPHomeDashboardUpdateEntity(
_attr_title = "ESPHome"
_attr_name = "Firmware"
_attr_release_url = "https://esphome.io/changelog/"
_attr_entity_registry_enabled_default = False
def __init__(
self, entry_data: RuntimeEntryData, coordinator: ESPHomeDashboardCoordinator

View File

@@ -107,13 +107,6 @@ class FeedReaderConfigFlow(ConfigFlow, domain=DOMAIN):
return self.abort_on_import_error(user_input[CONF_URL], "url_error")
return self.show_user_form(user_input, {"base": "url_error"})
if not feed.entries:
if self.context["source"] == SOURCE_IMPORT:
return self.abort_on_import_error(
user_input[CONF_URL], "no_feed_entries"
)
return self.show_user_form(user_input, {"base": "no_feed_entries"})
feed_title = feed["feed"]["title"]
return self.async_create_entry(
@@ -161,13 +154,6 @@ class FeedReaderConfigFlow(ConfigFlow, domain=DOMAIN):
step_id="reconfigure_confirm",
errors={"base": "url_error"},
)
if not feed.entries:
return self.show_user_form(
user_input=user_input,
description_placeholders={"name": self._config_entry.title},
step_id="reconfigure_confirm",
errors={"base": "no_feed_entries"},
)
self.hass.config_entries.async_update_entry(self._config_entry, data=user_input)
return self.async_abort(reason="reconfigure_successful")

View File

@@ -18,8 +18,7 @@
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"url_error": "The URL could not be opened.",
"no_feed_entries": "The URL seems not to serve any feed entries."
"url_error": "The URL could not be opened."
}
},
"options": {
@@ -38,10 +37,6 @@
"import_yaml_error_url_error": {
"title": "The Feedreader YAML configuration import failed",
"description": "Configuring the Feedreader using YAML is being removed but there was a connection error when trying to import the YAML configuration for `{url}`.\n\nPlease verify that url is reachable and accessable for Home Assistant and restart Home Assistant to try again or remove the Feedreader YAML configuration from your configuration.yaml file and continue to set up the integration manually."
},
"import_yaml_error_no_feed_entries": {
"title": "[%key:component::feedreader::issues::import_yaml_error_url_error::title%]",
"description": "Configuring the Feedreader using YAML is being removed but when trying to import the YAML configuration for `{url}` no feed entries were found.\n\nPlease verify that url serves any feed entries and restart Home Assistant to try again or remove the Feedreader YAML configuration from your configuration.yaml file and continue to set up the integration manually."
}
}
}

View File

@@ -98,7 +98,7 @@ class FlumeNotificationDataUpdateCoordinator(DataUpdateCoordinator[None]):
# The related binary sensors (leak detected, high flow, low battery)
# will be active until the notification is deleted in the Flume app.
self.notifications = pyflume.FlumeNotificationList(
self.auth, read=None, sort_direction="DESC"
self.auth, read=None
).notification_list
_LOGGER.debug("Notifications %s", self.notifications)

View File

@@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/flume",
"iot_class": "cloud_polling",
"loggers": ["pyflume"],
"requirements": ["PyFlume==0.8.7"]
"requirements": ["PyFlume==0.6.5"]
}

View File

@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["pyfritzhome"],
"quality_scale": "gold",
"requirements": ["pyfritzhome==0.6.11"],
"requirements": ["pyfritzhome==0.6.12"],
"ssdp": [
{
"st": "urn:schemas-upnp-org:device:fritzbox:1"

View File

@@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20240627.0"]
"requirements": ["home-assistant-frontend==20240710.0"]
}

View File

@@ -2,9 +2,12 @@
from __future__ import annotations
from fullykiosk import FullyKioskError
from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
@@ -36,8 +39,12 @@ class FullyCameraEntity(FullyKioskEntity, Camera):
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return bytes of camera image."""
image_bytes: bytes = await self.coordinator.fully.getCamshot()
return image_bytes
try:
image_bytes: bytes = await self.coordinator.fully.getCamshot()
except FullyKioskError as err:
raise HomeAssistantError(err) from err
else:
return image_bytes
async def async_turn_on(self) -> None:
"""Turn on camera."""

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/garages_amsterdam",
"iot_class": "cloud_polling",
"requirements": ["odp-amsterdam==6.0.1"]
"requirements": ["odp-amsterdam==6.0.2"]
}

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/calendar.google",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==6.0.4", "oauth2client==4.1.3", "ical==8.0.1"]
"requirements": ["gcal-sync==6.1.4", "oauth2client==4.1.3", "ical==8.1.1"]
}

View File

@@ -6,5 +6,5 @@
"dependencies": ["network"],
"documentation": "https://www.home-assistant.io/integrations/govee_light_local",
"iot_class": "local_push",
"requirements": ["govee-local-api==1.5.0"]
"requirements": ["govee-local-api==1.5.1"]
}

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/gree",
"iot_class": "local_polling",
"loggers": ["greeclimate"],
"requirements": ["greeclimate==1.4.1"]
"requirements": ["greeclimate==1.4.6"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/here_travel_time",
"iot_class": "cloud_polling",
"loggers": ["here_routing", "here_transit", "homeassistant.helpers.location"],
"requirements": ["here-routing==0.2.0", "here-transit==1.2.0"]
"requirements": ["here-routing==1.0.1", "here-transit==1.2.1"]
}

View File

@@ -142,10 +142,10 @@ class HiveClimateEntity(HiveEntity, ClimateEntity):
self.device = await self.hive.heating.getClimate(self.device)
self._attr_available = self.device["deviceData"].get("online")
if self._attr_available:
self._attr_hvac_mode = HIVE_TO_HASS_STATE[self.device["status"]["mode"]]
self._attr_hvac_action = HIVE_TO_HASS_HVAC_ACTION[
self._attr_hvac_mode = HIVE_TO_HASS_STATE.get(self.device["status"]["mode"])
self._attr_hvac_action = HIVE_TO_HASS_HVAC_ACTION.get(
self.device["status"]["action"]
]
)
self._attr_current_temperature = self.device["status"][
"current_temperature"
]
@@ -154,5 +154,6 @@ class HiveClimateEntity(HiveEntity, ClimateEntity):
self._attr_max_temp = self.device["max_temp"]
if self.device["status"]["boost"] == "ON":
self._attr_preset_mode = PRESET_BOOST
self._attr_hvac_mode = HVACMode.HEAT
else:
self._attr_preset_mode = PRESET_NONE

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.51", "babel==2.15.0"]
"requirements": ["holidays==0.53", "babel==2.15.0"]
}

View File

@@ -156,7 +156,6 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = {
key="GAS_POWER",
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.MEASUREMENT,
),
"GAS_ENERGY_COUNTER": SensorEntityDescription(
key="GAS_ENERGY_COUNTER",

View File

@@ -216,14 +216,13 @@ class HomematicipGenericEntity(Entity):
@property
def unique_id(self) -> str:
"""Return a unique ID."""
suffix = ""
if self._post is not None:
suffix = f"_{self._post}"
unique_id = f"{self.__class__.__name__}_{self._device.id}"
if self._is_multi_channel:
return f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}{suffix}"
unique_id = (
f"{self.__class__.__name__}_Channel{self._channel}_{self._device.id}"
)
return f"{self.__class__.__name__}_{self._device.id}{suffix}"
return unique_id
@property
def icon(self) -> str | None:

View File

@@ -3,7 +3,6 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from homematicip.aio.device import (
@@ -36,7 +35,6 @@ from homematicip.base.functionalChannels import FunctionalChannel
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
@@ -163,19 +161,28 @@ async def async_setup_entry(
for ch in get_channels_from_device(
device, FunctionalChannelType.ENERGY_SENSORS_INTERFACE_CHANNEL
):
if ch.connectedEnergySensorType not in SENSORS_ESI:
continue
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_IEC:
if ch.currentPowerConsumption is not None:
entities.append(HmipEsiIecPowerConsumption(hap, device))
if ch.energyCounterOneType != ESI_TYPE_UNKNOWN:
entities.append(HmipEsiIecEnergyCounterHighTariff(hap, device))
if ch.energyCounterTwoType != ESI_TYPE_UNKNOWN:
entities.append(HmipEsiIecEnergyCounterLowTariff(hap, device))
if ch.energyCounterThreeType != ESI_TYPE_UNKNOWN:
entities.append(
HmipEsiIecEnergyCounterInputSingleTariff(hap, device)
)
new_entities = [
HmipEsiSensorEntity(hap, device, ch.index, description)
for description in SENSORS_ESI[ch.connectedEnergySensorType]
]
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_GAS:
if ch.currentGasFlow is not None:
entities.append(HmipEsiGasCurrentGasFlow(hap, device))
if ch.gasVolume is not None:
entities.append(HmipEsiGasGasVolume(hap, device))
entities.extend(
entity
for entity in new_entities
if entity.entity_description.exists_fn(ch)
)
if ch.connectedEnergySensorType == ESI_CONNECTED_SENSOR_TYPE_LED:
if ch.currentPowerConsumption is not None:
entities.append(HmipEsiLedCurrentPowerConsumption(hap, device))
entities.append(HmipEsiLedEnergyCounterHighTariff(hap, device))
async_add_entities(entities)
@@ -434,132 +441,185 @@ class HomematicpTemperatureExternalSensorDelta(HomematicipGenericEntity, SensorE
return self._device.temperatureExternalDelta
@dataclass(kw_only=True, frozen=True)
class HmipEsiSensorEntityDescription(SensorEntityDescription):
"""SensorEntityDescription for HmIP Sensors."""
value_fn: Callable[[AsyncEnergySensorsInterface], StateType]
exists_fn: Callable[[FunctionalChannel], bool]
type_fn: Callable[[AsyncEnergySensorsInterface], str]
SENSORS_ESI = {
ESI_CONNECTED_SENSOR_TYPE_IEC: [
HmipEsiSensorEntityDescription(
key=ESI_TYPE_CURRENT_POWER_CONSUMPTION,
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda device: device.functional_channel.currentPowerConsumption,
exists_fn=lambda channel: channel.currentPowerConsumption is not None,
type_fn=lambda device: "CurrentPowerConsumption",
),
HmipEsiSensorEntityDescription(
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda device: device.functional_channel.energyCounterOne,
exists_fn=lambda channel: channel.energyCounterOneType != ESI_TYPE_UNKNOWN,
type_fn=lambda device: device.functional_channel.energyCounterOneType,
),
HmipEsiSensorEntityDescription(
key=ESI_TYPE_ENERGY_COUNTER_USAGE_LOW_TARIFF,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda device: device.functional_channel.energyCounterTwo,
exists_fn=lambda channel: channel.energyCounterTwoType != ESI_TYPE_UNKNOWN,
type_fn=lambda device: device.functional_channel.energyCounterTwoType,
),
HmipEsiSensorEntityDescription(
key=ESI_TYPE_ENERGY_COUNTER_INPUT_SINGLE_TARIFF,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda device: device.functional_channel.energyCounterThree,
exists_fn=lambda channel: channel.energyCounterThreeType
!= ESI_TYPE_UNKNOWN,
type_fn=lambda device: device.functional_channel.energyCounterThreeType,
),
],
ESI_CONNECTED_SENSOR_TYPE_LED: [
HmipEsiSensorEntityDescription(
key=ESI_TYPE_CURRENT_POWER_CONSUMPTION,
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda device: device.functional_channel.currentPowerConsumption,
exists_fn=lambda channel: channel.currentPowerConsumption is not None,
type_fn=lambda device: "CurrentPowerConsumption",
),
HmipEsiSensorEntityDescription(
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda device: device.functional_channel.energyCounterOne,
exists_fn=lambda channel: channel.energyCounterOne is not None,
type_fn=lambda device: ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
),
],
ESI_CONNECTED_SENSOR_TYPE_GAS: [
HmipEsiSensorEntityDescription(
key=ESI_TYPE_CURRENT_GAS_FLOW,
native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda device: device.functional_channel.currentGasFlow,
exists_fn=lambda channel: channel.currentGasFlow is not None,
type_fn=lambda device: "CurrentGasFlow",
),
HmipEsiSensorEntityDescription(
key=ESI_TYPE_CURRENT_GAS_VOLUME,
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
device_class=SensorDeviceClass.VOLUME,
state_class=SensorStateClass.TOTAL_INCREASING,
value_fn=lambda device: device.functional_channel.gasVolume,
exists_fn=lambda channel: channel.gasVolume is not None,
type_fn=lambda device: "GasVolume",
),
],
}
class HmipEsiSensorEntity(HomematicipGenericEntity, SensorEntity):
"""EntityDescription for HmIP-ESI Sensors."""
entity_description: HmipEsiSensorEntityDescription
def __init__(
self,
hap: HomematicipHAP,
device: HomematicipGenericEntity,
channel_index: int,
entity_description: HmipEsiSensorEntityDescription,
key: str,
value_fn: Callable[[FunctionalChannel], StateType],
type_fn: Callable[[FunctionalChannel], str],
) -> None:
"""Initialize Sensor Entity."""
super().__init__(
hap=hap,
device=device,
channel=channel_index,
post=entity_description.key,
channel=1,
post=key,
is_multi_channel=False,
)
self.entity_description = entity_description
self._value_fn = value_fn
self._type_fn = type_fn
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the esi sensor."""
state_attr = super().extra_state_attributes
state_attr[ATTR_ESI_TYPE] = self.entity_description.type_fn(self)
state_attr[ATTR_ESI_TYPE] = self._type_fn(self.functional_channel)
return state_attr
@property
def native_value(self) -> str | None:
"""Return the state of the sensor."""
return str(self.entity_description.value_fn(self))
return str(self._value_fn(self.functional_channel))
class HmipEsiIecPowerConsumption(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI IEC currentPowerConsumption sensor."""
_attr_device_class = SensorDeviceClass.POWER
_attr_native_unit_of_measurement = UnitOfPower.WATT
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key="CurrentPowerConsumption",
value_fn=lambda channel: channel.currentPowerConsumption,
type_fn=lambda channel: "CurrentPowerConsumption",
)
class HmipEsiIecEnergyCounterHighTariff(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI IEC energyCounterOne sensor."""
_attr_device_class = SensorDeviceClass.ENERGY
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
_attr_state_class = SensorStateClass.TOTAL_INCREASING
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
value_fn=lambda channel: channel.energyCounterOne,
type_fn=lambda channel: channel.energyCounterOneType,
)
class HmipEsiIecEnergyCounterLowTariff(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI IEC energyCounterTwo sensor."""
_attr_device_class = SensorDeviceClass.ENERGY
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
_attr_state_class = SensorStateClass.TOTAL_INCREASING
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key=ESI_TYPE_ENERGY_COUNTER_USAGE_LOW_TARIFF,
value_fn=lambda channel: channel.energyCounterTwo,
type_fn=lambda channel: channel.energyCounterTwoType,
)
class HmipEsiIecEnergyCounterInputSingleTariff(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI IEC energyCounterThree sensor."""
_attr_device_class = SensorDeviceClass.ENERGY
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
_attr_state_class = SensorStateClass.TOTAL_INCREASING
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key=ESI_TYPE_ENERGY_COUNTER_INPUT_SINGLE_TARIFF,
value_fn=lambda channel: channel.energyCounterThree,
type_fn=lambda channel: channel.energyCounterThreeType,
)
class HmipEsiGasCurrentGasFlow(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI Gas currentGasFlow sensor."""
_attr_device_class = SensorDeviceClass.VOLUME_FLOW_RATE
_attr_native_unit_of_measurement = UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key="CurrentGasFlow",
value_fn=lambda channel: channel.currentGasFlow,
type_fn=lambda channel: "CurrentGasFlow",
)
class HmipEsiGasGasVolume(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI Gas gasVolume sensor."""
_attr_device_class = SensorDeviceClass.GAS
_attr_native_unit_of_measurement = UnitOfVolume.CUBIC_METERS
_attr_state_class = SensorStateClass.TOTAL_INCREASING
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key="GasVolume",
value_fn=lambda channel: channel.gasVolume,
type_fn=lambda channel: "GasVolume",
)
class HmipEsiLedCurrentPowerConsumption(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI LED currentPowerConsumption sensor."""
_attr_device_class = SensorDeviceClass.POWER
_attr_native_unit_of_measurement = UnitOfPower.WATT
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key="CurrentPowerConsumption",
value_fn=lambda channel: channel.currentPowerConsumption,
type_fn=lambda channel: "CurrentPowerConsumption",
)
class HmipEsiLedEnergyCounterHighTariff(HmipEsiSensorEntity):
"""Representation of the Hmip-ESI LED energyCounterOne sensor."""
_attr_device_class = SensorDeviceClass.ENERGY
_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
_attr_state_class = SensorStateClass.TOTAL_INCREASING
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize the device."""
super().__init__(
hap,
device,
key=ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
value_fn=lambda channel: channel.energyCounterOne,
type_fn=lambda channel: ESI_TYPE_ENERGY_COUNTER_USAGE_HIGH_TARIFF,
)
class HomematicipPassageDetectorDeltaCounter(HomematicipGenericEntity, SensorEntity):

View File

@@ -71,13 +71,12 @@ class HoneywellSwitch(SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on if heat mode is enabled."""
if self._device.system_mode == "heat":
try:
await self._device.set_system_mode("emheat")
except SomeComfortError as err:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="switch_failed_on"
) from err
try:
await self._device.set_system_mode("emheat")
except SomeComfortError as err:
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="switch_failed_on"
) from err
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off if on."""

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
"iot_class": "cloud_push",
"loggers": ["aioautomower"],
"requirements": ["aioautomower==2024.6.3"]
"requirements": ["aioautomower==2024.6.4"]
}

View File

@@ -184,6 +184,8 @@ RESTRICTED_REASONS: list = [
RestrictedReasons.WEEK_SCHEDULE.lower(),
]
STATE_NO_WORK_AREA_ACTIVE = "no_work_area_active"
@callback
def _get_work_area_names(data: MowerAttributes) -> list[str]:
@@ -191,16 +193,21 @@ def _get_work_area_names(data: MowerAttributes) -> list[str]:
if TYPE_CHECKING:
# Sensor does not get created if it is None
assert data.work_areas is not None
return [data.work_areas[work_area_id].name for work_area_id in data.work_areas]
work_area_list = [
data.work_areas[work_area_id].name for work_area_id in data.work_areas
]
work_area_list.append(STATE_NO_WORK_AREA_ACTIVE)
return work_area_list
@callback
def _get_current_work_area_name(data: MowerAttributes) -> str:
"""Return the name of the current work area."""
if data.mower.work_area_id is None:
return STATE_NO_WORK_AREA_ACTIVE
if TYPE_CHECKING:
# Sensor does not get created if values are None
assert data.work_areas is not None
assert data.mower.work_area_id is not None
return data.work_areas[data.mower.work_area_id].name

View File

@@ -252,7 +252,8 @@
"work_area": {
"name": "Work area",
"state": {
"my_lawn": "My lawn"
"my_lawn": "My lawn",
"no_work_area_active": "No work area active"
}
}
},

View File

@@ -6,7 +6,7 @@
"description": "Enter your credentials",
"data": {
"username": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]",
"password": "App-specific password",
"with_family": "With family"
}
},
@@ -14,7 +14,7 @@
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Your previously entered password for {username} is no longer working. Update your password to keep using this integration.",
"data": {
"password": "[%key:common::config_flow::data::password%]"
"password": "App-specific password"
}
},
"trusted_device": {

View File

@@ -12,5 +12,5 @@
"documentation": "https://www.home-assistant.io/integrations/idasen_desk",
"iot_class": "local_push",
"quality_scale": "silver",
"requirements": ["idasen-ha==2.6.1"]
"requirements": ["idasen-ha==2.6.2"]
}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/incomfort",
"iot_class": "local_polling",
"loggers": ["incomfortclient"],
"requirements": ["incomfort-client==0.6.2"]
"requirements": ["incomfort-client==0.6.3"]
}

View File

@@ -28,5 +28,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/inkbird",
"iot_class": "local_push",
"requirements": ["inkbird-ble==0.5.6"]
"requirements": ["inkbird-ble==0.5.8"]
}

View File

@@ -8,6 +8,7 @@ from typing import Any
from pyecotrend_ista import KeycloakError, LoginError, PyEcotrendIsta, ServerError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
@@ -21,6 +22,8 @@ _LOGGER = logging.getLogger(__name__)
class IstaCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Ista EcoTrend data update coordinator."""
config_entry: ConfigEntry
def __init__(self, hass: HomeAssistant, ista: PyEcotrendIsta) -> None:
"""Initialize ista EcoTrend data update coordinator."""
super().__init__(
@@ -35,11 +38,14 @@ class IstaCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async def _async_update_data(self):
"""Fetch ista EcoTrend data."""
if not self.details:
self.details = await self.async_get_details()
try:
await self.hass.async_add_executor_job(self.ista.login)
if not self.details:
self.details = await self.async_get_details()
return await self.hass.async_add_executor_job(self.get_consumption_data)
except ServerError as e:
raise UpdateFailed(
"Unable to connect and retrieve data from ista EcoTrend, try again later"
@@ -48,7 +54,9 @@ class IstaCoordinator(DataUpdateCoordinator[dict[str, Any]]):
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="authentication_exception",
translation_placeholders={CONF_EMAIL: self.ista._email}, # noqa: SLF001
translation_placeholders={
CONF_EMAIL: self.config_entry.data[CONF_EMAIL]
},
) from e
def get_consumption_data(self) -> dict[str, Any]:
@@ -61,26 +69,16 @@ class IstaCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async def async_get_details(self) -> dict[str, Any]:
"""Retrieve details of consumption units."""
try:
result = await self.hass.async_add_executor_job(
self.ista.get_consumption_unit_details
result = await self.hass.async_add_executor_job(
self.ista.get_consumption_unit_details
)
return {
consumption_unit: next(
details
for details in result["consumptionUnits"]
if details["id"] == consumption_unit
)
except ServerError as e:
raise UpdateFailed(
"Unable to connect and retrieve data from ista EcoTrend, try again later"
) from e
except (LoginError, KeycloakError) as e:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="authentication_exception",
translation_placeholders={CONF_EMAIL: self.ista._email}, # noqa: SLF001
) from e
else:
return {
consumption_unit: next(
details
for details in result["consumptionUnits"]
if details["id"] == consumption_unit
)
for consumption_unit in self.ista.get_uuids()
}
for consumption_unit in self.ista.get_uuids()
}

View File

@@ -97,7 +97,11 @@ class JellyfinConfigFlow(ConfigFlow, domain=DOMAIN):
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
step_id="user",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA, user_input
),
errors=errors,
)
async def async_step_reauth(

View File

@@ -28,7 +28,7 @@ class JewishCalendarEntity(Entity):
) -> None:
"""Initialize a Jewish Calendar entity."""
self.entity_description = description
self._attr_unique_id = f"{config_entry.entry_id}_{description.key}"
self._attr_unique_id = f"{config_entry.entry_id}-{description.key}"
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, config_entry.entry_id)},

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Any
from knocki import KnockiClient, KnockiConnectionError
from knocki import KnockiClient, KnockiConnectionError, KnockiInvalidAuthError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -45,6 +45,8 @@ class KnockiConfigFlow(ConfigFlow, domain=DOMAIN):
raise
except KnockiConnectionError:
errors["base"] = "cannot_connect"
except KnockiInvalidAuthError:
errors["base"] = "invalid_auth"
except Exception: # noqa: BLE001
LOGGER.exception("Error logging into the Knocki API")
errors["base"] = "unknown"

View File

@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "cloud_push",
"loggers": ["knocki"],
"requirements": ["knocki==0.2.0"]
"requirements": ["knocki==0.3.1"]
}

View File

@@ -641,12 +641,10 @@ class KodiEntity(MediaPlayerEntity):
if self.state == MediaPlayerState.OFF:
return state_attr
hdr_type = (
self._item.get("streamdetails", {}).get("video", [{}])[0].get("hdrtype")
)
if hdr_type == "":
state_attr["dynamic_range"] = "sdr"
else:
state_attr["dynamic_range"] = "sdr"
if (video_details := self._item.get("streamdetails", {}).get("video")) and (
hdr_type := video_details[0].get("hdrtype")
):
state_attr["dynamic_range"] = hdr_type
return state_attr

View File

@@ -1,7 +1,7 @@
{
"domain": "lifx",
"name": "LIFX",
"codeowners": [],
"codeowners": ["@Djelibeybi"],
"config_flow": true,
"dependencies": ["network"],
"dhcp": [
@@ -48,7 +48,7 @@
"iot_class": "local_polling",
"loggers": ["aiolifx", "aiolifx_effects", "bitstring"],
"requirements": [
"aiolifx==1.0.2",
"aiolifx==1.0.5",
"aiolifx-effects==0.3.2",
"aiolifx-themes==0.4.15"
]

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/local_calendar",
"iot_class": "local_polling",
"loggers": ["ical"],
"requirements": ["ical==8.0.1"]
"requirements": ["ical==8.1.1"]
}

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/local_todo",
"iot_class": "local_polling",
"requirements": ["ical==8.0.1"]
"requirements": ["ical==8.1.1"]
}

View File

@@ -145,4 +145,20 @@ DISCOVERY_SCHEMAS = [
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.RainSensor,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="LockDoorStateSensor",
device_class=BinarySensorDeviceClass.DOOR,
# pylint: disable=unnecessary-lambda
measurement_to_ha=lambda x: {
clusters.DoorLock.Enums.DoorStateEnum.kDoorOpen: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorJammed: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorForcedOpen: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorClosed: False,
}.get(x),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.DoorLock.Attributes.DoorState,),
),
]

View File

@@ -60,6 +60,7 @@ SUPPORT_DRY_MODE_DEVICES: set[tuple[int, int]] = {
# In the list below specify tuples of (vendorid, productid) of devices that
# support dry mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x1209, 0x8007),
}
@@ -68,6 +69,7 @@ SUPPORT_FAN_MODE_DEVICES: set[tuple[int, int]] = {
# In the list below specify tuples of (vendorid, productid) of devices that
# support fan-only mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x1209, 0x8007),
}
@@ -225,6 +227,13 @@ class MatterClimate(MatterEntity, ClimateEntity):
self._attr_current_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.LocalTemperature
)
if self.get_matter_attribute_value(clusters.OnOff.Attributes.OnOff) is False:
# special case: the appliance has a dedicated Power switch on the OnOff cluster
# if the mains power is off - treat it as if the HVAC mode is off
self._attr_hvac_mode = HVACMode.OFF
self._attr_hvac_action = None
return
# update hvac_mode from SystemMode
system_mode_value = int(
self.get_matter_attribute_value(clusters.Thermostat.Attributes.SystemMode)
@@ -265,19 +274,13 @@ class MatterClimate(MatterEntity, ClimateEntity):
self._attr_hvac_action = HVACAction.FAN
case _:
self._attr_hvac_action = HVACAction.OFF
# update target_temperature
if self._attr_hvac_mode == HVACMode.HEAT_COOL:
self._attr_target_temperature = None
elif self._attr_hvac_mode == HVACMode.COOL:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
else:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
)
# update target temperature high/low
if self._attr_hvac_mode == HVACMode.HEAT_COOL:
supports_range = (
self._attr_supported_features
& ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
if supports_range and self._attr_hvac_mode == HVACMode.HEAT_COOL:
self._attr_target_temperature = None
self._attr_target_temperature_high = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
@@ -287,6 +290,16 @@ class MatterClimate(MatterEntity, ClimateEntity):
else:
self._attr_target_temperature_high = None
self._attr_target_temperature_low = None
# update target_temperature
if self._attr_hvac_mode == HVACMode.COOL:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedCoolingSetpoint
)
else:
self._attr_target_temperature = self._get_temperature_in_degrees(
clusters.Thermostat.Attributes.OccupiedHeatingSetpoint
)
# update min_temp
if self._attr_hvac_mode == HVACMode.COOL:
attribute = clusters.Thermostat.Attributes.AbsMinCoolSetpointLimit
@@ -337,6 +350,7 @@ DISCOVERY_SCHEMAS = [
clusters.Thermostat.Attributes.TemperatureSetpointHold,
clusters.Thermostat.Attributes.UnoccupiedCoolingSetpoint,
clusters.Thermostat.Attributes.UnoccupiedHeatingSetpoint,
clusters.OnOff.Attributes.OnOff,
),
device_type=(device_types.Thermostat, device_types.RoomAirConditioner),
),

View File

@@ -170,6 +170,14 @@ class MatterFan(MatterEntity, FanEntity):
"""Update from device."""
if not hasattr(self, "_attr_preset_modes"):
self._calculate_features()
if self.get_matter_attribute_value(clusters.OnOff.Attributes.OnOff) is False:
# special case: the appliance has a dedicated Power switch on the OnOff cluster
# if the mains power is off - treat it as if the fan mode is off
self._attr_preset_mode = None
self._attr_percentage = 0
return
if self._attr_supported_features & FanEntityFeature.DIRECTION:
direction_value = self.get_matter_attribute_value(
clusters.FanControl.Attributes.AirflowDirection
@@ -200,7 +208,13 @@ class MatterFan(MatterEntity, FanEntity):
wind_setting = self.get_matter_attribute_value(
clusters.FanControl.Attributes.WindSetting
)
if (
fan_mode = self.get_matter_attribute_value(
clusters.FanControl.Attributes.FanMode
)
if fan_mode == clusters.FanControl.Enums.FanModeEnum.kOff:
self._attr_preset_mode = None
self._attr_percentage = 0
elif (
self._attr_preset_modes
and PRESET_NATURAL_WIND in self._attr_preset_modes
and wind_setting & WindBitmap.kNaturalWind
@@ -299,6 +313,7 @@ DISCOVERY_SCHEMAS = [
clusters.FanControl.Attributes.RockSetting,
clusters.FanControl.Attributes.WindSetting,
clusters.FanControl.Attributes.AirflowDirection,
clusters.OnOff.Attributes.OnOff,
),
),
]

View File

@@ -446,6 +446,8 @@ DISCOVERY_SCHEMAS = [
device_types.DimmablePlugInUnit,
device_types.ExtendedColorLight,
device_types.OnOffLight,
device_types.DimmerSwitch,
device_types.ColorDimmerSwitch,
),
),
# Additional schema to match (HS Color) lights with incorrect/missing device type

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
from typing import Any
from chip.clusters import Objects as clusters
@@ -38,6 +39,7 @@ class MatterLock(MatterEntity, LockEntity):
"""Representation of a Matter lock."""
features: int | None = None
_optimistic_timer: asyncio.TimerHandle | None = None
@property
def code_format(self) -> str | None:
@@ -90,6 +92,15 @@ class MatterLock(MatterEntity, LockEntity):
async def async_lock(self, **kwargs: Any) -> None:
"""Lock the lock with pin if needed."""
if not self._attr_is_locked:
# optimistically signal locking to state machine
self._attr_is_locking = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
@@ -98,6 +109,15 @@ class MatterLock(MatterEntity, LockEntity):
async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock with pin if needed."""
if self._attr_is_locked:
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
if self.supports_unbolt:
@@ -114,6 +134,14 @@ class MatterLock(MatterEntity, LockEntity):
async def async_open(self, **kwargs: Any) -> None:
"""Open the door latch."""
# optimistically signal opening to state machine
self._attr_is_opening = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30 if self._attr_is_locked else 5, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
@@ -135,42 +163,39 @@ class MatterLock(MatterEntity, LockEntity):
clusters.DoorLock.Attributes.LockState
)
# always reset the optimisically (un)locking state on state update
self._reset_optimistic_state(write_state=False)
LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)
if lock_state is clusters.DoorLock.Enums.DlLockState.kUnlatched:
self._attr_is_locked = False
self._attr_is_open = True
if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
self._attr_is_locked = True
self._attr_is_locking = False
self._attr_is_unlocking = False
elif lock_state is clusters.DoorLock.Enums.DlLockState.kUnlocked:
self._attr_is_open = False
elif lock_state in (
clusters.DoorLock.Enums.DlLockState.kUnlocked,
clusters.DoorLock.Enums.DlLockState.kNotFullyLocked,
):
self._attr_is_locked = False
self._attr_is_locking = False
self._attr_is_unlocking = False
elif lock_state is clusters.DoorLock.Enums.DlLockState.kNotFullyLocked:
if self.is_locked is True:
self._attr_is_unlocking = True
elif self.is_locked is False:
self._attr_is_locking = True
self._attr_is_open = False
else:
# According to the matter docs a null state can happen during device startup.
# Treat any other state as unknown.
# NOTE: A null state can happen during device startup.
self._attr_is_locked = None
self._attr_is_locking = None
self._attr_is_unlocking = None
self._attr_is_open = None
if self.supports_door_position_sensor:
door_state = self.get_matter_attribute_value(
clusters.DoorLock.Attributes.DoorState
)
assert door_state is not None
LOGGER.debug("Door state: %s for %s", door_state, self.entity_id)
self._attr_is_jammed = (
door_state is clusters.DoorLock.Enums.DoorStateEnum.kDoorJammed
)
self._attr_is_open = (
door_state is clusters.DoorLock.Enums.DoorStateEnum.kDoorOpen
)
@callback
def _reset_optimistic_state(self, write_state: bool = True) -> None:
if self._optimistic_timer and not self._optimistic_timer.cancelled():
self._optimistic_timer.cancel()
self._optimistic_timer = None
self._attr_is_locking = False
self._attr_is_unlocking = False
self._attr_is_opening = False
if write_state:
self.async_write_ha_state()
DISCOVERY_SCHEMAS = [

View File

@@ -6,6 +6,6 @@
"dependencies": ["websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/matter",
"iot_class": "local_push",
"requirements": ["python-matter-server==6.2.0b1"],
"requirements": ["python-matter-server==6.2.2"],
"zeroconf": ["_matter._tcp.local.", "_matterc._udp.local."]
}

View File

@@ -6,7 +6,11 @@ from dataclasses import dataclass
from chip.clusters import Objects as clusters
from chip.clusters.Types import Nullable, NullValue
from matter_server.common.custom_clusters import EveCluster
from matter_server.common.custom_clusters import (
EveCluster,
NeoCluster,
ThirdRealityMeteringCluster,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -171,9 +175,6 @@ DISCOVERY_SCHEMAS = [
),
entity_class=MatterSensor,
required_attributes=(EveCluster.Attributes.Watt,),
# Add OnOff Attribute as optional attribute to poll
# the primary value when the relay is toggled
optional_attributes=(clusters.OnOff.Attributes.OnOff,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
@@ -213,9 +214,6 @@ DISCOVERY_SCHEMAS = [
),
entity_class=MatterSensor,
required_attributes=(EveCluster.Attributes.Current,),
# Add OnOff Attribute as optional attribute to poll
# the primary value when the relay is toggled
optional_attributes=(clusters.OnOff.Attributes.OnOff,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
@@ -364,4 +362,90 @@ DISCOVERY_SCHEMAS = [
clusters.ActivatedCarbonFilterMonitoring.Attributes.Condition,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="ThirdRealityEnergySensorWatt",
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 1000,
),
entity_class=MatterSensor,
required_attributes=(
ThirdRealityMeteringCluster.Attributes.InstantaneousDemand,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="ThirdRealityEnergySensorWattAccumulated",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=3,
state_class=SensorStateClass.TOTAL_INCREASING,
measurement_to_ha=lambda x: x / 1000,
),
entity_class=MatterSensor,
required_attributes=(
ThirdRealityMeteringCluster.Attributes.CurrentSummationDelivered,
),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWatt",
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 10,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Watt,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWattAccumulated",
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_display_precision=1,
state_class=SensorStateClass.TOTAL_INCREASING,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.WattAccumulated,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorVoltage",
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
measurement_to_ha=lambda x: x / 10,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Voltage,),
),
MatterDiscoverySchema(
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NeoEnergySensorWattCurrent",
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
required_attributes=(NeoCluster.Attributes.Current,),
),
]

View File

@@ -114,6 +114,7 @@ DISCOVERY_SCHEMAS = [
device_types.ColorTemperatureLight,
device_types.DimmableLight,
device_types.ExtendedColorLight,
device_types.DimmerSwitch,
device_types.ColorDimmerSwitch,
device_types.OnOffLight,
device_types.AirPurifier,

View File

@@ -30,8 +30,8 @@ async def async_setup_entry(
def _get_event_from_mealplan(mealplan: Mealplan) -> CalendarEvent:
"""Create a CalendarEvent from a Mealplan."""
description: str | None = None
name = "No recipe"
description: str | None = mealplan.description
name = mealplan.title or "No recipe"
if mealplan.recipe:
name = mealplan.recipe.name
description = mealplan.recipe.description
@@ -50,12 +50,9 @@ class MealieMealplanCalendarEntity(MealieEntity, CalendarEntity):
self, coordinator: MealieCoordinator, entry_type: MealplanEntryType
) -> None:
"""Create the Calendar entity."""
super().__init__(coordinator)
super().__init__(coordinator, entry_type.name.lower())
self._entry_type = entry_type
self._attr_translation_key = entry_type.name.lower()
self._attr_unique_id = (
f"{self.coordinator.config_entry.entry_id}_{entry_type.name.lower()}"
)
@property
def event(self) -> CalendarEvent | None:
@@ -63,7 +60,8 @@ class MealieMealplanCalendarEntity(MealieEntity, CalendarEntity):
mealplans = self.coordinator.data[self._entry_type]
if not mealplans:
return None
return _get_event_from_mealplan(mealplans[0])
sorted_mealplans = sorted(mealplans, key=lambda x: x.mealplan_date)
return _get_event_from_mealplan(sorted_mealplans[0])
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime

View File

@@ -28,14 +28,13 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a flow initialized by the user."""
errors: dict[str, str] = {}
if user_input:
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
client = MealieClient(
user_input[CONF_HOST],
token=user_input[CONF_API_TOKEN],
session=async_get_clientsession(self.hass),
)
try:
await client.get_mealplan_today()
info = await client.get_user_info()
except MealieConnectionError:
errors["base"] = "cannot_connect"
except MealieAuthenticationError:
@@ -44,6 +43,8 @@ class MealieConfigFlow(ConfigFlow, domain=DOMAIN):
LOGGER.exception("Unexpected error")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(info.user_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title="Mealie",
data=user_input,

View File

@@ -12,10 +12,13 @@ class MealieEntity(CoordinatorEntity[MealieCoordinator]):
_attr_has_entity_name = True
def __init__(self, coordinator: MealieCoordinator) -> None:
def __init__(self, coordinator: MealieCoordinator, key: str) -> None:
"""Initialize Mealie entity."""
super().__init__(coordinator)
unique_id = coordinator.config_entry.unique_id
assert unique_id is not None
self._attr_unique_id = f"{unique_id}_{key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
identifiers={(DOMAIN, unique_id)},
entry_type=DeviceEntryType.SERVICE,
)

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/mealie",
"integration_type": "service",
"iot_class": "local_polling",
"requirements": ["aiomealie==0.4.0"]
"requirements": ["aiomealie==0.5.0"]
}

View File

@@ -3,8 +3,11 @@
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"host": "[%key:common::config_flow::data::url%]",
"api_token": "[%key:common::config_flow::data::api_token%]"
},
"data_description": {
"host": "The URL of your Mealie instance."
}
}
},

View File

@@ -8,6 +8,6 @@
"iot_class": "calculated",
"loggers": ["yt_dlp"],
"quality_scale": "internal",
"requirements": ["yt-dlp==2024.05.27"],
"requirements": ["yt-dlp==2024.07.16"],
"single_config_entry": true
}

View File

@@ -721,10 +721,15 @@ async def webhook_get_config(
"""Handle a get config webhook."""
hass_config = hass.config.as_dict()
device: dr.DeviceEntry = hass.data[DOMAIN][DATA_DEVICES][
config_entry.data[CONF_WEBHOOK_ID]
]
resp = {
"latitude": hass_config["latitude"],
"longitude": hass_config["longitude"],
"elevation": hass_config["elevation"],
"hass_device_id": device.id,
"unit_system": hass_config["unit_system"],
"location_name": hass_config["location_name"],
"time_zone": hass_config["time_zone"],

View File

@@ -6,5 +6,5 @@
"iot_class": "local_polling",
"loggers": ["pymodbus"],
"quality_scale": "platinum",
"requirements": ["pymodbus==3.6.8"]
"requirements": ["pymodbus==3.6.9"]
}

View File

@@ -2,6 +2,7 @@
"domain": "mpd",
"name": "Music Player Daemon (MPD)",
"codeowners": [],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/mpd",
"iot_class": "local_polling",
"loggers": ["mpd"],

View File

@@ -1141,8 +1141,8 @@ class MQTT:
# see https://github.com/eclipse/paho.mqtt.python/issues/687
# properties and reason codes are not used in Home Assistant
future = self._async_get_mid_future(mid)
if future.done() and future.exception():
# Timed out
if future.done() and (future.cancelled() or future.exception()):
# Timed out or cancelled
return
future.set_result(None)

View File

@@ -36,7 +36,7 @@ from .const import (
)
from .models import DATA_MQTT, DATA_MQTT_AVAILABLE, ReceiveMessage
AVAILABILITY_TIMEOUT = 30.0
AVAILABILITY_TIMEOUT = 50.0
TEMP_DIR_NAME = f"home-assistant-{DOMAIN}"

View File

@@ -261,7 +261,7 @@ class NMBSSensor(SensorEntity):
attrs["via_arrival_platform"] = via["arrival"]["platform"]
attrs["via_transfer_platform"] = via["departure"]["platform"]
attrs["via_transfer_time"] = get_delay_in_minutes(
via["timeBetween"]
via["timebetween"]
) + get_delay_in_minutes(via["departure"]["delay"])
if delay > 0:

View File

@@ -73,7 +73,7 @@ CONFIG_SCHEMA = vol.Schema(
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
}
),
cv.has_at_least_one_key("auth"),
cv.has_at_least_one_key(CONF_API_KEY, CONF_PASSWORD),
)
},
extra=vol.ALLOW_EXTRA,

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