Compare commits

..

893 Commits

Author SHA1 Message Date
Michael Hansen
87a3abba00 Updated snapshots 2025-08-25 15:52:22 -05:00
Michael Hansen
f6b39d5c4b Removed the wrong country :D 2025-08-25 15:50:30 -05:00
Michael Hansen
082fe20ffb Add missing test files 2025-08-25 15:48:33 -05:00
Michael Hansen
5f7c634162 Remove country argument 2025-08-25 15:48:33 -05:00
Michael Hansen
9c9aa23e62 Add websocket command to get custom sentences 2025-08-25 15:48:33 -05:00
Erik Montnemery
58339d79d3 Revert "Fix entities/devices stuck in disabled state after config entry re-add" (#151158) 2025-08-25 22:48:30 +02:00
Erik Montnemery
05c8e8b4fd Adjust entity disabled_by flag when restoring a deleted entity (#151150) 2025-08-25 22:47:55 +02:00
Christopher Fenner
ae676d6857 Bump PyViCare to 2.51.0 (#151153) 2025-08-25 22:43:50 +02:00
Michael Hansen
db4d51e617 Bump hassil to 3.2.0 (#151168) 2025-08-25 21:39:11 +01:00
Glenn Vandeuren (aka Iondependent)
bfe84ccd12 Add reconfigure flow to niko_home_control (#133993)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-08-25 22:03:05 +02:00
J. Nick Koston
ede948c277 Fix HomeKit Controller entity state restore issues for IP/COAP devices (#151087) 2025-08-25 19:50:13 +02:00
G Johansson
28e8405622 Fix correct breaking version in stiebel_eltron (#151163) 2025-08-25 19:43:19 +02:00
Marc Mueller
202d285286 Update typing-extensions to 4.15.0 (#151157) 2025-08-25 18:33:59 +01:00
Marc Mueller
c0b1536cd8 Fix hassfest requirements check (#151159) 2025-08-25 18:31:17 +02:00
Norbert Rittel
f1d2b102cf Fix broken reference for "event_types" in template (#151152) 2025-08-25 15:57:04 +01:00
Ludovic BOUÉ
8b29e3011e Matter Valve new attributes (#150788) 2025-08-25 15:43:00 +02:00
Jan Bouwhuis
dea5e7454a Add MQTT lock subentry support (#150860)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-08-25 15:37:36 +02:00
Petro31
58d4fd0b75 Add update platform to template integration (#150277) 2025-08-25 14:43:23 +02:00
epenet
ca37bc1506 Add Tuya test fixtures (#151022) 2025-08-25 11:28:55 +02:00
karwosts
1f25641e3b Fix schedule entity can't delete icon (#150995) 2025-08-25 10:56:33 +02:00
Maikel Punie
962b276a77 Bump velbusaio to 2025.8.0 (#151133) 2025-08-25 10:41:22 +02:00
Yuxin Wang
0786e333ce Remove tests for setting up multiple integrations for APCUPSD (#151125) 2025-08-25 10:33:00 +02:00
Matthias Alphart
d35271a214 Update knx-frontend to 2025.8.24.205840 (#151118) 2025-08-25 08:52:06 +02:00
Petro31
8fbf6dbd0b Add event platform to templates (#145408)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-08-25 07:50:22 +02:00
Retha Runolfsson
60d8353b10 Bump pyswitchbot to 0.69.0 (#151123) 2025-08-25 07:03:09 +02:00
Yuxin Wang
2d69d2ef1c Remove unnecessary tests for APCUPSD (#151126) 2025-08-25 07:00:13 +02:00
LG-ThinQ-Integration
e1e11db3d2 Add switch for AC's air_clean, ventilator and washers to LG (#140842)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-08-25 06:42:14 +02:00
jan iversen
91ef5fb429 modbus: Sensor caused hanging call_later method (#151033) 2025-08-24 22:37:26 +02:00
Christian Fetzer
0dc2a5a02c Add m³/min as volume flow rate unit (#151051) 2025-08-24 20:30:25 +01:00
Joakim Plate
b018465a4d Update togrill to 0.8.0 (#150945) 2025-08-24 20:07:55 +02:00
Joris Pelgröm
e797d651b5 Bump letpot to 0.6.2 (#151094) 2025-08-24 18:08:30 +02:00
J. Nick Koston
bc6f261105 Fix entities/devices stuck in disabled state after config entry re-add (#151075) 2025-08-24 15:55:58 +02:00
Marc Mueller
6c2ba15a73 Update lxml to 6.0.1 (#151093) 2025-08-24 15:16:41 +02:00
Simone Chemelli
ef0712a785 Handle TypeError in Alexa Devices (#151088) 2025-08-24 15:08:51 +02:00
Tom
2bd45e4625 Bump airos to 0.4.3 (#151042) 2025-08-24 13:04:41 +02:00
J. Nick Koston
03ca164fb3 Switch to August OAuth with official API (#151080) 2025-08-24 05:29:53 +02:00
J. Nick Koston
3c11f8e50e Bump yalexs to 8.12.0 (#151079) 2025-08-24 05:04:40 +02:00
Andrea Turri
f0e5325510 Provide elapsed time sensor consistent in Miele (#145093)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-08-23 20:50:19 +02:00
Yevhenii Vaskivskyi
4ff2da7553 Bump asusrouter to 1.20.0 (#151067) 2025-08-23 20:45:20 +02:00
LG-ThinQ-Integration
af951ff0d4 Modified to use built-in fan modes for "mid". (#150927)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-08-23 13:24:10 +02:00
Matthias Alphart
207e2f61ea Fix KNX strings for UI entity creation (#151053) 2025-08-22 23:43:05 +02:00
Manu
9e204cd347 Bump habiticalib to v0.4.3 (#151050) 2025-08-22 21:08:13 +02:00
dependabot[bot]
97a7e46579 Bump github/codeql-action from 3.29.10 to 3.29.11 (#151020)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 20:58:10 +02:00
Andrew Jackson
8175f4ba65 Bump mastodon to 2.1.2 (#150948) 2025-08-22 19:35:11 +02:00
Andre Lengwenus
22e307a48a Bump lcn-frontend to 0.2.7 (#151041) 2025-08-22 19:34:09 +02:00
TimL
6c69b36b7b Bump pysmlight to v0.2.8 (#151036) 2025-08-22 15:06:23 +02:00
J. Diego Rodríguez Royo
dd270f54fc Delete Home Connect deprecated actions (#150929) 2025-08-22 14:16:01 +02:00
Simone Chemelli
158fb35c5b Add account reconfigure to Alexa Devices config flow (#149637)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-08-22 13:52:01 +02:00
Matthias Alphart
85e5081fc3 Use serialized schema from backend in UI entity configuration (#149496) 2025-08-22 12:24:05 +02:00
Billy Rowell
f6bb32c44b Add Tuya test fixtures for bzyd category (#150923)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-22 08:57:16 +02:00
Christopher Fenner
e16eff6f28 Improve device details for Husqvarna BLE mower device (#150993) 2025-08-22 08:15:35 +02:00
Aidan Timson
2b80a621af Update aiolyric to 2.0.2 (#150998) 2025-08-22 08:12:49 +02:00
Aidan Timson
80d2e08572 Update aioazuredevops to 2.2.2 (#151000) 2025-08-22 06:55:13 +02:00
puddly
e15166cbf3 Bump ZHA to 0.0.69 (#151010) 2025-08-22 06:49:38 +02:00
Matthias Alphart
72e78b6719 Allow nested translation schema for config panels (#149346) 2025-08-21 20:13:36 +02:00
J. Nick Koston
a50b035479 Bump habluetooth to 5.1.0 and bleak-retry-connector to 4.3.0 (#150962) 2025-08-21 10:19:39 -05:00
Simone Chemelli
e175e3ed0b Enable country site autodetection in Alexa Devices (#150989) 2025-08-21 17:10:27 +02:00
Bram Kragten
7cd767e920 Update frontend to 20250811.1 (#151005) 2025-08-21 17:02:02 +02:00
Calvin Walton
4c39936b81 Matter valve Open command doesn't support TargetLevel=0 (#150922) 2025-08-21 16:19:14 +02:00
elsi06
d90590b228 Bump python-mystrom to 2.5.0 (#150947) 2025-08-21 08:48:03 -05:00
J. Nick Koston
7914e6b135 Bump onvif-zeep-async to 4.0.4 (#150969) 2025-08-21 08:47:14 -05:00
Martin Hjelmare
1d646d06a6 Ask user for Z-Wave RF region if country is missing (#150959)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2025-08-21 14:44:19 +02:00
LG-ThinQ-Integration
135c80d194 Initialize the coordinator's data to include data.options. (#150839)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-08-21 13:40:22 +02:00
Imeon-Energy
aed853267a Fix update retry for Imeon inverter integration (#150936)
Co-authored-by: TheBushBoy <theodavid@icloud.com>
2025-08-21 13:31:06 +02:00
Erik Montnemery
62be31f899 Add test of automower_ble activity mapping (#150983)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 10:59:19 +02:00
Erik Montnemery
ca75581d43 Bump automower-ble to 0.2.7 (#150979) 2025-08-21 10:59:10 +02:00
Erik Montnemery
f8a8fb516b Except ujson from license check (#150980) 2025-08-21 10:33:37 +02:00
Andrea Turri
f9575a3b2f Add "profile" extra attribute to Miele program sensor on coffee machines (#145073) 2025-08-21 10:19:13 +02:00
Erik Montnemery
bbde98bc9f Bump pychromecast to 14.0.9 (#150939) 2025-08-21 10:18:29 +02:00
Paulus Schoutsen
90e8d74fcd Add HA version to device analytics (#150877) 2025-08-21 09:44:17 +02:00
dependabot[bot]
9489c19598 Bump github/codeql-action from 3.29.9 to 3.29.10 (#150913)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-21 09:42:36 +02:00
Erik Montnemery
210ec28f0a Fix bluetooth tests (#150978) 2025-08-21 09:38:42 +02:00
dependabot[bot]
88d853cfbd Bump codecov/codecov-action from 5.4.3 to 5.5.0 (#150977)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-21 09:26:35 +02:00
Yevhenii Vaskivskyi
069b21a5a5 Create a special cookie jar for aiohttp client session in asuswrt (#150973) 2025-08-21 06:35:12 +02:00
Ludovic BOUÉ
da864ca034 Matter Refrigerator DoorOpen alarm (#150759)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-20 23:01:03 +02:00
Joakim Plate
8d6b7bf950 Fix event affecting multiple probes (#150954) 2025-08-20 20:40:16 +02:00
Maciej Bieniek
cf8e7cfd28 Bump imgw-pib to version 1.5.4 (#150930) 2025-08-20 14:08:47 +03:00
Marc Mueller
8e5f9264b6 Fix togrill test warning (#150933) 2025-08-20 11:20:55 +02:00
J. Nick Koston
3d0ecf0585 Bump ESPHome minimum stable BLE version to 2025.8.0 (#150924) 2025-08-20 10:25:17 +03:00
JP-Ellis
19b2c6da23 fix(amberelectric): add request timeouts (#150613)
Signed-off-by: JP-Ellis <josh@jpellis.me>
2025-08-20 07:59:33 +02:00
Tobias Sauerwein
0b231ff042 update pyatmo to v9.2.3 (#150900) 2025-08-20 07:46:51 +02:00
Joost Lekkerkerker
d8ae89be6a Bump pysmartthings to 3.2.9 (#150892) 2025-08-20 07:45:35 +02:00
Keilin Bickar
e96ff77cbf Bump asyncsleepiq dependency to 1.6.0 (#150915) 2025-08-20 07:45:06 +02:00
Denis Shulyaka
9797d391af OpenAI external tools (#150599) 2025-08-19 23:36:23 +02:00
Paulus Schoutsen
e68df66028 Fix structured output object selector conversion for OpenAI (#150916) 2025-08-19 22:17:30 +02:00
dependabot[bot]
8d30d69af5 Bump actions/dependency-review-action from 4.7.1 to 4.7.2 (#150904)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 19:41:37 +02:00
J. Nick Koston
93e9fab6c7 Bump bleak-retry-connector to 4.0.2 (#150899) 2025-08-19 19:21:37 +02:00
Jan Bouwhuis
48300f4563 Use greek small letter mu "\u03bc" instead of micro sign "\u00B5" for micro unit prefix (alt 1) (#144853)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-08-19 18:48:50 +02:00
Erik Montnemery
48091e5995 Improve test of WS command get_services (#150901)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-19 17:47:12 +02:00
Erik Montnemery
26582cecbd Improve test of REST endpoint /api/services (#150897) 2025-08-19 17:32:52 +02:00
jan iversen
7ecf32390c Modbus: Avoid duplicate updates. (#150895) 2025-08-19 17:15:51 +02:00
Luke Heckman
cded163930 Update contributing guide links (#150159)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-19 17:15:31 +02:00
rossfoss
10fe479311 Add new attributes to Met Éireann (#150653)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-19 17:04:35 +02:00
Stefan Agner
65696f9b53 Bump aiohasupervisor from version 0.3.1 to version 0.3.2b0 (#150893) 2025-08-19 16:55:36 +02:00
Stefan Agner
08fc2ab03b Add missing unsupported reasons to list (#150866) 2025-08-19 16:55:16 +02:00
jan iversen
2290940638 Modbus: Remove unused variable. (#150894) 2025-08-19 16:54:05 +02:00
Matthias Alphart
63640af4d4 Update voluptuous-serialize to 2.7.0 (#150822) 2025-08-19 16:42:49 +02:00
Paulus Schoutsen
f4400516b8 Fix PWA theme color to match darker blue color scheme in 2025.8 (#150896)
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-19 17:24:32 +03:00
Thomas D
b52a806b36 Show charging power as 0 when not charging for the Volvo integration (#150797) 2025-08-19 16:23:30 +02:00
Paul Warren
76f3397aa0 Bump pyDaikin to 2.16.0 (#150867) 2025-08-19 16:12:53 +02:00
Retha Runolfsson
89abe65e1d Add air purifier for switchbot cloud integration (#147001) 2025-08-19 16:02:18 +02:00
jan iversen
785c9ebc3b Modbus: Retry primary connect. (#150853) 2025-08-19 15:03:05 +02:00
Joakim Plate
a08be4fcb6 Add event entity to Togrill (#150812) 2025-08-19 15:01:57 +02:00
markhannon
e8409e7c42 Bump to zcc-helper==3.6 (#150608) 2025-08-19 14:40:12 +02:00
epenet
c46618cbd1 Bump renault-api to 0.4.0 (#150624) 2025-08-19 14:37:56 +02:00
Alexandre CUER
319e37384f Migrate Emoncms_history to external async library (#149824) 2025-08-19 14:32:13 +02:00
Imeon-Energy
4c1788e757 Add sensors to Imeon inverter integration (#146437)
Co-authored-by: TheBushBoy <theodavid@icloud.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-19 14:21:33 +02:00
epenet
e6a158b1ac Add temperature sensor to Tuya solar inverters (#150878) 2025-08-19 14:20:29 +02:00
epenet
69dbcb0627 Add sound switch to Tuya fan light (#150879) 2025-08-19 14:20:05 +02:00
Andrew Jackson
c62c52e8cf Bump mastodon.py to 2.1.1 (#150876) 2025-08-19 14:19:44 +02:00
epenet
c18dc9b63b Fix icloud service calls (#150881) 2025-08-19 14:19:10 +02:00
Matrix
69757bed52 Support for YoLink YS4102 YS4103 (#150464) 2025-08-19 11:35:36 +02:00
epenet
899f0e03c1 Add Tuya test fixtures (#150835) 2025-08-19 11:34:25 +02:00
Imeon-Energy
fc5e720764 Use correct unit and class for the Imeon inverter sensors (#150847)
Co-authored-by: TheBushBoy <theodavid@icloud.com>
2025-08-19 10:19:03 +02:00
Noah Husby
0c0e54b541 Bump aiorussound to 4.8.1 (#150858) 2025-08-19 09:45:42 +02:00
Retha Runolfsson
86e3eca57f Bump pyswitchbot to 0.68.4 (#150871) 2025-08-19 09:43:35 +02:00
Martin Hjelmare
15505cdd56 Handle Z-Wave RssiErrorReceived (#150846) 2025-08-18 22:14:52 +02:00
G Johansson
c7001dcfc4 Bump holidays to 0.79 (#150857) 2025-08-18 21:40:43 +02:00
epenet
40feefc0fa Cleanup sw_version in Renault (#150844) 2025-08-18 22:28:58 +03:00
Marc Mueller
f6d23b9b34 Check for forbidden files in dependencies with hassfest (#150772) 2025-08-18 17:44:31 +02:00
Andrew Jackson
ab4aeb65f2 Bump mastodon.py to 2.1.0 and change quality scale (#150836) 2025-08-18 17:35:37 +02:00
Foscam-wangzhengyu
019c4ab874 Bump libpyfoscamcgi to 0.0.7 (#150829) 2025-08-18 17:34:00 +02:00
jan iversen
53ca369395 Do not start modbus update process until connection+delay. (#150796) 2025-08-18 17:20:41 +02:00
Manu
9910480980 Bump aiontfy to v0.5.4 (#150825) 2025-08-18 12:06:09 +02:00
Maciej Bieniek
7ecbe53b15 Bump brother to version 5.0.1 (#150840) 2025-08-18 12:05:10 +02:00
Maciej Bieniek
6baa162963 Bump brother to version 5.0.1 (#150840) 2025-08-18 12:04:52 +02:00
Ludovic BOUÉ
2f5561aeba Matter Custom Eve Weather trend (#147620) 2025-08-18 11:32:08 +02:00
Erik Montnemery
330bb46cf9 Revert "Bump automower-ble to 0.2.7" (#150833) 2025-08-18 10:07:57 +02:00
Joost Lekkerkerker
2f8ddae24d Include device data in Withings diagnostics (#150816) 2025-08-18 10:04:54 +02:00
Joost Lekkerkerker
419315d9cf Clean up freebox entity (#150695) 2025-08-18 09:47:02 +02:00
Joost Lekkerkerker
9138930cb9 Abort Nanoleaf discovery flows with user flow (#150818) 2025-08-18 09:41:37 +02:00
Joost Lekkerkerker
a325596898 Bump yt-dlp to 2025.08.11 (#150821) 2025-08-18 09:39:08 +02:00
Claudio Ruggeri - CR-Tech
5fdb95e83c Fix Modbus issue 150453: correct transition update for climate without HVAC mode enabled (#150522)
Co-authored-by: jan iversen <jancasacondor@gmail.com>
2025-08-18 08:49:09 +02:00
Joost Lekkerkerker
fcbfca52f3 Bump spotifyaio to 1.0.0 (#150820) 2025-08-18 08:28:58 +02:00
Yuxin Wang
f44578f45f Add more exception types for cannot_connect test in APCUPSD (#150830) 2025-08-18 07:19:47 +02:00
Yuxin Wang
2b7bd923d6 Add a base entity to APCUPSD integration (#150828) 2025-08-18 07:18:08 +02:00
Joakim Plate
3ab4fd3035 Add number entity to togrill (#150609) 2025-08-17 23:48:53 +02:00
tronikos
794deaa5fd Bump opower to 0.15.2 (#150809) 2025-08-17 23:48:14 +02:00
Pete Sage
79bbae2fde Change the default name of the speech enhancement select for Sonos (#150815) 2025-08-17 23:41:51 +02:00
Pete Sage
9f17a8a943 Add tests and improve error handling for Sonos update_alarm service call (#150715) 2025-08-17 22:47:45 +02:00
Joost Lekkerkerker
b44c47cd80 Removing myself as codeowner of Enphase (#150811) 2025-08-17 22:35:23 +02:00
Joost Lekkerkerker
e80c090932 Pin gql to 3.5.3 (#150800) 2025-08-17 19:27:17 +02:00
Pete Sage
ff418f513a Add dialog mode select for Sonos Arc Ultra soundbar (#150637)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-17 17:15:29 +02:00
Joost Lekkerkerker
b222cc5889 Use lifecycle hook instead of storing callback in starline (#150707) 2025-08-17 17:08:35 +02:00
Yuxin Wang
db1707fd72 Mark config-flow-test-coverage as done in APCUPSD quality scale (#150733) 2025-08-17 17:08:25 +02:00
peteS-UK
6f6f5809d0 Fix volume step error in Squeezebox media player (#150760) 2025-08-17 17:07:23 +02:00
Aidan Timson
1f43f82ea6 Update systembridgeconnector to 4.1.10 (#150736) 2025-08-17 17:03:46 +02:00
Yevhenii Vaskivskyi
942274234e Add asusrouter logger definition to asuswrt (#150747) 2025-08-17 16:59:02 +02:00
Maciej Bieniek
f03955b773 NextDNS tests improvements (#150791) 2025-08-17 16:56:25 +02:00
jan iversen
27ac375183 Remove unused strings in modbus (#150795) 2025-08-17 16:21:28 +02:00
Jamin
c951728767 VOIP RTP cleanup (#150490) 2025-08-17 16:16:20 +02:00
Paulus Schoutsen
3496494290 Remove filters from device analytics payload (#150771) 2025-08-17 16:15:02 +02:00
jan iversen
e90183391e Modbus: Delay start after connection is made. (#150526) 2025-08-17 16:09:24 +02:00
Joost Lekkerkerker
90558c517b Add info to Bravia device (#150690) 2025-08-17 15:30:46 +02:00
epenet
7fba94747e Add Tuya test fixtures (#150793) 2025-08-17 14:05:58 +02:00
Thomas Schamm
3b4b478afa Fix for bosch_shc: 'device_registry.async_get_or_create' referencing a non existing 'via_device' (#150756) 2025-08-17 10:49:04 +02:00
Joakim Plate
a3640c5664 feat: switch to model id for togrill (#150750) 2025-08-17 06:30:05 +02:00
Michael
246a181ad4 Fix restrict-task-creation workflow (#150774) 2025-08-17 01:56:56 +02:00
Thomas Schamm
d642ecb302 Bump boschshcpy to 0.2.107 (#150754) 2025-08-17 00:37:44 +02:00
Yevhenii Vaskivskyi
53889165b5 Bump asusrouter to 1.19.0 (#150742) 2025-08-16 21:32:27 +02:00
Marc Mueller
fe32e74910 Update charset-normalizer to 3.4.3 (#150770) 2025-08-16 21:31:14 +02:00
dontinelli
a71ae4db37 Add min/max values as extra attributes for measurements for fyta (#150562) 2025-08-16 20:49:55 +02:00
Marc Mueller
0d5ebdb692 Update hassfest package exceptions (#150744) 2025-08-16 12:52:26 +02:00
Denis Shulyaka
80e720f663 Add external tools support for chat log (#150461) 2025-08-16 12:20:20 +02:00
epenet
616b031df8 Use constants in Tuya tests (#150739) 2025-08-16 11:00:08 +02:00
Tom
bcdece4455 Add additional sensors to airOS (#150712) 2025-08-16 08:43:47 +02:00
Joost Lekkerkerker
1aa3efaf8a Add support for fineDustSensor capability in SmartThings (#150714) 2025-08-16 08:41:28 +02:00
Luke Lashley
7f16b11776 Improve roborock resume cleaning logic (#150726) 2025-08-16 08:40:46 +02:00
Maciej Bieniek
078b7224fc Add "bypass age verification" switch to NextDNS integration (#150716) 2025-08-15 21:46:06 +03:00
Denis Shulyaka
d5970e7733 Anthropic thinking content (#150341) 2025-08-15 15:52:36 +02:00
Joost Lekkerkerker
d5a74892e6 Remove unnecessary hass assignment in coordinators (#150696)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-15 15:52:13 +02:00
Joost Lekkerkerker
793a829236 Add serial number to Vodafone Station device (#150709) 2025-08-15 15:52:01 +02:00
G Johansson
7670146faf Improve handling decode errors in rest (#150699) 2025-08-15 15:51:48 +02:00
Nick Kuiper
eaedefe105 Update bluecurrent-api to 1.3.1 (#150559) 2025-08-15 15:45:40 +02:00
Marc Mueller
4f20776e0e Add check for dependency package names in hassfest (#150630) 2025-08-15 15:44:47 +02:00
epenet
6c21a14be4 Add binary sensor to 1-Wire DS2405 (#150679) 2025-08-15 15:37:34 +02:00
Alex Thompson
9015743483 Bump tilt-ble to 0.3.1 (#150711) 2025-08-15 15:37:08 +02:00
Thomas D
2a62e033dd Add binary sensor platform to qbus integration (#149975) 2025-08-15 15:35:51 +02:00
Joost Lekkerkerker
f72f2a326a Add MAC address to Modern forms devices (#150698) 2025-08-15 15:34:31 +02:00
Joost Lekkerkerker
61de50dfc0 Add hw_version to Point device (#150704) 2025-08-15 15:34:10 +02:00
Joost Lekkerkerker
ef7ed026db Add serial number to Ondilo ICO (#150702) 2025-08-15 15:33:13 +02:00
Joost Lekkerkerker
abdb48e7ce Add serial number to Nobo hub devices (#150700) 2025-08-15 15:32:43 +02:00
Joost Lekkerkerker
9646aa232a Add serial number to Zeversolar device (#150710) 2025-08-15 15:31:29 +02:00
Joost Lekkerkerker
635cfe7d17 Remove hass assignment in Openhome (#150703) 2025-08-15 15:30:01 +02:00
Joost Lekkerkerker
1e2f7cadc7 Add unregister hook to Vera (#150708) 2025-08-15 15:27:49 +02:00
Tom
94e9f32da5 Bump airOS to 0.3.0 (#150693) 2025-08-15 15:24:23 +02:00
Maciej Bieniek
b7ba99ed17 Bump nextdns to version 4.1.0 (#150706) 2025-08-15 15:24:05 +02:00
Joost Lekkerkerker
ebbeef8021 Add mac to Ambient station device (#150689) 2025-08-15 15:15:22 +02:00
Joost Lekkerkerker
8da75490c0 Add hw_version to RainMachine device (#150705) 2025-08-15 15:04:59 +02:00
Joost Lekkerkerker
bc89e8fd3c Move Notion hardware revision to hw_version (#150701) 2025-08-15 15:03:30 +02:00
Joost Lekkerkerker
602497904b Set firmware version to the right field in Guardian (#150697) 2025-08-15 15:01:42 +02:00
G Johansson
facf217b99 Fix missing labels for subdiv in workday (#150684) 2025-08-15 13:59:35 +02:00
Joost Lekkerkerker
b300654e15 Add serial number to Dremel device (#150691) 2025-08-15 13:58:44 +02:00
Joost Lekkerkerker
a742125f13 Add serial number to Emonitor device (#150692) 2025-08-15 13:58:23 +02:00
Thomas D
64768b1036 Fix re-auth flow for Volvo integration (#150478) 2025-08-15 13:58:03 +02:00
Petro31
792bb5781d Fix optimistic set to false for template entities (#150421) 2025-08-15 13:53:48 +02:00
Jan Bouwhuis
7bd126dc8e Assert the MQTT config entry is reloaded on subentry creation and mutation (#150636) 2025-08-15 13:04:12 +02:00
Joakim Sørensen
83ee380b17 Bump hass-nabucasa from 0.111.2 to 1.0.0 and refactor related code (#150566) 2025-08-15 11:35:52 +02:00
Ludovic BOUÉ
58f8b3c401 Bump Python Matter server to 8.1.0 (#150631) 2025-08-15 11:29:49 +02:00
Marc Mueller
2a6d1180f4 Update py-madvr2 to 1.6.40 (#150647) 2025-08-15 08:13:22 +02:00
J. Nick Koston
00b765893d Bump onvif-zeep-async to 4.0.3 (#150663) 2025-08-15 05:49:31 +02:00
karwosts
3e9e9b0489 Fix demo media_player.browse browsing (#150669) 2025-08-15 05:47:55 +02:00
Luke Lashley
25f7c02498 Bump python-snoo to 0.8.3 (#150670) 2025-08-15 05:46:59 +02:00
Manu
a785f3d509 Increase test coverage of Habitica (#150671) 2025-08-15 05:45:42 +02:00
J. Nick Koston
9f36b2dcde Bump protobuf to 6.32.0 (#150667) 2025-08-15 02:31:10 +02:00
Michael Hansen
57265ac648 Add fuzzy matching to default agent (#150595) 2025-08-14 16:28:42 -05:00
J. Nick Koston
f5fe53a67f Bump uiprotect to 7.21.1 (#150657) 2025-08-14 16:16:04 -05:00
Arie Catsman
7e6ceee9d1 Add IQ Meter Collar and C6 Combiner to enphase_envoy integration (#150649) 2025-08-14 15:34:37 -05:00
DeerMaximum
9c21965a34 Add diagnostics to NINA (#150638) 2025-08-14 19:57:33 +02:00
rwrozelle
1ea740d81c Add media_player add off on capability to esphome (#147990) 2025-08-14 12:07:01 -05:00
rwrozelle
6e98446523 Media player API enumeration alignment and feature flags (#149597)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-08-14 11:24:43 -05:00
Ludovic BOUÉ
2248584a0f Add Matter Electrical measurements additional attributes (#150188)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-08-14 17:07:18 +02:00
Martin Hjelmare
d9b6f82639 Add Z-Wave Fortrezz SSA2 discovery (#150629) 2025-08-14 17:37:44 +03:00
DeerMaximum
3eecfa8e57 Set PARALLEL_UPDATES in NINA (#150635) 2025-08-14 16:36:04 +02:00
epenet
382e7dfd39 Add Tuya test fixtures (#150622) 2025-08-14 14:51:43 +02:00
Joost Lekkerkerker
5358c89bfd Add fixtures for one door refrigerator in SmartThings (#150632) 2025-08-14 14:51:20 +02:00
Tom
e6103fdcf4 Bump airOS to 0.2.11 (#150627) 2025-08-14 13:43:32 +02:00
Martin Dybal
02dca5f0ad Fix type annotation for climate _attr_current_humidity (#150615) 2025-08-14 12:55:54 +02:00
Ludovic BOUÉ
cc4b9e0eca Extend UnitOfReactivePower with 'mvar' (#150415) 2025-08-14 11:46:06 +02:00
Joost Lekkerkerker
7e28e3dcd3 Add sw_version to JustNimbus device (#150592) 2025-08-14 09:31:43 +02:00
Joakim Plate
bb3d571887 Make sure we update the api version in philips_js discovery (#150604) 2025-08-14 09:30:47 +02:00
Joakim Plate
5a789cbbc8 Bump togrill to 0.7.0 in preperation for number (#150611) 2025-08-14 09:30:02 +02:00
dependabot[bot]
4954c2a84b Bump actions/ai-inference from 1.2.8 to 2.0.0 (#150619)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 09:27:18 +02:00
G Johansson
f28e9f60ee Use runtime_data in pvpc_hourly_pricing (#150565) 2025-08-14 01:03:04 +02:00
Luke Lashley
6a4bf4ec72 Bump python-snoo to 0.8.2 (#150569) 2025-08-14 00:12:18 +02:00
Luke Lashley
12706178c2 Change Snoo to use MQTT instead of PubNub (#150570) 2025-08-14 00:11:52 +02:00
Samuel Xiao
ed39b18d94 Add cover platform for switchbot cloud (#148993) 2025-08-14 00:10:19 +02:00
G Johansson
9999807891 Use OptionsFlowWithReload in coinbase (#150587) 2025-08-13 23:48:20 +02:00
Arie Catsman
b5db0e98b4 Bump pyenphase to 2.3.0 (#150600) 2025-08-13 23:44:07 +02:00
Åke Strandberg
f58b2177a2 Bump pymiele to 0.5.4 (#150605) 2025-08-13 23:42:47 +02:00
G Johansson
4f64014816 Add wind gust sensor to OpenWeatherMap (#150607) 2025-08-13 23:34:12 +02:00
Michael Hansen
cf68214c4d Bump hassil to 3.1.0 (#150584) 2025-08-13 20:58:57 +02:00
Marc Mueller
b3d3284f5c Update types packages (#150586) 2025-08-13 20:55:22 +02:00
Marc Mueller
12c346f550 Update orjson to 3.11.2 (#150588) 2025-08-13 20:53:55 +02:00
HarvsG
bda82e19a5 Pi_hole - Account for auth succeeding when it shouldn't (#150413) 2025-08-13 20:53:21 +02:00
Marc Mueller
f7726a7563 Update pre-commit-hooks to 6.0.0 (#150583) 2025-08-13 19:23:26 +02:00
Michael Hansen
2c0ed2cbfe Add intent for setting fan speed (#150576) 2025-08-13 18:57:25 +02:00
Marc Mueller
13376ef896 Fix RuntimeWarning in asuswrt tests (#150580) 2025-08-13 18:33:02 +02:00
Marc Mueller
d18cc3d6c3 Fix RuntimeWarning in squeezebox tests (#150582) 2025-08-13 18:32:50 +02:00
karwosts
b40aab479a Change monetary translation to 'Monetary balance' (#150054) 2025-08-13 17:21:36 +02:00
Michael Hansen
721f9a40d8 Add volume up/down intents for media players (#150443) 2025-08-13 09:35:37 -05:00
Ludovic BOUÉ
eb4b75a9a7 Extend UnitOfApparentPower with 'mVA' (#150422)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-08-13 15:56:04 +02:00
epenet
b40f381164 Add Tuya test fixture (#150557) 2025-08-13 14:09:19 +02:00
epenet
51413b7a8d Ensure Tuya fans have at least one valid DPCode (#150550) 2025-08-13 13:40:11 +02:00
Foscam-wangzhengyu
ff694a0058 Foscam Add prompt language and modify the default port to a more compatible (#150536) 2025-08-13 13:21:39 +02:00
Joakim Sørensen
eea04558a9 Move alexa access token updates to new handler (#150466)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-08-13 13:21:28 +02:00
starkillerOG
5ad2a27918 Use camera name in Reolink tests (#150555) 2025-08-13 13:06:12 +02:00
G Johansson
f39305f64e Remove deprecated json helper constants and function (#150111) 2025-08-13 12:42:00 +02:00
karwosts
7fba0ca2c0 Add 'all' option to light/switch group config flow (#149671) 2025-08-13 12:34:58 +02:00
Pete Sage
51fbccd125 Fix Sonos CI issue part 2 (#150529)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-08-13 12:26:24 +02:00
G Johansson
5fc2e6ed53 Add async_update_reload_and_abort to config entry subentries (#149768)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2025-08-13 11:59:37 +02:00
Christopher Fenner
5a7f7d90a0 move Volvo car connection status sensor to diagnostic section (#150487) 2025-08-13 11:45:05 +02:00
Luke Lashley
6d34d34ce1 Bump python-snoo to 0.8.1 (#150530) 2025-08-13 11:38:18 +02:00
dependabot[bot]
6454f40c3c Bump github/codeql-action from 3.29.8 to 3.29.9 (#150539)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 11:36:43 +02:00
epenet
53e40a6b8c Ensure Tuya humidifiers have at least one valid DPCode (#150546) 2025-08-13 11:25:59 +02:00
J. Nick Koston
8a54a1d95c Bump aioesphomeapi to 39.0.0 (#150523) 2025-08-13 03:17:20 -05:00
Yevhenii Vaskivskyi
8a52e9ca01 Bump asusrouter to 1.18.2 (#150541) 2025-08-13 10:46:08 +03:00
Robert Resch
d9ca253c6c Bump uv to 0.8.9 (#150542) 2025-08-13 09:45:54 +02:00
Pete Sage
b7853ea9bd Fix Sonos CI Issue (#150518)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-12 23:18:59 +02:00
J. Nick Koston
d19e410ea8 Bump aiodhcpwatcher to 1.2.1 (#150519) 2025-08-12 22:07:25 +02:00
Shay Levy
83f911e4ff Bump aiowebostv to 0.7.5 (#150514) 2025-08-12 22:53:56 +03:00
jan iversen
452322e971 Modbus: Do not remove non-duplicate error log. (#150511) 2025-08-12 21:16:43 +02:00
Manu
6fa7c6cb81 Add party to Habitica (#149608) 2025-08-12 20:51:12 +02:00
J. Nick Koston
ed6072d46b Bump bleak-retry-connector to 4.0.1 (#150515) 2025-08-12 20:49:43 +02:00
Yevhenii Vaskivskyi
9fdc632780 Switch asuswrt http(s) library to asusrouter package (#150426) 2025-08-12 20:45:39 +02:00
Norbert Rittel
4d426c31f9 Fix missing sentence-case in hydrawise (#150513) 2025-08-12 20:10:43 +02:00
jan iversen
ea946c90b3 Modbus: Cancel connect background task if stopping/restarting. (#150507) 2025-08-12 19:38:17 +02:00
Tom
fb68b2d454 Bump airOS to 0.2.8 (#150504) 2025-08-12 19:27:27 +02:00
Ludovic BOUÉ
2ebe0a929e Matter SmokeCoAlarm SelfTestRequest (#150497) 2025-08-12 19:10:55 +02:00
Manu
c1e5a7efc9 Add icons to Sleep as Android sensor entities (#150451) 2025-08-12 18:23:27 +02:00
dependabot[bot]
561ef7015c Bump actions/checkout from 4.2.2 to 5.0.0 (#150494)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 18:17:44 +02:00
Robin Lintermann
b4270e019e Bump pysmarlaapi to 0.9.2 (#150496) 2025-08-12 18:14:32 +02:00
Joost Lekkerkerker
614bf96fb9 Add model_id to Philips Hue (#150499) 2025-08-12 18:09:14 +02:00
Tucker Kern
ca290ee631 Implement Snapcast grouping with standard HA actions (#146855)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-12 18:07:29 +02:00
epenet
ad3174f6e6 Rename Tuya parsing models (#150498) 2025-08-12 18:02:26 +02:00
jan iversen
218b0738ca Modbus: Remove wrong comment on non-existing parameter. (#150501) 2025-08-12 18:00:51 +02:00
Joakim Plate
98e6e20079 Mock habluetooth adapters (#148919) 2025-08-12 10:46:31 -05:00
Norbert Rittel
89aa349881 Fix spelling of "an HS color command" in template (#150495) 2025-08-12 17:18:27 +02:00
wedsa5
07930b12d0 Fix brightness command not sent when in white color mode (#150439)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-12 16:36:52 +02:00
Renat Sibgatulin
711afa306c Add number platform for LED brightness to air-Q (#150492) 2025-08-12 15:39:28 +02:00
epenet
a3904ce60c Sort Tuya DPCodes alphabetically (#150477) 2025-08-12 15:28:42 +02:00
hanwg
455cf2fb42 Add notify platform for Telegram bot (#149853)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-12 15:24:13 +02:00
Aarni Koskela
072ae2b955 ruuvitag_ble: add new sensors (#150435)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-08-12 14:19:15 +02:00
epenet
2b70639b11 Add device registry snapshots to Tuya (#150482)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-12 14:04:36 +02:00
Åke Strandberg
2612dbeb9b Add missing boost2 code for Miele hobs (#150481) 2025-08-12 13:58:38 +02:00
Matrix
7ebdd24224 Bump yolink api to 0.5.8 (#150480) 2025-08-12 13:55:04 +02:00
Martin Hjelmare
66ff1cf005 Improve Z-Wave manual config flow step description (#150479) 2025-08-12 13:47:11 +02:00
David
08aae4bf49 Fix error of the Powerfox integration in combination with the new Powerfox FLOW adapter (#150429)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-12 12:45:21 +02:00
Etienne C.
313b5a483c Remove rounding of Waze duration sensor (#150424) 2025-08-12 12:20:48 +02:00
Arie Catsman
8edbcc92d3 Fix enphase_envoy non existing via device warning at first config. (#149010)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-12 11:55:43 +02:00
peteS-UK
067cab71fa Additional Fix error on startup when no Apps or Radio plugins are installed for Squeezebox (#150475) 2025-08-12 11:55:21 +02:00
Nippey
596e4883b1 Add more sensors to Tuya weather station (#150442)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-12 11:33:51 +02:00
yufeng
fb4a452872 Add supply frequency sensors to Tuya energy monitoring devices (#149320)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-12 11:02:03 +02:00
yufeng
5b232226e9 Add timers and switches to Tuya irrigation systems (#149236) 2025-08-12 10:53:08 +02:00
J. Nick Koston
db81610983 Bump aioesphomeapi to 38.2.1 (#150455) 2025-08-12 10:46:53 +02:00
epenet
8f5c8caf07 Add mute switch to Tuya smoke detectors (#150469) 2025-08-12 10:45:39 +02:00
Matrix
f6af524ddf Fix YoLink valve state when device running in class A mode (#150456) 2025-08-12 10:42:40 +02:00
Norbert Rittel
e0a8c9b458 Fix missing sentence-casing in somfy_mylink (#150463) 2025-08-12 10:30:38 +02:00
Cyrill Raccaud
c46412ee5b Bump cookidoo-api to 0.14.0 (#150450) 2025-08-12 09:51:39 +02:00
Mike Degatano
a06df2a680 Make disk_lifetime issue into a repair (#150140) 2025-08-12 08:39:37 +02:00
epenet
68fbcc8665 Add pymodbus to package constraints (#150419)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-12 00:50:05 +02:00
Manu
6cde5cfdcc Add diagnostics platform to Sleep as Android (#150447) 2025-08-11 23:47:07 +02:00
Wesley Vos
5605f5896a Remove the battery feature from supported features (#150101) 2025-08-11 23:26:27 +02:00
Manu
93c30f1b59 Add sensor platform to Sleep as Android (#150440) 2025-08-11 23:25:51 +02:00
Pete Sage
6e3ccbefc2 Add quality scale for Sonos (#144928) 2025-08-11 22:50:47 +02:00
Noah Husby
715dc12792 Add media browsing to Russound RIO (#148248) 2025-08-11 22:40:40 +02:00
Denis Shulyaka
9cae0e0acc OpenAI thinking content (#150340) 2025-08-11 22:28:36 +02:00
Kevin David
e13702d9b1 Bump python-snoo to 0.7.0 (#150434) 2025-08-11 22:25:41 +02:00
Tsvi Mostovicz
3b358df9e7 Jewish Calendar add coordinator (#141456)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-11 22:22:13 +02:00
Foscam-wangzhengyu
e394435d7c Add more Foscam switches (#147409)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-08-11 22:14:32 +02:00
Aarni Koskela
9e398ffc10 Bump to ruuvitag-ble==0.2.1 (#150436) 2025-08-11 22:05:44 +02:00
tdfountain
065a53a90d Add quality scale and set Platinum for NUT (#143269)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-08-11 21:27:33 +02:00
Manu
91f6b8e1fe Add Sleep as Android integration (#142569) 2025-08-11 20:03:37 +02:00
Michael Hansen
1a9d1a9649 Handle non-streaming TTS case correctly (#150218) 2025-08-11 11:47:29 -05:00
MB901
cb7c7767b5 Add model_id for Freebox integration (#150430) 2025-08-11 18:46:57 +02:00
CubeZ2mDeveloper
d02029143c Add SONOFF Dongle Lite MG21 discovery support in ZHA (#148813)
Co-authored-by: zetao.zheng <1050713479@qq.com>
2025-08-11 12:41:41 -04:00
Robin Lintermann
3eda687d30 Smarla integration sensor platform (#145748) 2025-08-11 17:08:07 +02:00
Jamie Magee
7688c367cc Remove coinbase v2 API support (#148387)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-11 16:58:36 +02:00
Manu
a1dc3f3eac Bump habiticalib to version 0.4.2 (#150417) 2025-08-11 15:51:22 +02:00
Martin Hjelmare
d135d08813 Lower Z-Wave firmware check delay (#150411) 2025-08-11 14:09:04 +02:00
Brett Adams
9595759fd1 Add stale device cleanup to Teslemetry (#144523) 2025-08-11 13:54:44 +02:00
Etienne C.
d54f979612 Add a coordinator to Waze Travel Time (#148585) 2025-08-11 13:20:18 +02:00
Paulus Schoutsen
531073acc0 Allow specifying multiple integrations (#150349) 2025-08-11 13:12:29 +02:00
Bouwe Westerdijk
73cbc962f9 Implement snapshot testing for Plugwise binary_sensor platform (#150375) 2025-08-11 13:11:24 +02:00
epenet
34b0b71375 Add Tuya snapshot tests for empty electricity RAW sensors (#150407) 2025-08-11 12:05:33 +02:00
Brett Adams
203c908730 Add charging and preconditioning actions to Teslemetry (#144184) 2025-08-11 11:59:39 +02:00
tronikos
23e6148d3b Create an issue if Opower utility is no longer supported (#150315) 2025-08-11 11:58:12 +02:00
epenet
2a5a66f9d5 Handle empty electricity RAW sensors in Tuya (#150406) 2025-08-11 11:55:47 +02:00
dependabot[bot]
84ce5d65e1 Bump github/codeql-action from 3.29.7 to 3.29.8 (#150405)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 11:50:00 +02:00
Bram Kragten
00c7838587 Update frontend to 20250811.0 (#150404) 2025-08-11 10:58:03 +02:00
Stefan Agner
d8b576c087 Rename local OAuth2 source (#150403) 2025-08-11 10:37:25 +02:00
Tomeroeni
330dce24c5 Bump aiounifi to version 86 (#150321) 2025-08-11 10:32:35 +02:00
karwosts
0089d3efa1 Support multiple for StateSelector (#146288) 2025-08-11 11:24:20 +03:00
Manuel Stahl
167e9c8f4a Update pystiebeleltron to 0.2.3 (#150339)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-11 09:43:09 +02:00
Manu
c7f5e25d41 Update quality scale to platinum 🏆️ for Uptime Kuma (#148951) 2025-08-10 23:36:57 +02:00
Florian von Garrel
7b5dd4a0ec Paperless-ngx: Disable entities by default and extended docs (#149473) 2025-08-10 23:36:36 +02:00
Denis Shulyaka
84de6aacfc Remove native field from conversation chatlog delta listeners (#150389) 2025-08-10 22:41:37 +02:00
epenet
9561c84920 Fix issue with Tuya suggested unit (#150394) 2025-08-10 22:39:00 +02:00
jan iversen
7572b2a669 Bump pymodbus to v3.11.1. (#150383) 2025-08-10 22:38:49 +02:00
dontinelli
b48409ab1b Add new sensors with battery data for solarlog (#150385)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-10 22:28:50 +02:00
Austin Mroczek
ab04e2c501 TotalConnect major test updates (#139672)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-08-10 22:26:43 +02:00
epenet
38e6a7c6d4 Add Tuya test fixtures (#150387) 2025-08-10 22:17:14 +02:00
Vincent Wolsink
c2b284de2d Add humidity (steamer) control to Huum (#150330)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-10 21:55:20 +02:00
epenet
b760bf342a Add frost protection and valve status to Tuya thermostats (#150177) 2025-08-10 21:37:14 +02:00
Yuxin Wang
79cfea3fea Use mock_setup_entry fixture for APCUPSD (#150392) 2025-08-10 21:35:47 +02:00
dontinelli
69ace08c01 Bump solarlog_cli to 0.5.0 (#150384) 2025-08-10 20:57:03 +02:00
Yuxin Wang
bf33e286d6 Add recovery test logic for connection failure for APCUPSD (#150382)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-10 20:32:25 +02:00
epenet
6b83effc5f Simplify DEVICE_MOCKS in Tuya (#150381) 2025-08-10 17:58:51 +02:00
epenet
2b158fe690 Add Tuya snapshots tests for kt category (air conditioner) (#150256) 2025-08-10 17:45:40 +02:00
epenet
712ddc03c8 Add Tuya snapshots tests for kj category (air purifier) (#150171) 2025-08-10 17:44:55 +02:00
epenet
efe519faad Add mute support to Tuya wg2 category (gateway) (#150122) 2025-08-10 17:44:26 +02:00
epenet
1b7cb418eb Add Tuya snapshots tests for cwysj category (pet water fountain) (#150121) 2025-08-10 17:44:00 +02:00
Yuxin Wang
c678bcd4f1 Split test_config_flow_duplicate tests into two separate ones for APCUPSD (#150379) 2025-08-10 17:35:48 +02:00
Marc Mueller
0eaea13e8d Update pylint to 3.3.8 + astroid to 3.3.11 (#150327) 2025-08-10 16:41:59 +02:00
Norbert Rittel
b1e4513f7d Capitalize "Ice Plus" as feature name in lg_thinq (#150370) 2025-08-10 15:14:40 +02:00
Steven Looman
6d7f8bb7d7 Remove unused string scan_interval in upnp component (#150372) 2025-08-10 15:14:14 +02:00
Norbert Rittel
b481aaba77 Fix wrong translation of unlock_inside_the_door in xiaomi_ble (#150371)
thanks
2025-08-10 11:45:24 +02:00
Alexandre CUER
d539f37aa4 Remove CONF_EXCLUDE_FEEDID constant from the emoncms integration (#150333) 2025-08-10 09:52:17 +02:00
J. Nick Koston
865b3a6646 Add raw advertisement data to Bluetooth WebSocket API (#150358) 2025-08-10 09:44:15 +02:00
Denis Shulyaka
1c603f968f Bump openai to 1.99.5 (#150342) 2025-08-10 09:41:55 +02:00
J. Nick Koston
d821d27730 Bump habluetooth to 5.0.1 (#150320) 2025-08-10 09:41:25 +02:00
tronikos
dfa060a7e1 Remove Mercury NZ Limited virtual integration (#150316) 2025-08-10 09:38:48 +02:00
Alexandre CUER
5262cca8e6 Use "device_id" instead of "slave" in modbus integration (#150200) 2025-08-10 09:31:26 +02:00
Yuxin Wang
2c36a74da5 Also test unique ID in config flow test for APCUPSD (#150362) 2025-08-10 07:49:25 +02:00
G Johansson
084cde6ecf Add base entity to workday (#150329) 2025-08-09 21:52:39 +02:00
Denis Shulyaka
3e34aa5fb7 Add thinking and native content to chatlog (#149699) 2025-08-09 15:26:19 +02:00
Pete Sage
268f0d9e03 Add Tests for Sonos Alarms (#150014) 2025-08-09 13:47:16 +02:00
Thomas D
f8d3bc1b89 Volvo: Skip unsupported API fields (#150285) 2025-08-09 12:24:53 +02:00
Philipp Waller
fb64ff1d17 Update knx-frontend to 2025.8.9.63154 (#150323) 2025-08-09 12:14:31 +02:00
steinmn
ff72faf83a Set suggested display precision on Volvo energy/fuel consumption sensors (#150296) 2025-08-09 07:48:49 +02:00
Tom
acb58c41eb Bump airOS to 0.2.7 supporting firmware 8.7.11 (#150298) 2025-08-09 07:48:05 +02:00
epenet
586b197fc3 Speedup Tuya snapshot tests (#150198) 2025-08-09 07:46:48 +02:00
Manu
5c1d16d582 Abort config flow if user has no friends in PlayStation Network (#150301) 2025-08-09 07:44:35 +02:00
Tom
73be4625ae Add sensor uom suggestions to airOS (#150303) 2025-08-09 07:43:51 +02:00
Andrew Jackson
775701133d Remove deprecated notify platform from Mastodon (#149735) 2025-08-09 01:17:48 +02:00
MB901
1af0282091 Add hardware version to FreeboxRouter device info (#150004) 2025-08-09 00:54:53 +02:00
Joakim Plate
c876bed33f Add ToGrill integration (#150075)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-09 00:24:54 +02:00
G Johansson
e9d39a826e Remove deprecated horizontal vane select from Sensibo (#150108) 2025-08-09 00:24:38 +02:00
Thomas55555
f9e1c07c04 Add event platform to Husqvarna Automower (#148212)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2025-08-09 00:07:47 +02:00
Renat Sibgatulin
c0bef51563 Refactor airq tests to mock the API class in a fixture (#149712) 2025-08-09 00:01:39 +02:00
Willem-Jan van Rootselaar
b41a9575af Add protected call for data retrieval (#150035) 2025-08-08 23:58:19 +02:00
Norbert Rittel
e585b3abd1 Fix missing sentence-casing of "AC failure" in bosch_alarm (#150279) 2025-08-08 23:33:55 +02:00
mbo18
5d2877f454 Add absolute humidity sensor to Awair integration (#150110) 2025-08-08 22:55:24 +02:00
Artur Pragacz
2d89c60ac5 Improve service schemas in unifiprotect (#150236) 2025-08-08 22:51:24 +02:00
Marco Gasparini
860a7b7d91 Fix Progettihwsw config flow (#150149) 2025-08-08 22:29:50 +02:00
Ludovic BOUÉ
5585376b40 Switchbot Hub Light level (#150147) 2025-08-08 22:13:23 +02:00
Thomas55555
c4cb70fc06 Handle HusqvarnaWSClientError (#150145) 2025-08-08 22:12:18 +02:00
Pete Sage
981ae39182 Fix dialog enhancement switch for Sonos Arc Ultra (#150116) 2025-08-08 22:11:32 +02:00
Alexandre CUER
dff4f79925 Remove useless strings from emoncms (#150182) 2025-08-08 22:00:48 +02:00
Manu
bf64e11960 Migrate unique_id only if monitor_id is present in Uptime Kuma (#150197) 2025-08-08 21:38:27 +02:00
Thomas D
823d20c67f Volvo: fix distance to empty battery (#150278) 2025-08-08 21:28:29 +02:00
Norbert Rittel
1a654cd35d Use common strings "Low"/"High" for more states in tuya (#150283) 2025-08-08 20:52:03 +02:00
Denis Shulyaka
13e592edaf Bump anthropic to 0.62.0 (#150284) 2025-08-08 20:51:49 +02:00
Norbert Rittel
94191239c6 Remove misleading "the" from Launch Library configuration (#150288) 2025-08-08 20:50:14 +02:00
Denis Shulyaka
91a1ca09f7 Add GPT-5 support (#150281) 2025-08-08 20:49:09 +02:00
Tom
9f1fe8a067 Add binary_sensor to UISP airOS (#149803)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-08 20:34:40 +02:00
Yuxin Wang
f2c9cdb09e Add quality scale for APCUPSD integration (#146999) 2025-08-08 20:31:34 +02:00
Tom
712115cdb8 Bump airOS to 0.2.6 improving device class matching more devices (#150134) 2025-08-08 19:33:16 +02:00
dependabot[bot]
eb6ae9d2d6 Bump actions/cache from 4.2.3 to 4.2.4 (#150253)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 15:28:51 +02:00
puddly
b126f3fa66 Bump ZHA to 0.0.68 (#150208) 2025-08-08 15:27:17 +02:00
puddly
2d720f0d32 Fix JSON serialization for ZHA diagnostics download (#150210) 2025-08-08 15:27:00 +02:00
Raphael Hehl
c0155f5e80 Handle Unifi Protect BadRequest exception during API key creation (#150223) 2025-08-08 15:26:02 +02:00
Thomas D
23a2d69984 Volvo: fix missing charging power options (#150272) 2025-08-08 15:25:19 +02:00
peteS-UK
a8779d5f52 Fix error on startup when no Apps or Radio plugins are installed for Squeezebox (#150267) 2025-08-08 15:24:41 +02:00
Robert Resch
01c197e830 Constraint num2words to 0.5.14 (#150276) 2025-08-08 15:06:31 +02:00
peteS-UK
ef4f476844 Fix handing for zero volume error in Squeezebox (#150265) 2025-08-08 14:26:04 +02:00
dependabot[bot]
8aee05b8b0 Bump github/codeql-action from 3.29.5 to 3.29.7 (#150254)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 13:57:20 +02:00
Denis Shulyaka
0f3f8d5707 Bump openai to 1.99.3 (#150232) 2025-08-08 13:57:12 +02:00
epenet
2948b1c58e Cleanup Tuya fixture files (#150190) 2025-08-08 13:56:44 +02:00
Joris Pelgröm
4cb2af4d08 Add select platform to LetPot integration (#150212) 2025-08-08 13:47:13 +02:00
G Johansson
8e12d2028d Remove previously deprecated linear_garage_door (#150109)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-08 13:09:01 +02:00
G Johansson
5b046def8e Move holiday object to runtime data in workday (#149122) 2025-08-08 12:02:07 +02:00
Åke Strandberg
6a81bf6f5e Improve interface between Miele integration and pymiele library (#150214) 2025-08-08 11:40:04 +02:00
epenet
102d6a37c0 Use generated device id in tuya tests (#150196) 2025-08-08 09:15:42 +02:00
Yuxin Wang
fd6aba3022 Add missing strings for APCUPSD (#150242) 2025-08-08 08:41:03 +02:00
tronikos
a88eadf863 Update Opower strings (#150247) 2025-08-08 08:40:28 +02:00
Tom
52f0d04c38 Improve Roborock test teardown (#150144) 2025-08-07 20:32:05 -07:00
Denis Shulyaka
3ab80c6ff2 Bump google-genai to 1.29.0 (#150225) 2025-08-07 16:26:02 -07:00
Vincent Wolsink
71485871c8 Bump Huum requirement to 0.8.1 (#150220) 2025-08-07 21:59:58 +01:00
Martin Hjelmare
ba0da4c2a3 Remove switchbot vacuum battery attribute (#150227) 2025-08-07 22:39:45 +02:00
Martin Hjelmare
cbaadebac3 Fix Tibber coordinator ContextVar warning (#150229) 2025-08-07 22:39:24 +02:00
Åke Strandberg
fd0ae32058 Bump pymiele to 0.5.3 (#150216) 2025-08-07 20:48:25 +02:00
Martin Hjelmare
382bf78ee0 Ignore MQTT vacuum battery warning (#150211) 2025-08-07 20:11:39 +02:00
Martin Hjelmare
6aa077a48d Silence vacuum battery deprecation for built in integrations (#150204) 2025-08-07 19:43:36 +02:00
Joakim Sørensen
b638fcbaad Bump hass-nabucasa from 0.111.1 to 0.111.2 (#150209) 2025-08-07 19:42:22 +02:00
G Johansson
704edac9fd Remove deprecated state from backup schedule (#150114)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-08-07 17:42:53 +01:00
yufeng
ff9e2a8f1e Update tuya translation for reverse energy sensor (#149317) 2025-08-07 17:08:57 +02:00
Stefan H.
d778afe61a Fix Enigma2 startup hang (#149756) 2025-08-07 15:33:24 +01:00
Norbert Rittel
448084e2b5 Fix description of button.press action (#150181) 2025-08-07 15:22:36 +02:00
jan iversen
d99379ffdf modbus: use only 1 logger instance. (#150130) 2025-08-07 15:11:00 +02:00
Maciej Bieniek
b835b7f266 Bump imgw_pib to version 1.5.3 (#150178) 2025-08-07 13:31:55 +02:00
epenet
e96e97edca Add Tuya snapshots tests for sj category (rain sensor) (#150173) 2025-08-07 13:24:33 +02:00
epenet
df7c657d7e Add Tuya snapshots tests for wk category (thermostat) (#150175) 2025-08-07 12:53:19 +02:00
epenet
4f5502ab47 Add Tuya snapshots tests for ldcg category (luminance sensor) (#150169) 2025-08-07 12:50:46 +02:00
epenet
c30ee776e9 Add Tuya snapshots tests for zwjcy category (soil sensor) (#150168) 2025-08-07 10:44:51 +02:00
epenet
efebdc0181 Add Tuya snapshots tests for cl category (curtains) (#150167) 2025-08-07 10:42:36 +02:00
jan iversen
da7fc88f1f Bump pymodbus to v3.11.0. (#150129) 2025-08-07 08:13:11 +02:00
Joris Pelgröm
566aeb5e9a Bump letpot to 0.6.1 (#150137) 2025-08-07 08:08:47 +02:00
J. Nick Koston
d17f0ef55a Bump inkbird-ble to 1.1.0 to add support for IAM-T2 (#150158) 2025-08-07 08:07:31 +02:00
Abílio Costa
35025c4b59 Fix roborock config flow tests (#150135) 2025-08-07 00:05:31 +01:00
Abílio Costa
e5d512d5e5 Add entity filter to target state change tracker (#150064)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-06 20:03:09 +02:00
puddly
2b5028bfb7 Bump ZHA to 0.0.67 (#150132) 2025-08-06 19:56:44 +02:00
Paul Bottein
757fee9f73 Use state selector for climate set hvac mode service (#148963) 2025-08-06 17:48:55 +01:00
Artur Pragacz
06130219b4 Use relative condition keys (#150021) 2025-08-06 17:20:30 +01:00
AlCalzone
4e2fe63182 Check for Z-Wave firmware updates of sleeping devices (#150123) 2025-08-06 18:08:51 +02:00
Luca Angemi
d0cc9990dd Deprecate Roborock battery feature (#150126) 2025-08-06 17:32:23 +02:00
epenet
76ca9ce3a4 Add comment to Tuya code for unsupported devices (#150125)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-06 17:12:32 +02:00
epenet
124e7cf4c8 Add support for tuya ywcgq category (liquid level) (#150096)
Thanks @joostlek / @frenck
2025-08-06 15:38:50 +02:00
G Johansson
260ea9a3be Remove previously deprecated raw value attribute from onewire (#150112) 2025-08-06 15:24:22 +02:00
Bram Kragten
e1f6820cb6 Update frontend to 20250806.0 (#150106)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-06 15:22:46 +02:00
David Poll
2215777cfb Fix zero-argument functions with as_function (#150062) 2025-08-06 15:20:03 +02:00
G Johansson
fa3ce62ae8 Bump holidays to 0.78 (#150103) 2025-08-06 14:55:00 +02:00
Joakim Sørensen
33421bddf3 Remove myself as codeowner from traccar_server (#150107) 2025-08-06 14:51:43 +02:00
markhannon
1efe2b437d Improve dependency transparency for Zimi integration (#145879) 2025-08-06 14:50:06 +02:00
Joost Lekkerkerker
a54f0adf74 Enable disabled Ollama config entries after entry migration (#150105) 2025-08-06 14:27:36 +02:00
epenet
afe574f74e Simplify DPCode lookup in Tuya (#150052) 2025-08-06 14:24:01 +02:00
epenet
25aae8944d Add Tuya snapshots tests for mzj category (sous-vide) (#150102) 2025-08-06 14:17:30 +02:00
Martin Hjelmare
f26e6ad211 Fix update coordinator ContextVar log for custom integrations (#150100) 2025-08-06 14:14:42 +02:00
Joost Lekkerkerker
e9444a2e4d Enable disabled Anthropic config entries after entry migration (#150098) 2025-08-06 13:24:49 +02:00
Joost Lekkerkerker
60988534a9 Enable disabled OpenAI config entries after entry migration (#150099) 2025-08-06 13:24:37 +02:00
Michael
932bf81ac8 Add common constant ATTR_CONFIG_ENTRY_ID (#150067) 2025-08-06 12:42:51 +02:00
Jan Bouwhuis
1302b6744e Deprecate MQTT vacuum battery feature and remove it as default feature (#149877)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-08-06 11:51:31 +02:00
tronikos
0aeff366bd Fix PG&E and Duquesne Light Company in Opower (#149658)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-08-06 11:32:42 +02:00
epenet
0db23b0da6 Add Tuya debug logging for new devices (#150091)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-06 11:23:34 +02:00
epenet
863e2074b6 Add more switches to Tuya tdq category (#150090) 2025-08-06 11:03:26 +02:00
epenet
13828f6713 Remove tuya vacuum battery level attribute (#150086) 2025-08-06 11:02:04 +02:00
starkillerOG
fdb38ec8ec Reduce Reolink fimware polling from 12h to 24h (#150095) 2025-08-06 10:58:52 +02:00
Robert Resch
55abb6e594 Fix hassio tests by only mocking supervisor id (#150093) 2025-08-06 10:53:55 +02:00
Stefan Agner
a83e4f5c63 Add missing translations for unhealthy Supervisor issues (#150036) 2025-08-06 10:07:36 +02:00
J. Nick Koston
cba15ee439 Bump habluetooth to 4.0.2 (#150078)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-08-06 09:51:44 +02:00
dependabot[bot]
400620399a Bump actions/download-artifact from 4.3.0 to 5.0.0 (#150084)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 09:48:10 +02:00
dependabot[bot]
28e19215ad Bump actions/ai-inference from 1.2.7 to 1.2.8 (#150083)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 09:46:02 +02:00
Philipp Waller
119d0a0170 Update knx-frontend to 2025.8.6.52906 (#150085) 2025-08-06 09:28:44 +02:00
Joakim Sørensen
69faf38e86 Bump hass-nabucasa from 0.111.0 to 0.111.1 (#150082) 2025-08-06 09:24:09 +02:00
puddly
d0ef1a1a8b Bump ZHA to 0.0.66 (#150081) 2025-08-06 09:22:07 +02:00
Retha Runolfsson
8f328810bf Bump pyswitchbot to 0.68.3 (#150080) 2025-08-05 19:20:37 -10:00
Pete Sage
4f1b75e3b4 Bump soco to 0.30.11 (#150072) 2025-08-05 22:56:27 +01:00
J. Nick Koston
445a7fc749 Bump yalexs to 8.11.1 (#150073) 2025-08-05 22:55:01 +01:00
Robert Svensson
977c0797aa Bump axis to v65 (#150065) 2025-08-05 11:36:48 -10:00
Ludovic BOUÉ
a24f027923 Add icon for esa_state in Matter integration (#149075) 2025-08-05 23:18:48 +02:00
Martin Hjelmare
7b45798e30 Remove matter vacuum battery level attribute (#150061) 2025-08-05 22:40:42 +02:00
Artur Pragacz
2b0cda0ad1 Adjust condition and trigger method names (#150060) 2025-08-05 19:46:03 +01:00
starkillerOG
12dca4b1bf Bump reolink-aio to 0.14.6 (#150055) 2025-08-05 18:58:22 +02:00
karwosts
8c509b11b2 Fix template sensor uom string (#150057) 2025-08-05 18:56:34 +02:00
Joost Lekkerkerker
991c9008bd Change AI task strings (#150051) 2025-08-05 16:35:41 +02:00
Martin Hjelmare
fe95f6e1c5 Improve downloader service (#150046)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-08-05 16:12:55 +02:00
Bram Kragten
37510aa316 Update frontend to 20250805.0 (#150049) 2025-08-05 16:01:47 +02:00
Marc Mueller
4e40e9bf74 Update mypy-dev to 1.18.0a4 (#150005) 2025-08-05 15:56:03 +02:00
Bouwe Westerdijk
70c9b1f095 Implement snapshot testing for Plugwise button platform (#149984) 2025-08-05 15:31:02 +02:00
dependabot[bot]
f714388130 Bump docker/login-action from 3.4.0 to 3.5.0 (#150034)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 15:25:58 +02:00
Joost Lekkerkerker
ffb2a693f4 Ignore vacuum entities that properly deprecate battery (#150043) 2025-08-05 15:22:21 +02:00
Andrew Jackson
9d8e253ad3 Default to zero quantity on new todo items in Mealie (#150047) 2025-08-05 15:15:08 +02:00
dependabot[bot]
31631cc882 Bump actions/ai-inference from 1.2.4 to 1.2.7 (#150038)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 14:40:01 +02:00
epenet
3a64357201 Fix Tuya fan speeds with numeric values (#149971) 2025-08-05 13:22:45 +02:00
Thomas55555
20fdec9e9c Reduce polling in Husqvarna Automower (#149255)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-05 12:56:27 +02:00
Nippey
064a63fe1f Add support for Tuya "Bresser 7-in-1 Weatherstation" (#149498) 2025-08-05 12:54:40 +02:00
epenet
803654223a Revert "Do not create Tuya fan entities without control" (#150032) 2025-08-05 12:23:06 +02:00
epenet
a6148b50cf Add Tuya snapshots tests for button and vacuum platform (#149968) 2025-08-05 11:21:05 +02:00
Ludovic BOUÉ
02a3c5be14 Matter pump setpoint CurrentLevel limit (#149689) 2025-08-05 11:19:03 +02:00
Paulus Schoutsen
08ea640629 Do not allow overriding users when uuid is duplicate (#149408) 2025-08-05 11:13:32 +02:00
Grzegorz M
7dd761c9c3 Bump icalendar from 6.1.0 to 6.3.1 for CalDav (#149990) 2025-08-05 11:09:03 +02:00
epenet
6b827dfc33 Do not create Tuya fan entities without control (#149976)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-05 09:52:29 +02:00
Robert Resch
67c19087dd Bump deebot-client to 13.6.0 (#149983) 2025-08-05 09:08:33 +02:00
epenet
55c7c2f730 Redact terminal_id in Tuya fixture files (#149957) 2025-08-05 09:06:15 +02:00
Matthias Alphart
afee936c3d Update knx-frontend to 2025.8.4.154919 (#149991) 2025-08-05 09:03:23 +02:00
Marc Mueller
ed2ced6c36 Fix zimi test RuntimeWarnings (#150017) 2025-08-05 08:55:54 +02:00
Martin Hjelmare
4c5cf028d7 Fix Z-Wave duplicate provisioned device (#150008) 2025-08-05 08:50:42 +02:00
Thomas55555
68faa897ad Bump aioautomower to 2.1.2 (#150003) 2025-08-05 08:48:47 +02:00
Artur Pragacz
53c9c42148 Use relative trigger keys (#149846) 2025-08-04 23:01:40 +01:00
Michael Hansen
d48cc03be7 Bump wyoming to 1.7.2 (#150007) 2025-08-04 23:36:24 +02:00
starkillerOG
28236aa023 Reolink disable entities by default (#149986) 2025-08-04 23:03:38 +02:00
Tom
bfae07135a Bump python-airos to 0.2.4 (#149885) 2025-08-04 22:35:47 +02:00
Thomas55555
99d580e371 Add reset cutting blade usage time to Husqvarna Automower (#149628) 2025-08-04 22:28:34 +02:00
Petro31
4d53450cbf Create battery_level deprecation repair for template vacuum platform (#149987)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-08-04 21:54:50 +02:00
epenet
1fbce01e26 Add initial support for Tuya wg2 category (#149676) 2025-08-04 21:30:43 +02:00
markhannon
a9621ac811 Add tests for Zimi entitites (#144292) 2025-08-04 20:41:05 +02:00
Marc Mueller
94f2118b19 Fix flaky history_stats test case (#149974) 2025-08-04 20:34:07 +02:00
Mike Degatano
73ca6b4900 Add translation strings for unsupported OS version (#149837) 2025-08-04 17:40:11 +02:00
Joakim Sørensen
31e647b5b0 Bump hass-nabucasa from 0.110.1 to 0.111.0 (#149977) 2025-08-04 16:59:07 +02:00
epenet
fac5b2c09c Add Tuya snapshots tests for camera platform (#149959) 2025-08-04 16:58:46 +02:00
Martin Hjelmare
ae48179e95 Bump zwave-js-server-python to 0.67.1 (#149972) 2025-08-04 15:58:57 +02:00
Willem-Jan van Rootselaar
88c9d5dbe3 Fix bsblan reauthentication (#149926) 2025-08-04 15:35:41 +02:00
hanwg
b76f47cd9f Add bot details to Telegram bot events (#148638) 2025-08-04 14:32:48 +02:00
hanwg
822e1ffc8d Minor UI improvements for Telegram bot actions (#149889) 2025-08-04 14:27:15 +02:00
Martin Hjelmare
1632e0aef6 Direct migrations with Z-Wave JS UI to docs (#149966) 2025-08-04 13:36:12 +02:00
Petro31
e2bc73f153 Fix optimistic covers (#149962) 2025-08-04 13:35:13 +02:00
Joakim Sørensen
46cfdddc80 Move to the new handler for migrate_paypal_agreement (#149934) 2025-08-04 13:29:11 +02:00
Joost Lekkerkerker
0bdf6757c4 Pass config entry to Remote Calendar coordinator (#149958) 2025-08-04 13:28:59 +02:00
Joost Lekkerkerker
312e590360 Pass config entry to Broadlink coordinator (#149949) 2025-08-04 13:27:51 +02:00
Joost Lekkerkerker
7a6aaf667b Pass config entry to hue coordinator (#149941) 2025-08-04 13:27:10 +02:00
Joost Lekkerkerker
33eaca24d6 Pass config entry to Simplisafe coordinator (#149943) 2025-08-04 13:21:29 +02:00
Joost Lekkerkerker
3d27d501b1 Pass config entry to Mill coordinator (#149942) 2025-08-04 13:20:30 +02:00
Joost Lekkerkerker
39b651e075 Pass config entry to Kraken coordinator (#149944) 2025-08-04 13:17:27 +02:00
Joost Lekkerkerker
a962777a2e Pass config entry to Meteo France coordinator (#149945) 2025-08-04 13:14:50 +02:00
Joost Lekkerkerker
594ce8f266 Pass config entry to Smarttub coordinator (#149946) 2025-08-04 12:58:46 +02:00
Joost Lekkerkerker
9f867f268c Pass config entry to Snoo coordinator (#149947) 2025-08-04 12:58:19 +02:00
Joost Lekkerkerker
9edd242734 Pass config entry to SMS coordinator (#149955) 2025-08-04 12:49:26 +02:00
Bouwe Westerdijk
93e11aa8bc Refresh plugwise test-fixtures (#149875) 2025-08-04 12:35:24 +02:00
Joakim Sørensen
c2b298283e Bump hass-nabucasa from 0.110.0 to 0.110.1 (#149956) 2025-08-04 12:32:01 +02:00
Joost Lekkerkerker
106c086e8b Pass config entry to Unifi coordinator (#149952) 2025-08-04 12:29:27 +02:00
Markus Adrario
cbf4130bff Add zeroconf flow to Homee (#149820)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-08-04 12:26:22 +02:00
Erik Montnemery
afffe0b08b Fix DeviceEntry.suggested_area deprecation warning (#149951) 2025-08-04 12:20:30 +02:00
Joost Lekkerkerker
c1ccfee7cc Pass config entry to AsusWRT coordinator (#149953) 2025-08-04 12:08:03 +02:00
epenet
8d8383e1c1 Add extra Tuya snapshots for dc and dj category (lights) (#149940) 2025-08-04 12:07:25 +02:00
Marc Mueller
f350a1a1fa Add hassfest check to help with future dependency updates (#149624) 2025-08-04 12:03:39 +02:00
epenet
fe2bd8d09e Add Tuya snapshots for ywcgq category (#149948) 2025-08-04 12:02:34 +02:00
Joost Lekkerkerker
cf14226b02 Pass config entry to Fronius coordinator (#149954) 2025-08-04 12:02:21 +02:00
Brett Adams
bd3fe1d4ad Fix credit sensor when there are no vehicles in Teslemetry (#149925) 2025-08-04 11:26:14 +02:00
Christopher Fenner
377ca04be8 Update sensor icons in Volvo integration (#149811) 2025-08-04 11:24:51 +02:00
epenet
5837f55205 Add extra Tuya snapshots for cz category (#149938) 2025-08-04 11:23:58 +02:00
andreimoraru
0766edb9c4 Bump yt-dlp to 2025.07.21 (#149916)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-08-04 11:15:38 +02:00
epenet
e62e3778f3 Add Tuya snapshots for hps category (#149936) 2025-08-04 11:14:11 +02:00
epenet
aa8e4c1c15 Add Tuya snapshots for sgbj, sp, wfcon and ywbj category (#149933) 2025-08-04 11:11:06 +02:00
Erik Montnemery
46ed8a73fc Bump automower-ble to 0.2.7 (#149928) 2025-08-04 11:09:18 +02:00
dependabot[bot]
83f22497ae Bump actions/ai-inference from 1.2.3 to 1.2.4 (#149929)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 11:09:02 +02:00
epenet
3dda1685dc Add Tuya snapshots for pc and pir category (#149931) 2025-08-04 11:08:43 +02:00
Ståle Storø Hauknes
6fa9d42401 Airthings ContextVar warning (#149930) 2025-08-04 11:05:32 +02:00
jvmahon
1a54d566f8 Apple vendor name update (#149845) 2025-08-04 10:26:11 +02:00
puddly
1a9cae0f89 Bump ZHA to 0.0.65 (#149922) 2025-08-04 10:17:25 +02:00
epenet
551dcaa169 Rename Tuya fixture files (#149927) 2025-08-04 10:08:03 +02:00
epenet
5467db065b Make Tuya complex type handling explicit (#149677) 2025-08-04 07:59:47 +02:00
J. Nick Koston
6a8d752e56 Bump aiodiscover to 2.7.1 (#149920) 2025-08-03 16:42:38 -10:00
J. Nick Koston
179a56628d Bump dbus-fast to 2.44.3 (#149921) 2025-08-03 16:42:11 -10:00
J. Nick Koston
b3f830773a Bump yalexs-ble to 3.1.2 (#149917) 2025-08-03 15:02:30 -10:00
Joost Lekkerkerker
084e06ec7d Bump python-open-router to 0.3.1 (#149873) 2025-08-03 21:46:40 +02:00
Maciej Bieniek
e0190afd3c Bump imgw_pib to version 1.5.2 (#149892) 2025-08-03 20:07:01 +02:00
Jan-Philipp Benecke
b9e16d54c4 Add jitter sensor to Ping integration (#149899) 2025-08-03 20:06:14 +02:00
Thomas55555
627785edc1 Fix options for error sensor in Husqvarna Automower (#149901) 2025-08-03 20:05:23 +02:00
Andrew Jackson
4318e29ce8 Bump aiomealie to 0.10.1 (#149890) 2025-08-03 14:18:13 +02:00
Martin Hjelmare
fea5c63bba Fix Z-Wave handling of driver ready event (#149879) 2025-08-03 11:23:01 +02:00
Åke Strandberg
b2349ac2bd Improve miele climate test coverage (#149859) 2025-08-03 11:19:08 +02:00
Marc Mueller
08f7b708a4 Update pytest warnings filter (#149839) 2025-08-03 09:25:17 +02:00
Martin Hjelmare
1236801b7d Fix Z-Wave config entry state conditions in listen task (#149841) 2025-08-02 23:07:16 +02:00
Thomas D
72d9dbf39d Add scopes in config flow auth request for Volvo integration (#149813) 2025-08-02 22:17:13 +02:00
Thomas D
755864f9f3 Add sensor platform to Qbus integration (#149389)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-08-02 20:01:58 +02:00
peteS-UK
fa476d4e34 Fix initialisation of Apps and Radios list for Squeezebox (#149834) 2025-08-02 20:01:02 +02:00
Manu
018197e41a Add notifiers to send direct messages to friends in PlayStation Network (#149844) 2025-08-02 19:55:45 +02:00
Brett Adams
7dd2b9e422 Make history coordinator more reliable in Tesla Fleet (#149854) 2025-08-02 19:54:19 +02:00
hahn-th
3e615fd373 Improve code quality for garage door modules in homematicip_cloud (#149856) 2025-08-02 19:51:08 +02:00
Oliver
c0bf167e10 Update denonavr to 1.1.2 (#149842) 2025-08-02 19:44:01 +02:00
Andrea Turri
45f6778ff4 Fix Miele hob translation keys (#149865) 2025-08-02 18:37:57 +02:00
Jamin
bddd4d621a Bump VoIP utils to 0.3.4 (#149786) 2025-08-01 20:37:45 +01:00
Norbert Rittel
b0e75e9ee4 Update reference for volatile_organic_compounds_parts in template (#149831) 2025-08-01 20:36:10 +01:00
Norbert Rittel
d45c03a795 Update reference for volatile_organic_compounds_parts in random (#149832) 2025-08-01 20:35:04 +01:00
Norbert Rittel
8562c8d32f Add translations for recently introduced device classes to scrape (#149822) 2025-08-01 20:34:31 +01:00
Norbert Rittel
ae42d71123 Add translations for recently introduced device classes to sql (#149821) 2025-08-01 20:33:47 +01:00
Alexandre CUER
9616c8cd7b Bump pyemoncms to 0.1.2 (#149825) 2025-08-01 20:04:16 +01:00
kizovinh
9394546668 Add EZVIZ battery camera power status and online status sensor (#146822) 2025-08-01 20:00:53 +01:00
Norbert Rittel
d43f21c2e2 Fix descriptions for template number fields (#149804) 2025-08-01 20:35:48 +02:00
Norbert Rittel
8d68fee9f8 Add translation for absolute_humidity device class to template (#149814) 2025-08-01 18:30:59 +01:00
Willem-Jan van Rootselaar
b4a4e218ec Add re-authentication to BSBLan (#146280)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-08-01 16:42:59 +02:00
Norbert Rittel
fb2d62d692 Add translation for absolute_humidity device class to mqtt (#149818) 2025-08-01 15:57:47 +02:00
Erik Montnemery
f538807d6e Make device suggested_area only influence new devices (#149758)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-08-01 14:54:58 +02:00
Joost Lekkerkerker
a08c3c9f44 Improve Tado binary sensor tests (#149807) 2025-08-01 14:38:12 +02:00
Joost Lekkerkerker
506431c75f Improve Tado water heater tests (#149806) 2025-08-01 14:38:02 +02:00
Joost Lekkerkerker
37579440e6 Improve Tado climate tests (#149808) 2025-08-01 14:37:12 +02:00
Joost Lekkerkerker
5ce2729dc2 Improve Tado sensor tests (#149809) 2025-08-01 14:36:57 +02:00
Joost Lekkerkerker
b5e4ae4a53 Improve Tado switch tests (#149810)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-01 14:36:37 +02:00
Norbert Rittel
3d4386ea6d Add translation for absolute_humidity device class to random (#149815) 2025-08-01 14:32:14 +02:00
Alexandre CUER
9f1cec893e emoncms - fix missing data descriptions (#149733) 2025-08-01 13:22:46 +02:00
starkillerOG
bc87140a6f Update after Motion Blinds tilt change (#149779) 2025-08-01 11:15:49 +02:00
Erik Montnemery
d77a3fca83 Exclude is_new from DeviceEntry snapshots (#149801) 2025-08-01 11:01:26 +02:00
Joakim Sørensen
924a86dfb6 Add nameservers to supervisor system health response (#149749) 2025-08-01 10:51:48 +02:00
Erik Montnemery
0d7608f7c5 Deprecate DeviceEntry.suggested_area (#149730) 2025-08-01 10:34:34 +02:00
Tom
22e054f4cd Add diagnostics to UISP AirOS (#149631) 2025-08-01 09:24:22 +02:00
epenet
8b53b26333 Fix tuya light supported color modes (#149793)
Co-authored-by: Erik <erik@montnemery.com>
2025-08-01 09:13:53 +02:00
Erik Montnemery
4d59e8cd80 Fix flaky velbus test (#149743) 2025-08-01 07:49:51 +02:00
Fabian Leutgeb
61396d92a5 Homekit valve duration characteristics (#149698)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-31 15:21:48 -10:00
Philippe Lafoucrière
c72c600de4 Fix bootstrap script path resolution (#149721) 2025-07-31 23:47:25 +01:00
J. Nick Koston
b86b0c10bd Bump aioesphomeapi to 37.2.2 (#149755) 2025-07-31 12:23:24 -10:00
starkillerOG
eb222f6c5d Bump motionblinds to 0.6.30 (#149764) 2025-08-01 01:09:20 +03:00
Manu
4b5fe424ed Hide configuration URL when Uptime Kuma is installed locally (#149781) 2025-08-01 01:07:56 +03:00
Nathan Spencer
61ca42e923 Bump pylitterbot to 2024.2.3 (#149763) 2025-07-31 21:04:23 +02:00
Copilot
21c1427abf Fix ZHA ContextVar deprecation by passing config_entry (#149748)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joostlek <7083755+joostlek@users.noreply.github.com>
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
Co-authored-by: TheJulianJES <6409465+TheJulianJES@users.noreply.github.com>
2025-07-31 14:52:17 -04:00
karwosts
aa6b37bc7c Fix add_suggested_values_to_schema when the schema has sections (#149718)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-07-31 20:50:26 +02:00
Marc Mueller
bbc1466cfc Update rpds-py to 0.26.0 (#149753) 2025-07-31 17:51:10 +01:00
Bram Kragten
21a9799060 Update frontend to 20250731.0 (#149757) 2025-07-31 18:46:10 +02:00
Erik Montnemery
f7d54b46ec Improve test of FlowHandler.add_suggested_values_to_schema (#149759) 2025-07-31 17:55:15 +02:00
Erik Montnemery
6ad1b8dcb1 Fix kitchen_sink option flow (#149760) 2025-07-31 17:49:09 +02:00
Abílio Costa
5f6b1212a3 Remove data flow step_id deprecation note (#149714) 2025-07-31 16:04:09 +02:00
dependabot[bot]
58dc6a952e Bump home-assistant/wheels from 2025.03.0 to 2025.07.0 (#149741) 2025-07-31 15:35:55 +02:00
Petro31
59d8df142d Nitpick default translations for template integration (#149740) 2025-07-31 15:19:43 +02:00
Petro31
04fb86b4ba Fix unique_id in config validation for legacy weather platform (#149742) 2025-07-31 15:19:37 +02:00
Erik Montnemery
3d744f032f Make _EventDeviceRegistryUpdatedData_Remove JSON serializable (#149734) 2025-07-31 12:35:13 +02:00
J. Nick Koston
f7c8cdb3a7 Bump aioesphomeapi to 37.2.0 (#149732) 2025-07-31 12:10:23 +02:00
Copilot
3952544822 Fix ContextVar deprecation warning in homeassistant_hardware integration (#149687)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joostlek <7083755+joostlek@users.noreply.github.com>
Co-authored-by: mib1185 <35783820+mib1185@users.noreply.github.com>
2025-07-31 12:06:04 +02:00
Erik Montnemery
42101dd432 Remove result from FlowResult (#149202) 2025-07-31 10:58:36 +02:00
L.
f7eacaa48d Bump xiaomi-ble to 1.2.0 (#149711) 2025-07-31 09:01:06 +02:00
johanzander
ad0db5c83a Update growattServer to version 1.7.1 (#149716) 2025-07-31 08:17:33 +02:00
J. Nick Koston
63216b77c2 Bump aioesphomeapi to 37.1.6 (#149715) 2025-07-30 13:54:18 -10:00
Åke Strandberg
7a55373b0b Fix bug when interpreting miele action response (#149710) 2025-07-31 01:07:12 +02:00
J. Nick Koston
f9e7459901 Fix ESPHome unnecessary probing on DHCP discovery (#149713) 2025-07-31 01:06:08 +02:00
starkillerOG
94dc2e2ea3 Bump reolink-aio to 0.14.5 (#149700) 2025-07-30 22:54:32 +01:00
Åke Strandberg
2cf144fb25 Add missing translations for miele dishwasher (#149702) 2025-07-30 22:45:05 +01:00
Jan Bouwhuis
f318766021 Fix inconsistent use of the term 'target' and a typo in MQTT translation strings (#149703) 2025-07-30 22:42:53 +01:00
Andrea Turri
ec7fb140ac Fix Miele induction hob empty state (#149706) 2025-07-30 22:38:11 +01:00
Petro31
2706c7d67d Add translations for all fields in template integration (#149692)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-07-30 22:30:05 +01:00
Roman Sivriver
b4e50902eb Fix typo in backup log message (#149705) 2025-07-30 22:29:26 +01:00
Åke Strandberg
1ead01bc9a Explicitly pass config_entry to miele coordinator (#149691) 2025-07-30 20:19:01 +02:00
puddly
389a1251a1 Bump ZHA to 0.0.64 (#149683)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
Co-authored-by: abmantis <amfcalt@gmail.com>
2025-07-30 18:59:41 +01:00
Manu
8d27ca1e21 Fix KeyError in friends coordinator (#149684) 2025-07-30 19:59:01 +02:00
Michael Hansen
a76af50c10 Bump intents to 2025.7.30 (#149678) 2025-07-30 19:57:59 +02:00
Renat Sibgatulin
09b91bd76a Clean airq tests (#149682) 2025-07-30 18:48:36 +01:00
Jan Bouwhuis
736d582d04 Fix translation string reference for MQTT climate subentry option (#149673) 2025-07-30 18:53:21 +02:00
Bram Kragten
8114df4219 Bump version to 2025.9.0 (#149680) 2025-07-30 18:36:20 +02:00
Joost Lekkerkerker
8193259e02 Revert "Add select for heating circuit to Tado zones" (#149670) 2025-07-30 17:06:55 +02:00
Petro31
6306baa3c9 Add config flow to template lock platform (#149449) 2025-07-30 17:04:39 +02:00
Petro31
d481a694f1 Add config flow to template vacuum platform (#149458) 2025-07-30 17:04:08 +02:00
Robert Resch
edca3fc0b7 Add matter to Third Reality (#149659) 2025-07-30 16:52:20 +02:00
Bram Kragten
daea76c2f1 Update frontend to 20250730.0 (#149672) 2025-07-30 16:51:10 +02:00
Petro31
160b61e0b9 Add config flow to template fan platform (#149446)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2025-07-30 16:17:49 +02:00
epenet
fc900a632a Revert logging for unsupported Tuya devices (#149665) 2025-07-30 16:04:45 +02:00
Joost Lekkerkerker
1b58809655 Add AI Task to OpenRouter (#149275) 2025-07-30 16:01:44 +02:00
Åke Strandberg
223c34056d Add missing colons in miele messages (#149668) 2025-07-30 15:58:43 +02:00
Jeef
99ee56a4dd Add Precipitation sensors to Weatherflow Cloud (#149619)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-30 15:45:03 +02:00
lucasfijen
91be25a292 Add get recipes search service to Mealie integration (#149348)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-30 15:43:10 +02:00
Petro31
a21af78aa1 Add config flow to template light platform (#149448) 2025-07-30 15:27:43 +02:00
Manu
70cfdfa231 Remove unnecessary CONFIG_SCHEMA from Uptime Kuma integration (#149601) 2025-07-30 15:23:54 +02:00
Jan Bouwhuis
a5b075af68 Add climate support for MQTT subentries (#149451)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-07-30 15:20:23 +02:00
Manu
c4d4ef884e Add hassio discovery flow to Uptime Kuma (#148770) 2025-07-30 15:13:39 +02:00
Manu
ba4e7e50e0 Add friend tracking to PlayStation Network (#149546) 2025-07-30 15:10:30 +02:00
Alistair Francis
dd0b23afb0 husqvarna_automower_ble: Support battery percentage sensor (#146159)
Signed-off-by: Alistair Francis <alistair@alistair23.me>
2025-07-30 15:07:47 +02:00
Manu
779f0afcc4 Refactor Habitica button and switch functions to use habiticalib instance directly (#149602) 2025-07-30 15:07:22 +02:00
Manu
d8016f7f41 Remove stale devices in Uptime Kuma (#149605) 2025-07-30 15:06:59 +02:00
Avery
25169e9075 Bump datadogpy to 0.52.0 (#149596) 2025-07-30 15:06:38 +02:00
Samuel Xiao
260ca70785 Add Light platform to Switchbot cloud (#146382) 2025-07-30 15:03:13 +02:00
Samuel Xiao
69e3a5bc34 Add support for more switchbot cloud vacuum models (#146637) 2025-07-30 15:02:37 +02:00
Simone Chemelli
1a75a88c76 Add actions to Alexa Devices (#145645) 2025-07-30 14:52:31 +02:00
Petro31
6c2a662838 Add config flow to template cover platform (#149433) 2025-07-30 14:48:24 +02:00
Artur Pragacz
749fc318ca Validate selectors in the trigger helper (#149662) 2025-07-30 14:22:55 +02:00
epenet
828f979c78 Use Tuya device listener in binary sensor tests (#148890) 2025-07-30 13:43:07 +02:00
Åke Strandberg
1eb6d5fe32 Add action for set_program_oven to miele (#149620) 2025-07-30 13:35:24 +02:00
epenet
5930ac6425 Use translation_placeholders in tuya switch descriptions (#149664) 2025-07-30 13:27:24 +02:00
Simone Chemelli
15e45df8a7 Use async_create_clientsession in Alexa Devices (#149432) 2025-07-30 12:49:21 +02:00
karwosts
a79d2da9a3 Move group toggle descriptions to data_description (#149625)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-07-30 12:31:32 +02:00
Robert Resch
ac86f2e2ba Add Frient brand (#149654) 2025-07-30 12:21:27 +02:00
Norbert Rittel
03ee97d38f Clarify description of turn_away_mode_on.osoenergy action (#149655) 2025-07-30 12:16:40 +02:00
J. Nick Koston
06233b5134 Bump aioesphomeapi to 37.1.5 (#149656) 2025-07-30 12:16:16 +02:00
Petro31
9d66b19c03 Add assumed optimistic to template number entities (#148499) 2025-07-30 11:20:04 +02:00
Martin Hjelmare
bb6bcfdd01 Add Z-Wave controller firmware updates (#149623) 2025-07-30 11:07:41 +02:00
Marc Mueller
8e9e304608 Update lxml to 6.0.0 (#149640) 2025-07-30 10:38:42 +02:00
dependabot[bot]
6b641411a0 Bump github/codeql-action from 3.29.4 to 3.29.5 (#149648)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 10:33:09 +02:00
Norbert Rittel
6f8214bbb4 Fix spelling mistakes in abort message of leaone (#149653) 2025-07-29 22:22:35 -10:00
Marcel van der Veldt
f66e83f33e Add dynamic encryption key support to the ESPHome integration (#148746)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-29 21:54:00 -10:00
Robert Resch
2ee82e1d6f Remove battery attribute from Ecovacs vacuums (#149581) 2025-07-30 09:24:16 +02:00
Jan Bouwhuis
0dd1e0cabb Suppress exception stack trace when writing MQTT entity state if a ValueError occured (#149583) 2025-07-30 09:06:15 +02:00
Åke Strandberg
45ae34cc0e Strip leading and trailing whitespace in program names in miele action response (#149643) 2025-07-30 00:23:03 +02:00
hypnosiss
73e578b168 Bump pymysensors library version (#149632) 2025-07-29 22:29:53 +01:00
Arie Catsman
52ee5d53ee bump pyenphase to 2.2.3 (#149641) 2025-07-29 22:27:43 +01:00
Marc Mueller
62713b1371 Update pyblu to 2.0.4 (#149589) 2025-07-29 22:32:32 +02:00
Simone Chemelli
c4c4463c63 Update IQS for Alexa Devices (#149639) 2025-07-29 22:00:49 +02:00
Franck Nijhof
7e2fd6e47b Merge branch 'master' into dev 2025-07-29 18:52:18 +00:00
karwosts
9f45801409 Remove advanced mode from group all option. (#149626) 2025-07-29 20:03:27 +02:00
J. Nick Koston
aaec243bf4 Properly cleanup ONVIF events to prevent log flooding on setup errors (#149603) 2025-07-29 19:49:20 +02:00
Tom
b67e85e8da Introduce Ubiquiti UISP airOS (#148989)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-29 19:41:13 +02:00
J. Nick Koston
25407c0f4b Bump aiohttp to 3.12.15 (#149609) 2025-07-29 19:21:31 +02:00
Stefan Agner
09e7d8d1a5 Increase open file descriptor limit on startup (#148940)
Co-authored-by: Jan Čermák <sairon@sairon.cz>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-07-29 17:42:26 +02:00
Markus Adrario
ff7c125334 Upgrade Homee quality scale to silver (#149194) 2025-07-29 15:19:08 +02:00
Martin Hjelmare
3d6f868cbc Bump zwave-js-server-python to 0.67.0 (#149616) 2025-07-29 13:57:40 +02:00
Thomas D
378c3af9df Bump qbusmqttapi to 1.4.2 (#149622) 2025-07-29 13:51:32 +02:00
osohotwateriot
c7271d1af9 Add OSO Energy Custom Away Mode Service (#149612) 2025-07-29 13:50:31 +02:00
Klaas Schoute
87400c6a17 Bump odp-amsterdam to v6.1.2 (#149617) 2025-07-29 12:59:30 +02:00
Christopher Fenner
692a1119a6 Adjust suggested display precision on Volvo distance sensors (#149593) 2025-07-29 12:29:07 +02:00
Thomas55555
2e728eb7de Bump aioautomower to 2.1.1 (#149585) 2025-07-29 09:38:50 +02:00
Manu
45ec9c7dad Refactor coordinator setup in Iron OS (#149600) 2025-07-29 09:37:32 +02:00
Manu
62ee1fbc64 Remove unnecessary CONF_NAME usage in Habitica integration (#149595) 2025-07-29 08:55:32 +02:00
Jan-Philipp Benecke
3c1aa9d9de Make exceptions translatable in Tankerkoenig integration (#149611) 2025-07-29 08:52:42 +02:00
J. Nick Koston
bf568b22d7 Bump onvif-zeep-async to 4.0.2 (#149606) 2025-07-29 08:41:45 +02:00
Michael
596f6cd216 Add people and tags collections to Immich media source (#149340) 2025-07-28 23:21:04 +02:00
Åke Strandberg
cf05f1046d Add action to retrieve list of programs on miele appliance (#149307) 2025-07-28 22:19:51 +02:00
Thomas55555
7f9be420d2 Add details to Husqvarna Automower restricted reason sensor (#147678)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-07-28 19:54:54 +01:00
Abílio Costa
dda46e7e0b Use non-autospec mock in Reolink's remaining tests (#149565)
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
2025-07-28 18:38:06 +01:00
Michael
b1dd742a57 Move battery properties from legacy Ecovacs vacuum entity to separate entities (#149084) 2025-07-28 18:49:12 +02:00
Simone Chemelli
5af4290b77 Update IQS for Alexa Devices (#149440) 2025-07-28 18:33:39 +02:00
Petro31
8339516fb4 Add optimistic option to alarm control panel yaml (#149334) 2025-07-28 17:44:43 +02:00
Matrix
aa1314c1d5 Add YoLink YS6614 support. (#149153) 2025-07-28 17:43:20 +02:00
epenet
92ad922ddc Add fan mode support for Tuya air conditioner (aqoouq7x) (#149226) 2025-07-28 17:42:36 +02:00
epenet
e518e7beac Add service tests to Tuya select platform (#149156)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-28 17:42:18 +02:00
Thomas D
483d814a8f Add new Volvo integration (#142994)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-07-28 17:24:15 +02:00
Tom
8f795f021c Bump Plugwise to v1.7.8 preventing rogue KeyError (#149000) 2025-07-28 17:19:43 +02:00
Petro31
d823b574c0 Add optimistic option to light yaml (#149395) 2025-07-28 16:59:57 +02:00
Petro31
49bd15718c Add optimistic option to fan yaml (#149390) 2025-07-28 16:58:46 +02:00
Abílio Costa
d3f18c1678 Add quality scale to ring manifest (#149406) 2025-07-28 16:35:38 +02:00
Joost Lekkerkerker
5ef17c8588 Bump the required version of ruff to 0.12.1 (#149571) 2025-07-28 16:32:56 +02:00
Norbert Rittel
e8b8d31027 Make actions labels consistent for Template alarm control panel (#149574) 2025-07-28 16:31:13 +02:00
Manu
978ee3870c Add notify platform to PlayStation Network integration (#149557) 2025-07-28 16:18:57 +02:00
Petro31
b3862591ea Add optimism to vacuum platform (#149425) 2025-07-28 16:18:37 +02:00
Petro31
1895db0ddd Add optimistic option to switch yaml (#149402) 2025-07-28 16:17:39 +02:00
Petro31
ee2cf961f6 Add assumed optimistic functionality to lock platform (#149397) 2025-07-28 16:17:09 +02:00
Martin Hjelmare
9a364ec729 Fix Z-Wave removal of devices when connected to unknown controller (#149339) 2025-07-28 16:13:39 +02:00
starkillerOG
96529ec245 Add Reolink pre-recording entities (#149522)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 16:12:53 +02:00
David Knowles
8fc8220924 Teach Hydrawise to auto-add/remove devices (#149547)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-28 16:06:15 +02:00
osohotwateriot
386f709fd3 Osoenergy holiday mode services (#149430)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-28 16:00:22 +02:00
alvi kazi 🇧🇩
d088fccb88 VeSync: add support for LAP-V102S-WJP air purifier (#149102) 2025-07-28 15:51:07 +02:00
jennoian
2a5448835f Add Vacuum support to smartthings (#148724)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-28 15:37:37 +02:00
Avery
a71eecaaa4 Update datadog test logic (#149459)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-07-28 15:10:55 +02:00
hanwg
46d810b9f9 Better error handling when setting up config entry for Telegram bot (#149444) 2025-07-28 14:52:40 +02:00
Petro31
48c4240a5d Delete unused switch platform code (#149468) 2025-07-28 14:48:45 +02:00
wittypluck
bf05c23414 Update OpenWeatherMap config step description to clarify API key documentation (#146843) 2025-07-28 14:40:00 +02:00
Michael
db1e6a0d98 Add quality scale and set Silver for Tankerkoenig (#143418) 2025-07-28 14:34:27 +02:00
Assaf Inbal
4ad35e8421 Add charging binary sensor to ituran (#149562)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-07-28 13:18:43 +02:00
wollew
850e04d9aa Add binary sensor for rain detection for Velux windows that have them (#148275)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-28 13:15:59 +02:00
Manu
95c5a91f01 Refactor active session handling in PlaystationNetwork (#149559) 2025-07-28 13:13:08 +02:00
Petro31
140f56aeaa Add common translation strings (#149472)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-07-28 13:12:52 +02:00
Michael
40ce228c9c Add upload_file action to immich integration (#147295)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-07-28 13:12:16 +02:00
Abílio Costa
18c5437fe7 Revert "Make default title configurable in XMPP" (#149544) 2025-07-28 13:42:40 +03:00
Norbert Rittel
ebad1ff4cc Fix capitalization of "IP address" in goalzero (#149563) 2025-07-28 11:59:11 +02:00
Ludovic BOUÉ
a68e722c92 Matter MicrowaveOven device (#148219)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-07-28 11:33:03 +02:00
Joakim Sørensen
05935bbc01 Bump hass-nabucasa from 0.108.0 to 0.110.0 (#149560) 2025-07-28 11:17:26 +02:00
Assaf Inbal
c67636b4f6 Add support for EVs in ituran (#149484) 2025-07-28 10:35:52 +02:00
Franck Nijhof
777b3128bb 2025.7.4 (#149526) 2025-07-28 10:15:08 +02:00
Shai Ungar
ab6cd0eb41 Bump israel-rail to 0.1.3 (#149555) 2025-07-28 08:42:40 +02:00
Brett Adams
f35558413a Bump tesla-fleet-api to 1.2.3 (#149550) 2025-07-28 07:58:59 +02:00
Michael
e30d405625 Enable strict typing in Tankerkoenig (#149535) 2025-07-27 22:48:15 +02:00
Thomas55555
622cce03a1 Bump aioautomower to 2.1.0 (#149541) 2025-07-27 22:46:59 +02:00
Raphael Hehl
1fa9141ce1 Bump uiprotect to version 7.20.0 (#149533) 2025-07-27 21:52:53 +02:00
Norbert Rittel
a060f7486f Replace duplicated strings and fix "street name" in waze_travel_time (#149512) 2025-07-27 21:36:25 +03:00
Manu
dbb5730389 Increase trophy titles retrieval page size to 500 for PlayStation Network (#149528) 2025-07-27 21:35:01 +03:00
Michael
431b2aa1d5 Add data description strings to Tankerkoenig (#149519)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-27 20:13:05 +02:00
Michael
c99d81a554 Set PARALLEL_UPDATES in Tankerkoenig platforms (#149518) 2025-07-27 20:02:24 +02:00
starkillerOG
ff4dc393cf Bump reolink-aio to 0.14.4 (#149521) 2025-07-27 20:00:50 +02:00
Franck Nijhof
d384bee576 Bump version to 2025.7.4 2025-07-27 17:35:19 +00:00
Manu
f0cb5d5480 Bump habiticalib to v0.4.1 (#149523) 2025-07-27 17:35:01 +00:00
jb101010-2
725799c73e Bump pysuezV2 to 2.0.7 (#149436) 2025-07-27 17:34:59 +00:00
Simone Chemelli
dc6d2e3e84 Bump aioamazondevices to 3.5.1 (#149385) 2025-07-27 17:34:09 +00:00
Alex Hermann
4a7d06a68a Update slixmpp to 1.10.0 (#149374) 2025-07-27 17:29:37 +00:00
Brett Adams
cd800da357 Update Tesla OAuth Server in Tesla Fleet (#149280) 2025-07-27 17:29:35 +00:00
Antoine Reversat
4c8ab8eb64 Add fan off mode to the supported fan modes to fujitsu_fglair (#149277) 2025-07-27 17:29:33 +00:00
Martin Hjelmare
60f4d29d60 Add Z-Wave USB migration confirm step (#149243) 2025-07-27 17:29:31 +00:00
Avi Miller
68b7d09476 Fix brightness_step and brightness_step_pct via lifx.set_state (#149217)
Signed-off-by: Avi Miller <me@dje.li>
2025-07-27 17:29:29 +00:00
jvmahon
c3eb6dea11 Fix Matter light get brightness (#149186) 2025-07-27 17:29:28 +00:00
David Knowles
f428ffde87 Bump pyschlage to 2025.7.2 (#149148) 2025-07-27 17:29:26 +00:00
hanwg
fa207860a0 Fix multiple webhook secrets for Telegram bot (#149103) 2025-07-27 17:29:24 +00:00
Allen Porter
959c3a8a99 Fix a bug in rainbird device migration that results in additional devices (#149078) 2025-07-27 17:29:23 +00:00
Marc Hörsken
254ccca4e5 Fix warning about failure to get action during setup phase (#148923) 2025-07-27 17:29:21 +00:00
AlCalzone
5b08724d81 Keep entities of dead Z-Wave devices available (#148611) 2025-07-27 17:29:20 +00:00
Marc Mueller
ea2b3b3ff3 Update ical + gcal-sync (#149413) 2025-07-27 19:22:01 +02:00
Alex Hermann
a33760bc1a Update slixmpp to 1.10.0 (#149374) 2025-07-27 19:18:00 +02:00
Manu
4ea7ad52b1 Bump habiticalib to v0.4.1 (#149523) 2025-07-27 19:09:13 +02:00
Manu
dac75d1902 Add update platform to Uptime Kuma (#148973) 2025-07-27 18:02:33 +02:00
petep0p
0e9ced3c00 Correct core Purpleair integration's RSSI sensor to use RSSI value rather than barometric pressure (#149418) 2025-07-27 07:13:31 -06:00
Norbert Rittel
22d0fbcbd2 Fix spelling of "its" in mqtt (#149517) 2025-07-27 14:39:21 +02:00
Abílio Costa
57b641b97d Use non-autospec mock in Reolink's media source, number, sensor and siren tests (#149396) 2025-07-27 12:43:48 +02:00
J. Nick Koston
27bd6d2e38 Bump aioesphomeapi to 37.1.2 (#149460) 2025-07-26 22:48:48 -10:00
Assaf Inbal
427e5d81df Bump pyituran to 0.1.5 (#149486) 2025-07-26 19:03:51 +03:00
Shay Levy
b6bd92ed19 Shelly entity device info code quality (#149477) 2025-07-26 17:08:08 +03:00
Florian von Garrel
7976729e76 Paperless-ngx: Retry setup on initialization error (#149476) 2025-07-26 14:19:33 +02:00
Shay Levy
5aa0d0dc81 Remove Shelly redundant device info assignment in Button class (#149469) 2025-07-26 14:32:51 +03:00
jb101010-2
e1501d7510 Bump pysuezV2 to 2.0.7 (#149436) 2025-07-26 13:38:38 +03:00
Norbert Rittel
be5109fddf Change spelling of "Favorite x" to intl. English in bang_olufsen (#149464) 2025-07-26 13:35:11 +03:00
Norbert Rittel
c5cf9b07b7 Replace HA alarm (control panel) states with references in risco (#149466) 2025-07-26 13:34:24 +03:00
Norbert Rittel
002b7c6789 Fix descriptions in home_connect.set_program_and_options action (#149462) 2025-07-26 09:47:26 +02:00
Paul Bottein
e017dc80a0 Allow to reorder members within a group (#149003) 2025-07-26 01:07:51 +02:00
Erik Montnemery
aab7381553 Add test of ConfigSubentryFlow._subentry_type (#147565) 2025-07-26 00:27:04 +02:00
Norbert Rittel
cbf4409db3 Fix inconsistent spelling of "Wi-Fi" in unifiprotect (#149311)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-25 09:51:01 -10:00
Raphael Hehl
56fb59e48e Unifiprotect refactor device description ID retrieval in tests (#149445) 2025-07-25 09:21:57 -10:00
rappenze
971bd56bee Add Z-Box Hub virtual integration (#146678) 2025-07-25 20:37:36 +02:00
Matt Zimmerman
b2710c1bce Add smarttub cover sensor (#139134)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-07-25 20:10:39 +02:00
Petro31
a069b59efc Transition template types from string to platform keys (#149434) 2025-07-25 19:55:40 +02:00
osohotwateriot
02eb1dd533 Bump pyosoenergyapi to 1.2.4 (#149439) 2025-07-25 19:30:58 +02:00
Thomas55555
b3130c7929 Bump aioautomower to 2.0.2 (#149441) 2025-07-25 19:29:40 +02:00
Norbert Rittel
aad1dbecb4 Fix spelling of "IP" and improve action descriptions in lcn (#149314) 2025-07-25 19:28:43 +02:00
jvmahon
65109ea000 Fix Matter light get brightness (#149186) 2025-07-25 19:09:58 +02:00
Marc Mueller
356ac74fa5 Update orjson to 3.11.1 (#149442) 2025-07-25 19:07:07 +02:00
Norbert Rittel
f3513f7f29 Add missing hyphen to "case-sensitive" in tplink (#149363) 2025-07-25 19:01:57 +02:00
Marc Mueller
4bbb94f43d Update coverage to 7.10.0 (#149412) 2025-07-25 15:05:20 +02:00
Jan Bouwhuis
c1fa721a57 Revert "Use OptionsFlowWithReload in mqtt" (#149431) 2025-07-25 15:03:44 +02:00
Norbert Rittel
e3ffb41650 Improve some option and state names in home_connect (#149373) 2025-07-25 13:52:01 +02:00
Shay Levy
123cce6d96 Add configuration URL and model details to Shelly sub device info (#149404) 2025-07-25 14:26:32 +03:00
Guido Schmitz
6920dec352 Rework devolo Home Control config flow (#147121) 2025-07-25 12:55:42 +02:00
Guido Schmitz
f7cc260336 Add quality scale for devolo Home Network (#131510)
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
2025-07-25 12:20:33 +02:00
osohotwateriot
b7da31a021 Bump pyosoenergyapi to 1.2.3 (#149422) 2025-07-25 12:15:42 +02:00
Kevin Stillhammer
95d4dc678c Add option traffic_mode in here_travel_time (#146676) 2025-07-25 12:14:36 +02:00
Álvaro Fernández Rojas
7e9da052ca Update aioairzone-cloud to v0.7.1 (#149388)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2025-07-25 08:17:26 +02:00
Marc Mueller
59ece455d9 Update numpy to 2.3.2 (#149411) 2025-07-25 02:24:25 +02:00
Jake Martin
3ba144c8b2 Bump monzopy to 1.5.1 (#149410) 2025-07-25 02:38:48 +03:00
Bram Kragten
456f992b7e 2025.7.3 (#149024) 2025-07-22 10:30:09 +02:00
Franck Nijhof
0675e34c62 Bump version to 2025.7.3 2025-07-18 17:05:52 +00:00
Simone Chemelli
190c98f5a8 Bump aioamazondevices to 3.5.0 (#149011) 2025-07-18 17:04:01 +00:00
Jan Bouwhuis
c6bb26be89 Ignore MQTT sensor unit of measurement if it is an empty string (#149006) 2025-07-18 17:02:02 +00:00
J. Nick Koston
d57c5ffa8f Bump PySwitchbot to 0.68.2 (#148996) 2025-07-18 17:02:01 +00:00
Bram Kragten
68889e1790 Update frontend to 20250702.3 (#148994) 2025-07-18 17:02:00 +00:00
Joost Lekkerkerker
8fdc50a29f Pass Syncthru entry to coordinator (#148974) 2025-07-18 17:01:58 +00:00
Steven Looman
5656b4c20d Bump async-upnp-client to 0.45.0 (#148961) 2025-07-18 17:01:57 +00:00
Maciej Bieniek
b6edcc9422 Bump gios to version 6.1.2 (#148884) 2025-07-18 17:01:56 +00:00
Maciej Bieniek
7a3eb53453 Bump gios to version 6.1.1 (#148414) 2025-07-18 17:01:54 +00:00
Arie Catsman
11a2c73e8a Bump pyenphase to 2.2.2 (#148870) 2025-07-18 17:00:32 +00:00
Brett Adams
1644484c92 Fix button platform parent class in Teslemetry (#148863) 2025-07-18 17:00:30 +00:00
Pete Sage
8e0a89dc2f Add guard to prevent exception in Sonos Favorites (#148854) 2025-07-18 17:00:29 +00:00
Robert Resch
9e4b8df344 Use ffmpeg for generic cameras in go2rtc (#148818) 2025-07-18 17:00:28 +00:00
Brett Adams
69fdc1d269 Bump Tesla Fleet API to 1.2.2 (#148776) 2025-07-18 17:00:26 +00:00
Joost Lekkerkerker
56e0aa103d Bump pySmartThings to 3.2.8 (#148761) 2025-07-18 17:00:25 +00:00
Maciej Bieniek
caf0492009 Fix Shelly n_current sensor removal condition (#148740) 2025-07-18 17:00:24 +00:00
hahn-th
c6d0aad3d3 Handle connection issues after websocket reconnected in homematicip_cloud (#147731) 2025-07-18 17:00:22 +00:00
Franck Nijhof
1f59b735c6 2025.7.2 (#148725) 2025-07-14 13:12:29 +02:00
Franck Nijhof
87af9fc8ba Bump version to 2025.7.2 2025-07-14 10:30:35 +00:00
Simone Chemelli
691a0ca065 Bump aioamazondevices to 3.2.10 (#148709) 2025-07-14 10:27:45 +00:00
Shay Levy
80384b89a5 Bump aioshelly to 13.7.2 (#148706) 2025-07-14 10:25:24 +00:00
Jan Bouwhuis
f7672985ed Fix hide empty sections in mqtt subentry flows (#148692) 2025-07-14 10:25:23 +00:00
Christopher Fenner
d4374dbcc7 Bump PyViCare to 2.50.0 (#148679) 2025-07-14 10:25:22 +00:00
Brett Adams
c4ddcd64c8 Fix Charge Cable binary sensor in Teslemetry (#148675) 2025-07-14 10:25:21 +00:00
0xEF
c802430066 Bump nyt_games to 0.5.0 (#148654) 2025-07-14 10:25:20 +00:00
falconindy
649fbfc729 snoo: use correct value for right safety clip binary sensor (#148647) 2025-07-14 10:25:18 +00:00
Jan Bouwhuis
80c52ad8ea Fix - only enable AlexaModeController if at least one mode is offered (#148614) 2025-07-14 10:25:17 +00:00
Lưu Quang Vũ
150d4716fa Fix Google Cloud 504 Deadline Exceeded (#148589) 2025-07-14 10:25:16 +00:00
Bram Kragten
dc2736580f Update frontend to 20250702.2 (#148573) 2025-07-14 10:25:15 +00:00
J. Nick Koston
f1272ef513 Bump aiohttp to 3.12.14 (#148565) 2025-07-14 10:25:14 +00:00
Åke Strandberg
3c2fa023b4 Remove vg argument from miele auth flow (#148541) 2025-07-14 10:25:12 +00:00
Kristof Mariën
5cf5be8c9c Fix for Renson set Breeze fan speed (#148537) 2025-07-14 10:25:11 +00:00
Jan-Philipp Benecke
63b21fda1a Ensure response is fully read to prevent premature connection closure in rest command (#148532) 2025-07-14 10:25:10 +00:00
J. Diego Rodríguez Royo
d87379d083 Use the link to the issue instead of creating new issues at Home Connect (#148523) 2025-07-14 10:25:09 +00:00
J. Diego Rodríguez Royo
0990cef917 Add Home Connect resume command button when an appliance is paused (#148512) 2025-07-14 10:25:08 +00:00
Michael
962ad99c20 Add workaround for sub units without main device in AVM Fritz!SmartHome (#148507) 2025-07-14 10:25:07 +00:00
Michael
9c9836defd Bump aioimmich to 0.10.2 (#148503) 2025-07-14 10:25:06 +00:00
Jan Bouwhuis
e951fc401c Fix entity_id should be based on object_id the first time an entity is added (#148484) 2025-07-14 10:25:05 +00:00
Robert Resch
00e2a177a5 Revert "Deprecate hddtemp" (#148482) 2025-07-14 10:25:04 +00:00
Joakim Sørensen
b6d316c8f2 Bump hass-nabucasa from 0.105.0 to 0.106.0 (#148473) 2025-07-14 10:25:02 +00:00
Raphael Hehl
b8425de0d0 Bump uiprotect to version 7.14.2 (#148453) 2025-07-14 10:25:01 +00:00
Joost Lekkerkerker
d51a44acbc Bump pySmartThings to 3.2.7 (#148394) 2025-07-14 10:25:00 +00:00
Josef Zweck
435465e569 Bump pylamarzocco to 2.0.11 (#148386) 2025-07-14 10:24:59 +00:00
Josef Zweck
3b047859f9 Create own clientsession for lamarzocco (#148385) 2025-07-14 10:24:58 +00:00
Simone Chemelli
91cdf1a367 Bump aioamazondevices to 3.2.8 (#148365)
Co-authored-by: Joakim Plate <elupus@ecce.se>
2025-07-14 10:24:57 +00:00
Joakim Plate
2377b136f3 Handle binary coils with non default mappings in nibe heatpump (#148354) 2025-07-14 10:24:56 +00:00
Retha Runolfsson
186c4e7038 Bump pyswitchbot to 0.68.1 (#148335) 2025-07-14 10:24:55 +00:00
Samuel Xiao
d303a7d17e Fix Switchbot cloud plug mini current unit Issue (#148314) 2025-07-14 10:24:54 +00:00
jvits227
14f059c766 Add lamp states to smartthings selector (#148302)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-07-14 10:23:55 +00:00
Arie Catsman
4a10370932 Bump pyenphase to 2.2.1 (#148292) 2025-07-14 10:13:43 +00:00
J. Nick Koston
672ffa5984 Restore httpx compatibility for non-primitive REST query parameters (#148286) 2025-07-14 10:13:42 +00:00
Maciej Bieniek
3d3f2527cb Bump gios to version 6.1.0 (#148274) 2025-07-14 10:13:41 +00:00
Shay Levy
5c3b279f95 Bump aiowebostv to 0.7.4 (#148273) 2025-07-14 10:13:40 +00:00
starkillerOG
59bcf1167a bump motionblinds to 0.6.29 (#148265) 2025-07-14 10:13:39 +00:00
Mark Adkins
b4d789f8e2 Bump sharkiq to 1.1.1 (#148244) 2025-07-14 10:12:00 +00:00
Josef Zweck
f4ca56052b Bump pylamarzocco to 2.0.10 (#148233) 2025-07-14 10:11:59 +00:00
J. Nick Koston
74f9549431 Fix UTF-8 encoding for REST basic authentication (#148225) 2025-07-14 10:11:58 +00:00
J. Nick Koston
9650727515 Fix REST sensor charset handling to respect Content-Type header (#148223) 2025-07-14 10:11:57 +00:00
TimL
c965da6559 Bump pysmlight to v0.2.7 (#148101)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-07-14 10:09:19 +00:00
Sören Beye
9077965214 Squeezebox: Fix tracks not having thumbnails (#147187) 2025-07-14 10:09:18 +00:00
Sören Beye
2b7992e849 Squeezebox: Fix track selection in media browser (#147185) 2025-07-14 10:09:16 +00:00
Franck Nijhof
5d6b02f470 2025.7.1 (#148171) 2025-07-04 22:00:18 +02:00
Franck Nijhof
a274961593 Bump version to 2025.7.1 2025-07-04 19:22:41 +00:00
Michael Freeman
4e163c4591 Bump venstarcolortouch to 0.21 (#148152) 2025-07-04 19:21:33 +00:00
Marc Mueller
3ffec2a655 [ci] Fix typing issue with aiohttp and aiosignal (#148141) 2025-07-04 19:21:31 +00:00
Bram Kragten
c646658643 Update frontend to 20250702.1 (#148131) 2025-07-04 19:21:30 +00:00
Simone Chemelli
342b4c3442 Bump aioamazondevices to 3.2.3 (#148082) 2025-07-04 19:21:28 +00:00
Arie Catsman
eb58c10e5e Cancel enphase mac verification on unload. (#148072) 2025-07-04 19:21:27 +00:00
Arie Catsman
f42e7d982f Bump pyenphase to 2.2.0 (#148070) 2025-07-04 19:21:25 +00:00
hanwg
898ef43750 Fix Telegram bots using plain text parser failing to load on restart (#148050) 2025-07-04 19:21:24 +00:00
Joakim Sørensen
f806e6ba49 Bump hass-nabucasa from 0.104.0 to 0.105.0 (#148040) 2025-07-04 19:21:23 +00:00
Marcel van der Veldt
c23bfb1b39 Fix state being incorrectly reported in some situations on Music Assistant players (#147997) 2025-07-04 19:21:22 +00:00
Robert Svensson
a2ffe32b02 Bump aiounifi to v84 (#147987) 2025-07-04 19:21:21 +00:00
puddly
0f32b6331d Bump ZHA to 0.0.62 (#147966) 2025-07-04 19:21:19 +00:00
epenet
9a4959560e Fix missing port in samsungtv (#147962)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-07-04 19:21:18 +00:00
Thomas55555
41ab7b346c Set timeout for remote calendar (#147024) 2025-07-04 19:21:17 +00:00
1978 changed files with 165533 additions and 21522 deletions

View File

@@ -27,7 +27,7 @@ jobs:
publish: ${{ steps.version.outputs.publish }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
@@ -90,7 +90,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
@@ -175,7 +175,7 @@ jobs:
sed -i "s|pykrakenapi|# pykrakenapi|g" requirements_all.txt
- name: Download translations
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: translations
@@ -190,7 +190,7 @@ jobs:
echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE
- name: Login to GitHub Container Registry
uses: docker/login-action@v3.4.0
uses: docker/login-action@v3.5.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -242,7 +242,7 @@ jobs:
- green
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set build additional args
run: |
@@ -256,7 +256,7 @@ jobs:
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@v3.4.0
uses: docker/login-action@v3.5.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -279,7 +279,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
@@ -321,7 +321,7 @@ jobs:
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Install Cosign
uses: sigstore/cosign-installer@v3.9.2
@@ -330,14 +330,14 @@ jobs:
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
uses: docker/login-action@v3.4.0
uses: docker/login-action@v3.5.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: matrix.registry == 'ghcr.io/home-assistant'
uses: docker/login-action@v3.4.0
uses: docker/login-action@v3.5.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -454,7 +454,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.6.0
@@ -462,7 +462,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Download translations
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: translations
@@ -499,10 +499,10 @@ jobs:
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Login to GitHub Container Registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}

View File

@@ -37,10 +37,10 @@ on:
type: boolean
env:
CACHE_VERSION: 4
CACHE_VERSION: 7
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2025.8"
HA_SHORT_VERSION: "2025.9"
DEFAULT_PYTHON: "3.13"
ALL_PYTHON_VERSIONS: "['3.13']"
# 10.3 is the oldest supported version
@@ -94,7 +94,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Generate partial Python venv restore key
id: generate_python_cache_key
run: |
@@ -246,7 +246,7 @@ jobs:
- info
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -255,7 +255,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: venv
key: >-
@@ -271,7 +271,7 @@ jobs:
uv pip install "$(cat requirements_test.txt | grep pre-commit)"
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: ${{ env.PRE_COMMIT_CACHE }}
lookup-only: true
@@ -292,7 +292,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.6.0
id: python
@@ -301,7 +301,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -310,7 +310,7 @@ jobs:
needs.info.outputs.pre-commit_cache_key }}
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: ${{ env.PRE_COMMIT_CACHE }}
fail-on-cache-miss: true
@@ -332,7 +332,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.6.0
id: python
@@ -341,7 +341,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -350,7 +350,7 @@ jobs:
needs.info.outputs.pre-commit_cache_key }}
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: ${{ env.PRE_COMMIT_CACHE }}
fail-on-cache-miss: true
@@ -372,7 +372,7 @@ jobs:
- pre-commit
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.6.0
id: python
@@ -381,7 +381,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -390,7 +390,7 @@ jobs:
needs.info.outputs.pre-commit_cache_key }}
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: ${{ env.PRE_COMMIT_CACHE }}
fail-on-cache-miss: true
@@ -462,7 +462,7 @@ jobs:
- script/hassfest/docker/Dockerfile
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Register hadolint problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
@@ -481,7 +481,7 @@ jobs:
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -497,7 +497,7 @@ jobs:
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: venv
key: >-
@@ -505,7 +505,7 @@ jobs:
needs.info.outputs.python_cache_key }}
- name: Restore uv wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: ${{ env.UV_CACHE_DIR }}
key: >-
@@ -584,7 +584,7 @@ jobs:
sudo apt-get -y install \
libturbojpeg
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -593,7 +593,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -617,7 +617,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -626,7 +626,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -651,9 +651,9 @@ jobs:
&& github.event_name == 'pull_request'
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Dependency review
uses: actions/dependency-review-action@v4.7.1
uses: actions/dependency-review-action@v4.7.2
with:
license-check: false # We use our own license audit checks
@@ -674,7 +674,7 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -683,7 +683,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -717,7 +717,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -726,7 +726,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -764,7 +764,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -773,7 +773,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -809,7 +809,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -825,7 +825,7 @@ jobs:
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -833,7 +833,7 @@ jobs:
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Restore mypy cache
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: .mypy_cache
key: >-
@@ -886,7 +886,7 @@ jobs:
libturbojpeg \
libgammu-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -895,7 +895,7 @@ jobs:
check-latest: true
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -947,7 +947,7 @@ jobs:
libgammu-dev \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -956,7 +956,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -970,7 +970,7 @@ jobs:
run: |
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
- name: Download pytest_buckets
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: pytest_buckets
- name: Compile English translations
@@ -1080,7 +1080,7 @@ jobs:
libmariadb-dev-compat \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -1089,7 +1089,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -1222,7 +1222,7 @@ jobs:
sudo apt-get -y install \
postgresql-server-dev-14
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -1231,7 +1231,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -1334,14 +1334,14 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@v5.4.3
uses: codecov/codecov-action@v5.5.0
with:
fail_ci_if_error: true
flags: full-suite
@@ -1381,7 +1381,7 @@ jobs:
libgammu-dev \
libxml2-utils
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v5.6.0
@@ -1390,7 +1390,7 @@ jobs:
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache/restore@v4.2.3
uses: actions/cache/restore@v4.2.4
with:
path: venv
fail-on-cache-miss: true
@@ -1484,14 +1484,14 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@v5.4.3
uses: codecov/codecov-action@v5.5.0
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
@@ -1511,7 +1511,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
pattern: test-results-*
- name: Upload test results to Codecov

View File

@@ -21,14 +21,14 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.29.4
uses: github/codeql-action/init@v3.29.11
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.29.4
uses: github/codeql-action/analyze@v3.29.11
with:
category: "/language:python"

View File

@@ -231,7 +231,7 @@ jobs:
- name: Detect duplicates using AI
id: ai_detection
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
uses: actions/ai-inference@v1.2.3
uses: actions/ai-inference@v2.0.0
with:
model: openai/gpt-4o
system-prompt: |

View File

@@ -57,7 +57,7 @@ jobs:
- name: Detect language using AI
id: ai_language_detection
if: steps.detect_language.outputs.should_continue == 'true'
uses: actions/ai-inference@v1.2.3
uses: actions/ai-inference@v2.0.0
with:
model: openai/gpt-4o-mini
system-prompt: |

View File

@@ -9,7 +9,7 @@ jobs:
check-authorization:
runs-on: ubuntu-latest
# Only run if this is a Task issue type (from the issue form)
if: github.event.issue.issue_type == 'Task'
if: github.event.issue.type.name == 'Task'
steps:
- name: Check if user is authorized
uses: actions/github-script@v7

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.6.0

View File

@@ -32,7 +32,7 @@ jobs:
architectures: ${{ steps.info.outputs.architectures }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
@@ -135,20 +135,20 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Download env_file
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: requirements_diff
@@ -159,7 +159,7 @@ jobs:
sed -i "/uv/d" requirements_diff.txt
- name: Build wheels
uses: home-assistant/wheels@2025.03.0
uses: home-assistant/wheels@2025.07.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2
@@ -184,25 +184,25 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Download env_file
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: env_file
- name: Download build_constraints
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: build_constraints
- name: Download requirements_diff
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: requirements_diff
- name: Download requirements_all_wheels
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@v5.0.0
with:
name: requirements_all_wheels
@@ -219,7 +219,7 @@ jobs:
sed -i "/uv/d" requirements_diff.txt
- name: Build wheels
uses: home-assistant/wheels@2025.03.0
uses: home-assistant/wheels@2025.07.0
with:
abi: ${{ matrix.abi }}
tag: musllinux_1_2

View File

@@ -18,7 +18,7 @@ repos:
exclude_types: [csv, json, html]
exclude: ^tests/fixtures/|homeassistant/generated/|tests/components/.*/snapshots/
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: check-executables-have-shebangs
stages: [manual]

View File

@@ -53,6 +53,7 @@ homeassistant.components.air_quality.*
homeassistant.components.airgradient.*
homeassistant.components.airly.*
homeassistant.components.airnow.*
homeassistant.components.airos.*
homeassistant.components.airq.*
homeassistant.components.airthings.*
homeassistant.components.airthings_ble.*
@@ -309,7 +310,6 @@ homeassistant.components.letpot.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
homeassistant.components.linear_garage_door.*
homeassistant.components.linkplay.*
homeassistant.components.litejet.*
homeassistant.components.litterrobot.*
@@ -466,6 +466,7 @@ homeassistant.components.simplisafe.*
homeassistant.components.siren.*
homeassistant.components.skybell.*
homeassistant.components.slack.*
homeassistant.components.sleep_as_android.*
homeassistant.components.sleepiq.*
homeassistant.components.smhi.*
homeassistant.components.smlight.*
@@ -501,6 +502,7 @@ homeassistant.components.tag.*
homeassistant.components.tailscale.*
homeassistant.components.tailwind.*
homeassistant.components.tami4.*
homeassistant.components.tankerkoenig.*
homeassistant.components.tautulli.*
homeassistant.components.tcp.*
homeassistant.components.technove.*
@@ -546,6 +548,7 @@ homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
homeassistant.components.volvo.*
homeassistant.components.wake_on_lan.*
homeassistant.components.wake_word.*
homeassistant.components.wallbox.*

22
CODEOWNERS generated
View File

@@ -67,6 +67,8 @@ build.json @home-assistant/supervisor
/tests/components/airly/ @bieniu
/homeassistant/components/airnow/ @asymworks
/tests/components/airnow/ @asymworks
/homeassistant/components/airos/ @CoMPaTech
/tests/components/airos/ @CoMPaTech
/homeassistant/components/airq/ @Sibgatulin @dl2080
/tests/components/airq/ @Sibgatulin @dl2080
/homeassistant/components/airthings/ @danielhiversen @LaStrada
@@ -154,8 +156,8 @@ build.json @home-assistant/supervisor
/tests/components/assist_pipeline/ @balloob @synesthesiam
/homeassistant/components/assist_satellite/ @home-assistant/core @synesthesiam
/tests/components/assist_satellite/ @home-assistant/core @synesthesiam
/homeassistant/components/asuswrt/ @kennedyshead @ollo69
/tests/components/asuswrt/ @kennedyshead @ollo69
/homeassistant/components/asuswrt/ @kennedyshead @ollo69 @Vaskivskyi
/tests/components/asuswrt/ @kennedyshead @ollo69 @Vaskivskyi
/homeassistant/components/atag/ @MatsNL
/tests/components/atag/ @MatsNL
/homeassistant/components/aten_pe/ @mtdcr
@@ -420,6 +422,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/emby/ @mezz64
/homeassistant/components/emoncms/ @borpin @alexandrecuer
/tests/components/emoncms/ @borpin @alexandrecuer
/homeassistant/components/emoncms_history/ @alexandrecuer
/tests/components/emoncms_history/ @alexandrecuer
/homeassistant/components/emonitor/ @bdraco
/tests/components/emonitor/ @bdraco
/homeassistant/components/emulated_hue/ @bdraco @Tho85
@@ -436,8 +440,8 @@ build.json @home-assistant/supervisor
/tests/components/enigma2/ @autinerd
/homeassistant/components/enocean/ @bdurrer
/tests/components/enocean/ @bdurrer
/homeassistant/components/enphase_envoy/ @bdraco @cgarwood @joostlek @catsmanac
/tests/components/enphase_envoy/ @bdraco @cgarwood @joostlek @catsmanac
/homeassistant/components/enphase_envoy/ @bdraco @cgarwood @catsmanac
/tests/components/enphase_envoy/ @bdraco @cgarwood @catsmanac
/homeassistant/components/entur_public_transport/ @hfurubotten
/homeassistant/components/environment_canada/ @gwww @michaeldavie
/tests/components/environment_canada/ @gwww @michaeldavie
@@ -860,8 +864,6 @@ build.json @home-assistant/supervisor
/tests/components/lifx/ @Djelibeybi
/homeassistant/components/light/ @home-assistant/core
/tests/components/light/ @home-assistant/core
/homeassistant/components/linear_garage_door/ @IceBotYT
/tests/components/linear_garage_door/ @IceBotYT
/homeassistant/components/linkplay/ @Velleman
/tests/components/linkplay/ @Velleman
/homeassistant/components/linux_battery/ @fabaff
@@ -1415,6 +1417,8 @@ build.json @home-assistant/supervisor
/tests/components/skybell/ @tkdrob
/homeassistant/components/slack/ @tkdrob @fletcherau
/tests/components/slack/ @tkdrob @fletcherau
/homeassistant/components/sleep_as_android/ @tr4nt0r
/tests/components/sleep_as_android/ @tr4nt0r
/homeassistant/components/sleepiq/ @mfugate1 @kbickar
/tests/components/sleepiq/ @mfugate1 @kbickar
/homeassistant/components/slide/ @ualex73
@@ -1597,6 +1601,8 @@ build.json @home-assistant/supervisor
/tests/components/todo/ @home-assistant/core
/homeassistant/components/todoist/ @boralyl
/tests/components/todoist/ @boralyl
/homeassistant/components/togrill/ @elupus
/tests/components/togrill/ @elupus
/homeassistant/components/tolo/ @MatthiasLohr
/tests/components/tolo/ @MatthiasLohr
/homeassistant/components/tomorrowio/ @raman325 @lymanepp
@@ -1611,8 +1617,6 @@ build.json @home-assistant/supervisor
/tests/components/tplink_omada/ @MarkGodwin
/homeassistant/components/traccar/ @ludeeus
/tests/components/traccar/ @ludeeus
/homeassistant/components/traccar_server/ @ludeeus
/tests/components/traccar_server/ @ludeeus
/homeassistant/components/trace/ @home-assistant/core
/tests/components/trace/ @home-assistant/core
/homeassistant/components/tractive/ @Danielhiversen @zhulik @bieniu
@@ -1706,6 +1710,8 @@ build.json @home-assistant/supervisor
/tests/components/voip/ @balloob @synesthesiam @jaminh
/homeassistant/components/volumio/ @OnFreund
/tests/components/volumio/ @OnFreund
/homeassistant/components/volvo/ @thomasddn
/tests/components/volvo/ @thomasddn
/homeassistant/components/volvooncall/ @molobrakos
/tests/components/volvooncall/ @molobrakos
/homeassistant/components/vulcan/ @Antoni-Czaplicki

View File

@@ -14,5 +14,8 @@ Still interested? Then you should take a peek at the [developer documentation](h
## Feature suggestions
If you want to suggest a new feature for Home Assistant (e.g., new integrations), please open a thread in our [Community Forum: Feature Requests](https://community.home-assistant.io/c/feature-requests).
We use [GitHub for tracking issues](https://github.com/home-assistant/core/issues), not for tracking feature requests.
If you want to suggest a new feature for Home Assistant (e.g. new integrations), please [start a discussion](https://github.com/orgs/home-assistant/discussions) on GitHub.
## Issue Tracker
If you want to report an issue, please [create an issue](https://github.com/home-assistant/core/issues) on GitHub.

2
Dockerfile generated
View File

@@ -31,7 +31,7 @@ RUN \
&& go2rtc --version
# Install uv
RUN pip3 install uv==0.7.1
RUN pip3 install uv==0.8.9
WORKDIR /usr/src

View File

@@ -120,6 +120,9 @@ class AuthStore:
new_user = models.User(**kwargs)
while new_user.id in self._users:
new_user = models.User(**kwargs)
self._users[new_user.id] = new_user
if credentials is None:

View File

@@ -33,7 +33,10 @@ class AuthFlowContext(FlowContext, total=False):
redirect_uri: str
AuthFlowResult = FlowResult[AuthFlowContext, tuple[str, str]]
class AuthFlowResult(FlowResult[AuthFlowContext, tuple[str, str]], total=False):
"""Typed result dict for auth flow."""
result: Credentials # Only present if type is CREATE_ENTRY
@attr.s(slots=True)

View File

@@ -0,0 +1,5 @@
{
"domain": "frient",
"name": "Frient",
"iot_standards": ["zigbee"]
}

View File

@@ -1,5 +1,5 @@
{
"domain": "third_reality",
"name": "Third Reality",
"iot_standards": ["zigbee"]
"iot_standards": ["matter", "zigbee"]
}

View File

@@ -1,5 +1,5 @@
{
"domain": "ubiquiti",
"name": "Ubiquiti",
"integrations": ["unifi", "unifi_direct", "unifiled", "unifiprotect"]
"integrations": ["airos", "unifi", "unifi_direct", "unifiled", "unifiprotect"]
}

View File

@@ -61,7 +61,7 @@
"display_pm_standard": {
"name": "Display PM standard",
"state": {
"ugm3": "µg/m³",
"ugm3": "μg/m³",
"us_aqi": "US AQI"
}
},

View File

@@ -0,0 +1,45 @@
"""The Ubiquiti airOS integration."""
from __future__ import annotations
from airos.airos8 import AirOS
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import AirOSConfigEntry, AirOSDataUpdateCoordinator
_PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Platform.SENSOR,
]
async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
"""Set up Ubiquiti airOS from a config entry."""
# By default airOS 8 comes with self-signed SSL certificates,
# with no option in the web UI to change or upload a custom certificate.
session = async_get_clientsession(hass, verify_ssl=False)
airos_device = AirOS(
host=entry.data[CONF_HOST],
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
session=session,
)
coordinator = AirOSDataUpdateCoordinator(hass, entry, airos_device)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, _PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, _PLATFORMS)

View File

@@ -0,0 +1,106 @@
"""AirOS Binary Sensor component for Home Assistant."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AirOSConfigEntry, AirOSData, AirOSDataUpdateCoordinator
from .entity import AirOSEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AirOSBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describe an AirOS binary sensor."""
value_fn: Callable[[AirOSData], bool]
BINARY_SENSORS: tuple[AirOSBinarySensorEntityDescription, ...] = (
AirOSBinarySensorEntityDescription(
key="portfw",
translation_key="port_forwarding",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.portfw,
),
AirOSBinarySensorEntityDescription(
key="dhcp_client",
translation_key="dhcp_client",
device_class=BinarySensorDeviceClass.RUNNING,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.services.dhcpc,
),
AirOSBinarySensorEntityDescription(
key="dhcp_server",
translation_key="dhcp_server",
device_class=BinarySensorDeviceClass.RUNNING,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.services.dhcpd,
entity_registry_enabled_default=False,
),
AirOSBinarySensorEntityDescription(
key="dhcp6_server",
translation_key="dhcp6_server",
device_class=BinarySensorDeviceClass.RUNNING,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.services.dhcp6d_stateful,
entity_registry_enabled_default=False,
),
AirOSBinarySensorEntityDescription(
key="pppoe",
translation_key="pppoe",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.services.pppoe,
entity_registry_enabled_default=False,
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AirOSConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the AirOS binary sensors from a config entry."""
coordinator = config_entry.runtime_data
async_add_entities(
AirOSBinarySensor(coordinator, description) for description in BINARY_SENSORS
)
class AirOSBinarySensor(AirOSEntity, BinarySensorEntity):
"""Representation of a binary sensor."""
entity_description: AirOSBinarySensorEntityDescription
def __init__(
self,
coordinator: AirOSDataUpdateCoordinator,
description: AirOSBinarySensorEntityDescription,
) -> None:
"""Initialize the binary sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.data.host.device_id}_{description.key}"
@property
def is_on(self) -> bool:
"""Return the state of the binary sensor."""
return self.entity_description.value_fn(self.coordinator.data)

View File

@@ -0,0 +1,82 @@
"""Config flow for the Ubiquiti airOS integration."""
from __future__ import annotations
import logging
from typing import Any
from airos.exceptions import (
AirOSConnectionAuthenticationError,
AirOSConnectionSetupError,
AirOSDataMissingError,
AirOSDeviceConnectionError,
AirOSKeyDataMissingError,
)
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .coordinator import AirOS
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_USERNAME, default="ubnt"): str,
vol.Required(CONF_PASSWORD): str,
}
)
class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Ubiquiti airOS."""
VERSION = 1
async def async_step_user(
self,
user_input: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
# By default airOS 8 comes with self-signed SSL certificates,
# with no option in the web UI to change or upload a custom certificate.
session = async_get_clientsession(self.hass, verify_ssl=False)
airos_device = AirOS(
host=user_input[CONF_HOST],
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
session=session,
)
try:
await airos_device.login()
airos_data = await airos_device.status()
except (
AirOSConnectionSetupError,
AirOSDeviceConnectionError,
):
errors["base"] = "cannot_connect"
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
errors["base"] = "invalid_auth"
except AirOSKeyDataMissingError:
errors["base"] = "key_data_missing"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(airos_data.derived.mac)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=airos_data.host.hostname, data=user_input
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)

View File

@@ -0,0 +1,9 @@
"""Constants for the Ubiquiti airOS integration."""
from datetime import timedelta
DOMAIN = "airos"
SCAN_INTERVAL = timedelta(minutes=1)
MANUFACTURER = "Ubiquiti"

View File

@@ -0,0 +1,70 @@
"""DataUpdateCoordinator for AirOS."""
from __future__ import annotations
import logging
from airos.airos8 import AirOS, AirOSData
from airos.exceptions import (
AirOSConnectionAuthenticationError,
AirOSConnectionSetupError,
AirOSDataMissingError,
AirOSDeviceConnectionError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, SCAN_INTERVAL
_LOGGER = logging.getLogger(__name__)
type AirOSConfigEntry = ConfigEntry[AirOSDataUpdateCoordinator]
class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOSData]):
"""Class to manage fetching AirOS data from single endpoint."""
config_entry: AirOSConfigEntry
def __init__(
self, hass: HomeAssistant, config_entry: AirOSConfigEntry, airos_device: AirOS
) -> None:
"""Initialize the coordinator."""
self.airos_device = airos_device
super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self) -> AirOSData:
"""Fetch data from AirOS."""
try:
await self.airos_device.login()
return await self.airos_device.status()
except (AirOSConnectionAuthenticationError,) as err:
_LOGGER.exception("Error authenticating with airOS device")
raise ConfigEntryError(
translation_domain=DOMAIN, translation_key="invalid_auth"
) from err
except (
AirOSConnectionSetupError,
AirOSDeviceConnectionError,
TimeoutError,
) as err:
_LOGGER.error("Error connecting to airOS device: %s", err)
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from err
except (AirOSDataMissingError,) as err:
_LOGGER.error("Expected data not returned by airOS device: %s", err)
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="error_data_missing",
) from err

View File

@@ -0,0 +1,33 @@
"""Diagnostics support for airOS."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_HOST, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from .coordinator import AirOSConfigEntry
IP_REDACT = ["addr", "ipaddr", "ip6addr", "lastip"] # IP related
HW_REDACT = ["apmac", "hwaddr", "mac"] # MAC address
TO_REDACT_HA = [CONF_HOST, CONF_PASSWORD]
TO_REDACT_AIROS = [
"hostname", # Prevent leaking device naming
"essid", # Network SSID
"lat", # GPS latitude to prevent exposing location data.
"lon", # GPS longitude to prevent exposing location data.
*HW_REDACT,
*IP_REDACT,
]
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: AirOSConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return {
"entry_data": async_redact_data(entry.data, TO_REDACT_HA),
"data": async_redact_data(entry.runtime_data.data.to_dict(), TO_REDACT_AIROS),
}

View File

@@ -0,0 +1,36 @@
"""Generic AirOS Entity Class."""
from __future__ import annotations
from homeassistant.const import CONF_HOST
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
from .coordinator import AirOSDataUpdateCoordinator
class AirOSEntity(CoordinatorEntity[AirOSDataUpdateCoordinator]):
"""Represent a AirOS Entity."""
_attr_has_entity_name = True
def __init__(self, coordinator: AirOSDataUpdateCoordinator) -> None:
"""Initialise the gateway."""
super().__init__(coordinator)
airos_data = self.coordinator.data
configuration_url: str | None = (
f"https://{coordinator.config_entry.data[CONF_HOST]}"
)
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_NETWORK_MAC, airos_data.derived.mac)},
configuration_url=configuration_url,
identifiers={(DOMAIN, str(airos_data.host.device_id))},
manufacturer=MANUFACTURER,
model=airos_data.host.devmodel,
name=airos_data.host.hostname,
sw_version=airos_data.host.fwversion,
)

View File

@@ -0,0 +1,10 @@
{
"domain": "airos",
"name": "Ubiquiti airOS",
"codeowners": ["@CoMPaTech"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airos",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["airos==0.4.3"]
}

View File

@@ -0,0 +1,70 @@
rules:
# Bronze
action-setup:
status: exempt
comment: airOS does not have actions
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: airOS does not have actions
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: local_polling without events
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: airOS does not have actions
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: todo
integration-owner: done
log-when-unavailable: todo
parallel-updates: todo
reauthentication-flow: todo
test-coverage: done
# Gold
devices: done
diagnostics: done
discovery-update-info: todo
discovery: todo
docs-data-update: done
docs-examples: todo
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: todo
docs-troubleshooting: done
docs-use-cases: todo
dynamic-devices: todo
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
icon-translations:
status: exempt
comment: no (custom) icons used or envisioned
reconfiguration-flow: todo
repair-issues: todo
stale-devices: todo
# Platinum
async-dependency: done
inject-websession: done
strict-typing: done

View File

@@ -0,0 +1,194 @@
"""AirOS Sensor component for Home Assistant."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from airos.data import DerivedWirelessMode, DerivedWirelessRole, NetRole
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS,
UnitOfDataRate,
UnitOfFrequency,
UnitOfLength,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .coordinator import AirOSConfigEntry, AirOSData, AirOSDataUpdateCoordinator
from .entity import AirOSEntity
_LOGGER = logging.getLogger(__name__)
NETROLE_OPTIONS = [mode.value for mode in NetRole]
WIRELESS_MODE_OPTIONS = [mode.value for mode in DerivedWirelessMode]
WIRELESS_ROLE_OPTIONS = [mode.value for mode in DerivedWirelessRole]
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AirOSSensorEntityDescription(SensorEntityDescription):
"""Describe an AirOS sensor."""
value_fn: Callable[[AirOSData], StateType]
SENSORS: tuple[AirOSSensorEntityDescription, ...] = (
AirOSSensorEntityDescription(
key="host_cpuload",
translation_key="host_cpuload",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
value_fn=lambda data: data.host.cpuload,
entity_registry_enabled_default=False,
),
AirOSSensorEntityDescription(
key="host_netrole",
translation_key="host_netrole",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data.host.netrole.value,
options=NETROLE_OPTIONS,
),
AirOSSensorEntityDescription(
key="wireless_frequency",
translation_key="wireless_frequency",
native_unit_of_measurement=UnitOfFrequency.MEGAHERTZ,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda data: data.wireless.frequency,
),
AirOSSensorEntityDescription(
key="wireless_essid",
translation_key="wireless_essid",
value_fn=lambda data: data.wireless.essid,
),
AirOSSensorEntityDescription(
key="wireless_antenna_gain",
translation_key="wireless_antenna_gain",
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda data: data.wireless.antenna_gain,
),
AirOSSensorEntityDescription(
key="wireless_throughput_tx",
translation_key="wireless_throughput_tx",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
suggested_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
value_fn=lambda data: data.wireless.throughput.tx,
),
AirOSSensorEntityDescription(
key="wireless_throughput_rx",
translation_key="wireless_throughput_rx",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
suggested_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
value_fn=lambda data: data.wireless.throughput.rx,
),
AirOSSensorEntityDescription(
key="wireless_polling_dl_capacity",
translation_key="wireless_polling_dl_capacity",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
suggested_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
value_fn=lambda data: data.wireless.polling.dl_capacity,
),
AirOSSensorEntityDescription(
key="wireless_polling_ul_capacity",
translation_key="wireless_polling_ul_capacity",
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
device_class=SensorDeviceClass.DATA_RATE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
suggested_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND,
value_fn=lambda data: data.wireless.polling.ul_capacity,
),
AirOSSensorEntityDescription(
key="host_uptime",
translation_key="host_uptime",
native_unit_of_measurement=UnitOfTime.SECONDS,
device_class=SensorDeviceClass.DURATION,
suggested_display_precision=0,
suggested_unit_of_measurement=UnitOfTime.DAYS,
value_fn=lambda data: data.host.uptime,
entity_registry_enabled_default=False,
),
AirOSSensorEntityDescription(
key="wireless_distance",
translation_key="wireless_distance",
native_unit_of_measurement=UnitOfLength.METERS,
device_class=SensorDeviceClass.DISTANCE,
suggested_display_precision=1,
suggested_unit_of_measurement=UnitOfLength.KILOMETERS,
value_fn=lambda data: data.wireless.distance,
),
AirOSSensorEntityDescription(
key="wireless_mode",
translation_key="wireless_mode",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data.derived.mode.value,
options=WIRELESS_MODE_OPTIONS,
entity_registry_enabled_default=False,
),
AirOSSensorEntityDescription(
key="wireless_role",
translation_key="wireless_role",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data.derived.role.value,
options=WIRELESS_ROLE_OPTIONS,
entity_registry_enabled_default=False,
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AirOSConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the AirOS sensors from a config entry."""
coordinator = config_entry.runtime_data
async_add_entities(AirOSSensor(coordinator, description) for description in SENSORS)
class AirOSSensor(AirOSEntity, SensorEntity):
"""Representation of a Sensor."""
entity_description: AirOSSensorEntityDescription
def __init__(
self,
coordinator: AirOSDataUpdateCoordinator,
description: AirOSSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.data.derived.mac}_{description.key}"
@property
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data)

View File

@@ -0,0 +1,117 @@
{
"config": {
"flow_title": "Ubiquiti airOS device",
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "IP address or hostname of the airOS device",
"username": "Administrator username for the airOS device, normally 'ubnt'",
"password": "Password configured through the UISP app or web interface"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"key_data_missing": "Expected data not returned from the device, check the documentation for supported devices",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},
"entity": {
"binary_sensor": {
"port_forwarding": {
"name": "Port forwarding"
},
"dhcp_client": {
"name": "DHCP client"
},
"dhcp_server": {
"name": "DHCP server"
},
"dhcp6_server": {
"name": "DHCPv6 server"
},
"pppoe": {
"name": "PPPoE link"
}
},
"sensor": {
"host_cpuload": {
"name": "CPU load"
},
"host_netrole": {
"name": "Network role",
"state": {
"bridge": "Bridge",
"router": "Router"
}
},
"wireless_frequency": {
"name": "Wireless frequency"
},
"wireless_essid": {
"name": "Wireless SSID"
},
"wireless_antenna_gain": {
"name": "Antenna gain"
},
"wireless_throughput_tx": {
"name": "Throughput transmit (actual)"
},
"wireless_throughput_rx": {
"name": "Throughput receive (actual)"
},
"wireless_polling_dl_capacity": {
"name": "Download capacity"
},
"wireless_polling_ul_capacity": {
"name": "Upload capacity"
},
"wireless_remote_hostname": {
"name": "Remote hostname"
},
"host_uptime": {
"name": "Uptime"
},
"wireless_distance": {
"name": "Wireless distance"
},
"wireless_role": {
"name": "Wireless role",
"state": {
"access_point": "Access point",
"station": "Station"
}
},
"wireless_mode": {
"name": "Wireless mode",
"state": {
"point_to_point": "Point-to-point",
"point_to_multipoint": "Point-to-multipoint"
}
}
}
},
"exceptions": {
"invalid_auth": {
"message": "[%key:common::config_flow::error::invalid_auth%]"
},
"cannot_connect": {
"message": "[%key:common::config_flow::error::cannot_connect%]"
},
"key_data_missing": {
"message": "Key data not returned from device"
},
"error_data_missing": {
"message": "Data incomplete or missing"
}
}
}

View File

@@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
from .const import CONF_CLIP_NEGATIVE, CONF_RETURN_AVERAGE
from .coordinator import AirQCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR]
PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR]
AirQConfigEntry = ConfigEntry[AirQCoordinator]

View File

@@ -75,6 +75,7 @@ class AirQCoordinator(DataUpdateCoordinator):
return_average=self.return_average,
clip_negative_values=self.clip_negative,
)
data["brightness"] = await self.airq.get_current_brightness()
if warming_up_sensors := identify_warming_up_sensors(data):
_LOGGER.debug(
"Following sensors are still warming up: %s", warming_up_sensors

View File

@@ -0,0 +1,85 @@
"""Definition of air-Q number platform used to control the LED strips."""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
import logging
from aioairq.core import AirQ
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import AirQConfigEntry, AirQCoordinator
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True, kw_only=True)
class AirQBrightnessDescription(NumberEntityDescription):
"""Describes AirQ number entity responsible for brightness control."""
value: Callable[[dict], float]
set_value: Callable[[AirQ, float], Awaitable[None]]
AIRQ_LED_BRIGHTNESS = AirQBrightnessDescription(
key="airq_led_brightness",
translation_key="airq_led_brightness",
native_min_value=0.0,
native_max_value=100.0,
native_step=1.0,
native_unit_of_measurement=PERCENTAGE,
value=lambda data: data["brightness"],
set_value=lambda device, value: device.set_current_brightness(value),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AirQConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up number entities: a single entity for the LEDs."""
coordinator = entry.runtime_data
entities = [AirQLEDBrightness(coordinator, AIRQ_LED_BRIGHTNESS)]
async_add_entities(entities)
class AirQLEDBrightness(CoordinatorEntity[AirQCoordinator], NumberEntity):
"""Representation of the LEDs from a single AirQ."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AirQCoordinator,
description: AirQBrightnessDescription,
) -> None:
"""Initialize a single sensor."""
super().__init__(coordinator)
self.entity_description: AirQBrightnessDescription = description
self._attr_device_info = coordinator.device_info
self._attr_unique_id = f"{coordinator.device_id}_{description.key}"
@property
def native_value(self) -> float:
"""Return the brightness of the LEDs in %."""
return self.entity_description.value(self.coordinator.data)
async def async_set_native_value(self, value: float) -> None:
"""Set the brightness of the LEDs to the value in %."""
_LOGGER.debug(
"Changing LED brighntess from %.0f%% to %.0f%%",
self.coordinator.data["brightness"],
value,
)
await self.entity_description.set_value(self.coordinator.airq, value)
await self.coordinator.async_request_refresh()

View File

@@ -35,6 +35,11 @@
}
},
"entity": {
"number": {
"airq_led_brightness": {
"name": "LED brightness"
}
},
"sensor": {
"acetaldehyde": {
"name": "Acetaldehyde"

View File

@@ -7,21 +7,18 @@ import logging
from airthings import Airthings
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import CONF_SECRET
from .coordinator import AirthingsDataUpdateCoordinator
from .coordinator import AirthingsConfigEntry, AirthingsDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.SENSOR]
SCAN_INTERVAL = timedelta(minutes=6)
type AirthingsConfigEntry = ConfigEntry[AirthingsDataUpdateCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) -> bool:
"""Set up Airthings from a config entry."""
@@ -31,7 +28,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirthingsConfigEntry) ->
async_get_clientsession(hass),
)
coordinator = AirthingsDataUpdateCoordinator(hass, airthings)
coordinator = AirthingsDataUpdateCoordinator(hass, airthings, entry)
await coordinator.async_config_entry_first_refresh()

View File

@@ -5,6 +5,7 @@ import logging
from airthings import Airthings, AirthingsDevice, AirthingsError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -13,15 +14,23 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=6)
type AirthingsConfigEntry = ConfigEntry[AirthingsDataUpdateCoordinator]
class AirthingsDataUpdateCoordinator(DataUpdateCoordinator[dict[str, AirthingsDevice]]):
"""Coordinator for Airthings data updates."""
def __init__(self, hass: HomeAssistant, airthings: Airthings) -> None:
def __init__(
self,
hass: HomeAssistant,
airthings: Airthings,
config_entry: AirthingsConfigEntry,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_method=self._update_method,
update_interval=SCAN_INTERVAL,

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
"iot_class": "cloud_push",
"loggers": ["aioairzone_cloud"],
"requirements": ["aioairzone-cloud==0.7.0"]
"requirements": ["aioairzone-cloud==0.7.1"]
}

View File

@@ -1,9 +1,13 @@
"""Alexa Devices integration."""
from homeassistant.const import Platform
from homeassistant.const import CONF_COUNTRY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import _LOGGER, COUNTRY_DOMAINS, DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .services import async_setup_services
PLATFORMS = [
Platform.BINARY_SENSOR,
@@ -12,11 +16,20 @@ PLATFORMS = [
Platform.SWITCH,
]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Alexa Devices component."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Set up Alexa Devices platform."""
coordinator = AmazonDevicesCoordinator(hass, entry)
session = aiohttp_client.async_create_clientsession(hass)
coordinator = AmazonDevicesCoordinator(hass, entry, session)
await coordinator.async_config_entry_first_refresh()
@@ -27,10 +40,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
return True
async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version == 1 and entry.minor_version == 0:
_LOGGER.debug(
"Migrating from version %s.%s", entry.version, entry.minor_version
)
# Convert country in domain
country = entry.data[CONF_COUNTRY]
domain = COUNTRY_DOMAINS.get(country, country)
# Save domain and remove country
new_data = entry.data.copy()
new_data.update({"site": f"https://www.amazon.{domain}"})
hass.config_entries.async_update_entry(
entry, data=new_data, version=1, minor_version=1
)
_LOGGER.info(
"Migration to version %s.%s successful", entry.version, entry.minor_version
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Unload a config entry."""
coordinator = entry.runtime_data
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
await coordinator.api.close()
return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -10,15 +10,14 @@ from aioamazondevices.exceptions import (
CannotAuthenticate,
CannotConnect,
CannotRetrieveData,
WrongCountry,
)
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_CODE, CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import CountrySelector
from .const import CONF_LOGIN_DATA, DOMAIN
@@ -28,28 +27,33 @@ STEP_REAUTH_DATA_SCHEMA = vol.Schema(
vol.Required(CONF_CODE): cv.string,
}
)
STEP_RECONFIGURE = vol.Schema(
{
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_CODE): cv.string,
}
)
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
"""Validate the user input allows us to connect."""
session = aiohttp_client.async_create_clientsession(hass)
api = AmazonEchoApi(
data[CONF_COUNTRY],
session,
data[CONF_USERNAME],
data[CONF_PASSWORD],
)
try:
data = await api.login_mode_interactive(data[CONF_CODE])
finally:
await api.close()
return data
return await api.login_mode_interactive(data[CONF_CODE])
class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Alexa Devices."""
VERSION = 1
MINOR_VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -60,12 +64,10 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
data = await validate_input(self.hass, user_input)
except CannotConnect:
errors["base"] = "cannot_connect"
except CannotAuthenticate:
except (CannotAuthenticate, TypeError):
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
except WrongCountry:
errors["base"] = "wrong_country"
else:
await self.async_set_unique_id(data["customer_info"]["user_id"])
self._abort_if_unique_id_configured()
@@ -80,9 +82,6 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
data_schema=vol.Schema(
{
vol.Required(
CONF_COUNTRY, default=self.hass.config.country
): CountrySelector(),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_CODE): cv.string,
@@ -111,7 +110,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
await validate_input(self.hass, {**reauth_entry.data, **user_input})
except CannotConnect:
errors["base"] = "cannot_connect"
except CannotAuthenticate:
except (CannotAuthenticate, TypeError):
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
@@ -131,3 +130,47 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
data_schema=STEP_REAUTH_DATA_SCHEMA,
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of the device."""
reconfigure_entry = self._get_reconfigure_entry()
if not user_input:
return self.async_show_form(
step_id="reconfigure",
data_schema=STEP_RECONFIGURE,
)
updated_password = user_input[CONF_PASSWORD]
self._async_abort_entries_match(
{CONF_USERNAME: reconfigure_entry.data[CONF_USERNAME]}
)
errors: dict[str, str] = {}
try:
data = await validate_input(
self.hass, {**reconfigure_entry.data, **user_input}
)
except CannotConnect:
errors["base"] = "cannot_connect"
except CannotAuthenticate:
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
else:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={
CONF_PASSWORD: updated_password,
CONF_LOGIN_DATA: data,
},
)
return self.async_show_form(
step_id="reconfigure",
data_schema=STEP_RECONFIGURE,
errors=errors,
)

View File

@@ -6,3 +6,22 @@ _LOGGER = logging.getLogger(__package__)
DOMAIN = "alexa_devices"
CONF_LOGIN_DATA = "login_data"
DEFAULT_DOMAIN = {"domain": "com"}
COUNTRY_DOMAINS = {
"ar": DEFAULT_DOMAIN,
"at": DEFAULT_DOMAIN,
"au": {"domain": "com.au"},
"be": {"domain": "com.be"},
"br": DEFAULT_DOMAIN,
"gb": {"domain": "co.uk"},
"il": DEFAULT_DOMAIN,
"jp": {"domain": "co.jp"},
"mx": {"domain": "com.mx"},
"no": DEFAULT_DOMAIN,
"nz": {"domain": "com.au"},
"pl": DEFAULT_DOMAIN,
"tr": {"domain": "com.tr"},
"us": DEFAULT_DOMAIN,
"za": {"domain": "co.za"},
}

View File

@@ -8,9 +8,10 @@ from aioamazondevices.exceptions import (
CannotConnect,
CannotRetrieveData,
)
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -31,6 +32,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
self,
hass: HomeAssistant,
entry: AmazonConfigEntry,
session: ClientSession,
) -> None:
"""Initialize the scanner."""
super().__init__(
@@ -41,7 +43,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
update_interval=timedelta(seconds=SCAN_INTERVAL),
)
self.api = AmazonEchoApi(
entry.data[CONF_COUNTRY],
session,
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
entry.data[CONF_LOGIN_DATA],
@@ -64,7 +66,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
translation_key="cannot_retrieve_data_with_error",
translation_placeholders={"error": repr(err)},
) from err
except CannotAuthenticate as err:
except (CannotAuthenticate, TypeError) as err:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",

View File

@@ -38,5 +38,13 @@
}
}
}
},
"services": {
"send_sound": {
"service": "mdi:cast-audio"
},
"send_text_command": {
"service": "mdi:microphone-message"
}
}
}

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "silver",
"requirements": ["aioamazondevices==3.5.1"]
"requirements": ["aioamazondevices==5.0.0"]
}

View File

@@ -48,19 +48,19 @@ rules:
comment: There are a ton of mac address ranges in use, but also by kindles which are not supported by this integration
docs-data-update: done
docs-examples: done
docs-known-limitations: todo
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: todo
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: todo
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: todo
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: no known use cases for repair issues or flows, yet
@@ -70,5 +70,5 @@ rules:
# Platinum
async-dependency: done
inject-websession: todo
inject-websession: done
strict-typing: done

View File

@@ -0,0 +1,121 @@
"""Support for services."""
from aioamazondevices.sounds import SOUNDS_LIST
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_DEVICE_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from .const import DOMAIN
from .coordinator import AmazonConfigEntry
ATTR_TEXT_COMMAND = "text_command"
ATTR_SOUND = "sound"
ATTR_SOUND_VARIANT = "sound_variant"
SERVICE_TEXT_COMMAND = "send_text_command"
SERVICE_SOUND_NOTIFICATION = "send_sound"
SCHEMA_SOUND_SERVICE = vol.Schema(
{
vol.Required(ATTR_SOUND): cv.string,
vol.Required(ATTR_SOUND_VARIANT): cv.positive_int,
vol.Required(ATTR_DEVICE_ID): cv.string,
},
)
SCHEMA_CUSTOM_COMMAND = vol.Schema(
{
vol.Required(ATTR_TEXT_COMMAND): cv.string,
vol.Required(ATTR_DEVICE_ID): cv.string,
}
)
@callback
def async_get_entry_id_for_service_call(
call: ServiceCall,
) -> tuple[dr.DeviceEntry, AmazonConfigEntry]:
"""Get the entry ID related to a service call (by device ID)."""
device_registry = dr.async_get(call.hass)
device_id = call.data[ATTR_DEVICE_ID]
if (device_entry := device_registry.async_get(device_id)) is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_device_id",
translation_placeholders={"device_id": device_id},
)
for entry_id in device_entry.config_entries:
if (entry := call.hass.config_entries.async_get_entry(entry_id)) is None:
continue
if entry.domain == DOMAIN:
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_loaded",
translation_placeholders={"entry": entry.title},
)
return (device_entry, entry)
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="config_entry_not_found",
translation_placeholders={"device_id": device_id},
)
async def _async_execute_action(call: ServiceCall, attribute: str) -> None:
"""Execute action on the device."""
device, config_entry = async_get_entry_id_for_service_call(call)
assert device.serial_number
value: str = call.data[attribute]
coordinator = config_entry.runtime_data
if attribute == ATTR_SOUND:
variant: int = call.data[ATTR_SOUND_VARIANT]
pad = "_" if variant > 10 else "_0"
file = f"{value}{pad}{variant!s}"
if value not in SOUNDS_LIST or variant > SOUNDS_LIST[value]:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_sound_value",
translation_placeholders={"sound": value, "variant": str(variant)},
)
await coordinator.api.call_alexa_sound(
coordinator.data[device.serial_number], file
)
elif attribute == ATTR_TEXT_COMMAND:
await coordinator.api.call_alexa_text_command(
coordinator.data[device.serial_number], value
)
async def async_send_sound_notification(call: ServiceCall) -> None:
"""Send a sound notification to a AmazonDevice."""
await _async_execute_action(call, ATTR_SOUND)
async def async_send_text_command(call: ServiceCall) -> None:
"""Send a custom command to a AmazonDevice."""
await _async_execute_action(call, ATTR_TEXT_COMMAND)
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the services for the Amazon Devices integration."""
for service_name, method, schema in (
(
SERVICE_SOUND_NOTIFICATION,
async_send_sound_notification,
SCHEMA_SOUND_SERVICE,
),
(
SERVICE_TEXT_COMMAND,
async_send_text_command,
SCHEMA_CUSTOM_COMMAND,
),
):
hass.services.async_register(DOMAIN, service_name, method, schema=schema)

View File

@@ -0,0 +1,504 @@
send_text_command:
fields:
device_id:
required: true
selector:
device:
integration: alexa_devices
text_command:
required: true
example: "Play B.B.C. on TuneIn"
selector:
text:
send_sound:
fields:
device_id:
required: true
selector:
device:
integration: alexa_devices
sound_variant:
required: true
example: 1
default: 1
selector:
number:
min: 1
max: 50
sound:
required: true
example: amzn_sfx_doorbell_chime
default: amzn_sfx_doorbell_chime
selector:
select:
options:
- air_horn
- air_horns
- airboat
- airport
- aliens
- amzn_sfx_airplane_takeoff_whoosh
- amzn_sfx_army_march_clank_7x
- amzn_sfx_army_march_large_8x
- amzn_sfx_army_march_small_8x
- amzn_sfx_baby_big_cry
- amzn_sfx_baby_cry
- amzn_sfx_baby_fuss
- amzn_sfx_battle_group_clanks
- amzn_sfx_battle_man_grunts
- amzn_sfx_battle_men_grunts
- amzn_sfx_battle_men_horses
- amzn_sfx_battle_noisy_clanks
- amzn_sfx_battle_yells_men
- amzn_sfx_battle_yells_men_run
- amzn_sfx_bear_groan_roar
- amzn_sfx_bear_roar_grumble
- amzn_sfx_bear_roar_small
- amzn_sfx_beep_1x
- amzn_sfx_bell_med_chime
- amzn_sfx_bell_short_chime
- amzn_sfx_bell_timer
- amzn_sfx_bicycle_bell_ring
- amzn_sfx_bird_chickadee_chirp_1x
- amzn_sfx_bird_chickadee_chirps
- amzn_sfx_bird_forest
- amzn_sfx_bird_forest_short
- amzn_sfx_bird_robin_chirp_1x
- amzn_sfx_boing_long_1x
- amzn_sfx_boing_med_1x
- amzn_sfx_boing_short_1x
- amzn_sfx_bus_drive_past
- amzn_sfx_buzz_electronic
- amzn_sfx_buzzer_loud_alarm
- amzn_sfx_buzzer_small
- amzn_sfx_car_accelerate
- amzn_sfx_car_accelerate_noisy
- amzn_sfx_car_click_seatbelt
- amzn_sfx_car_close_door_1x
- amzn_sfx_car_drive_past
- amzn_sfx_car_honk_1x
- amzn_sfx_car_honk_2x
- amzn_sfx_car_honk_3x
- amzn_sfx_car_honk_long_1x
- amzn_sfx_car_into_driveway
- amzn_sfx_car_into_driveway_fast
- amzn_sfx_car_slam_door_1x
- amzn_sfx_car_undo_seatbelt
- amzn_sfx_cat_angry_meow_1x
- amzn_sfx_cat_angry_screech_1x
- amzn_sfx_cat_long_meow_1x
- amzn_sfx_cat_meow_1x
- amzn_sfx_cat_purr
- amzn_sfx_cat_purr_meow
- amzn_sfx_chicken_cluck
- amzn_sfx_church_bell_1x
- amzn_sfx_church_bells_ringing
- amzn_sfx_clear_throat_ahem
- amzn_sfx_clock_ticking
- amzn_sfx_clock_ticking_long
- amzn_sfx_copy_machine
- amzn_sfx_cough
- amzn_sfx_crow_caw_1x
- amzn_sfx_crowd_applause
- amzn_sfx_crowd_bar
- amzn_sfx_crowd_bar_rowdy
- amzn_sfx_crowd_boo
- amzn_sfx_crowd_cheer_med
- amzn_sfx_crowd_excited_cheer
- amzn_sfx_dog_med_bark_1x
- amzn_sfx_dog_med_bark_2x
- amzn_sfx_dog_med_bark_growl
- amzn_sfx_dog_med_growl_1x
- amzn_sfx_dog_med_woof_1x
- amzn_sfx_dog_small_bark_2x
- amzn_sfx_door_open
- amzn_sfx_door_shut
- amzn_sfx_doorbell
- amzn_sfx_doorbell_buzz
- amzn_sfx_doorbell_chime
- amzn_sfx_drinking_slurp
- amzn_sfx_drum_and_cymbal
- amzn_sfx_drum_comedy
- amzn_sfx_earthquake_rumble
- amzn_sfx_electric_guitar
- amzn_sfx_electronic_beep
- amzn_sfx_electronic_major_chord
- amzn_sfx_elephant
- amzn_sfx_elevator_bell_1x
- amzn_sfx_elevator_open_bell
- amzn_sfx_fairy_melodic_chimes
- amzn_sfx_fairy_sparkle_chimes
- amzn_sfx_faucet_drip
- amzn_sfx_faucet_running
- amzn_sfx_fireplace_crackle
- amzn_sfx_fireworks
- amzn_sfx_fireworks_firecrackers
- amzn_sfx_fireworks_launch
- amzn_sfx_fireworks_whistles
- amzn_sfx_food_frying
- amzn_sfx_footsteps
- amzn_sfx_footsteps_muffled
- amzn_sfx_ghost_spooky
- amzn_sfx_glass_on_table
- amzn_sfx_glasses_clink
- amzn_sfx_horse_gallop_4x
- amzn_sfx_horse_huff_whinny
- amzn_sfx_horse_neigh
- amzn_sfx_horse_neigh_low
- amzn_sfx_horse_whinny
- amzn_sfx_human_walking
- amzn_sfx_jar_on_table_1x
- amzn_sfx_kitchen_ambience
- amzn_sfx_large_crowd_cheer
- amzn_sfx_large_fire_crackling
- amzn_sfx_laughter
- amzn_sfx_laughter_giggle
- amzn_sfx_lightning_strike
- amzn_sfx_lion_roar
- amzn_sfx_magic_blast_1x
- amzn_sfx_monkey_calls_3x
- amzn_sfx_monkey_chimp
- amzn_sfx_monkeys_chatter
- amzn_sfx_motorcycle_accelerate
- amzn_sfx_motorcycle_engine_idle
- amzn_sfx_motorcycle_engine_rev
- amzn_sfx_musical_drone_intro
- amzn_sfx_oars_splashing_rowboat
- amzn_sfx_object_on_table_2x
- amzn_sfx_ocean_wave_1x
- amzn_sfx_ocean_wave_on_rocks_1x
- amzn_sfx_ocean_wave_surf
- amzn_sfx_people_walking
- amzn_sfx_person_running
- amzn_sfx_piano_note_1x
- amzn_sfx_punch
- amzn_sfx_rain
- amzn_sfx_rain_on_roof
- amzn_sfx_rain_thunder
- amzn_sfx_rat_squeak_2x
- amzn_sfx_rat_squeaks
- amzn_sfx_raven_caw_1x
- amzn_sfx_raven_caw_2x
- amzn_sfx_restaurant_ambience
- amzn_sfx_rooster_crow
- amzn_sfx_scifi_air_escaping
- amzn_sfx_scifi_alarm
- amzn_sfx_scifi_alien_voice
- amzn_sfx_scifi_boots_walking
- amzn_sfx_scifi_close_large_explosion
- amzn_sfx_scifi_door_open
- amzn_sfx_scifi_engines_on
- amzn_sfx_scifi_engines_on_large
- amzn_sfx_scifi_engines_on_short_burst
- amzn_sfx_scifi_explosion
- amzn_sfx_scifi_explosion_2x
- amzn_sfx_scifi_incoming_explosion
- amzn_sfx_scifi_laser_gun_battle
- amzn_sfx_scifi_laser_gun_fires
- amzn_sfx_scifi_laser_gun_fires_large
- amzn_sfx_scifi_long_explosion_1x
- amzn_sfx_scifi_missile
- amzn_sfx_scifi_motor_short_1x
- amzn_sfx_scifi_open_airlock
- amzn_sfx_scifi_radar_high_ping
- amzn_sfx_scifi_radar_low
- amzn_sfx_scifi_radar_medium
- amzn_sfx_scifi_run_away
- amzn_sfx_scifi_sheilds_up
- amzn_sfx_scifi_short_low_explosion
- amzn_sfx_scifi_small_whoosh_flyby
- amzn_sfx_scifi_small_zoom_flyby
- amzn_sfx_scifi_sonar_ping_3x
- amzn_sfx_scifi_sonar_ping_4x
- amzn_sfx_scifi_spaceship_flyby
- amzn_sfx_scifi_timer_beep
- amzn_sfx_scifi_zap_backwards
- amzn_sfx_scifi_zap_electric
- amzn_sfx_sheep_baa
- amzn_sfx_sheep_bleat
- amzn_sfx_silverware_clank
- amzn_sfx_sirens
- amzn_sfx_sleigh_bells
- amzn_sfx_small_stream
- amzn_sfx_sneeze
- amzn_sfx_stream
- amzn_sfx_strong_wind_desert
- amzn_sfx_strong_wind_whistling
- amzn_sfx_subway_leaving
- amzn_sfx_subway_passing
- amzn_sfx_subway_stopping
- amzn_sfx_swoosh_cartoon_fast
- amzn_sfx_swoosh_fast_1x
- amzn_sfx_swoosh_fast_6x
- amzn_sfx_test_tone
- amzn_sfx_thunder_rumble
- amzn_sfx_toilet_flush
- amzn_sfx_trumpet_bugle
- amzn_sfx_turkey_gobbling
- amzn_sfx_typing_medium
- amzn_sfx_typing_short
- amzn_sfx_typing_typewriter
- amzn_sfx_vacuum_off
- amzn_sfx_vacuum_on
- amzn_sfx_walking_in_mud
- amzn_sfx_walking_in_snow
- amzn_sfx_walking_on_grass
- amzn_sfx_water_dripping
- amzn_sfx_water_droplets
- amzn_sfx_wind_strong_gusting
- amzn_sfx_wind_whistling_desert
- amzn_sfx_wings_flap_4x
- amzn_sfx_wings_flap_fast
- amzn_sfx_wolf_howl
- amzn_sfx_wolf_young_howl
- amzn_sfx_wooden_door
- amzn_sfx_wooden_door_creaks_long
- amzn_sfx_wooden_door_creaks_multiple
- amzn_sfx_wooden_door_creaks_open
- amzn_ui_sfx_gameshow_bridge
- amzn_ui_sfx_gameshow_countdown_loop_32s_full
- amzn_ui_sfx_gameshow_countdown_loop_64s_full
- amzn_ui_sfx_gameshow_countdown_loop_64s_minimal
- amzn_ui_sfx_gameshow_intro
- amzn_ui_sfx_gameshow_negative_response
- amzn_ui_sfx_gameshow_neutral_response
- amzn_ui_sfx_gameshow_outro
- amzn_ui_sfx_gameshow_player1
- amzn_ui_sfx_gameshow_player2
- amzn_ui_sfx_gameshow_player3
- amzn_ui_sfx_gameshow_player4
- amzn_ui_sfx_gameshow_positive_response
- amzn_ui_sfx_gameshow_tally_negative
- amzn_ui_sfx_gameshow_tally_positive
- amzn_ui_sfx_gameshow_waiting_loop_30s
- anchor
- answering_machines
- arcs_sparks
- arrows_bows
- baby
- back_up_beeps
- bars_restaurants
- baseball
- basketball
- battles
- beeps_tones
- bell
- bikes
- billiards
- board_games
- body
- boing
- books
- bow_wash
- box
- break_shatter_smash
- breaks
- brooms_mops
- bullets
- buses
- buzz
- buzz_hums
- buzzers
- buzzers_pistols
- cables_metal
- camera
- cannons
- car_alarm
- car_alarms
- car_cell_phones
- carnivals_fairs
- cars
- casino
- casinos
- cellar
- chimes
- chimes_bells
- chorus
- christmas
- church_bells
- clock
- cloth
- concrete
- construction
- construction_factory
- crashes
- crowds
- debris
- dining_kitchens
- dinosaurs
- dripping
- drops
- electric
- electrical
- elevator
- evolution_monsters
- explosions
- factory
- falls
- fax_scanner_copier
- feedback_mics
- fight
- fire
- fire_extinguisher
- fireballs
- fireworks
- fishing_pole
- flags
- football
- footsteps
- futuristic
- futuristic_ship
- gameshow
- gear
- ghosts_demons
- giant_monster
- glass
- glasses_clink
- golf
- gorilla
- grenade_lanucher
- griffen
- gyms_locker_rooms
- handgun_loading
- handgun_shot
- handle
- hands
- heartbeats_ekg
- helicopter
- high_tech
- hit_punch_slap
- hits
- horns
- horror
- hot_tub_filling_up
- human
- human_vocals
- hygene # codespell:ignore
- ice_skating
- ignitions
- infantry
- intro
- jet
- juggling
- key_lock
- kids
- knocks
- lab_equip
- lacrosse
- lamps_lanterns
- leather
- liquid_suction
- locker_doors
- machine_gun
- magic_spells
- medium_large_explosions
- metal
- modern_rings
- money_coins
- motorcycles
- movement
- moves
- nature
- oar_boat
- pagers
- paintball
- paper
- parachute
- pay_phones
- phone_beeps
- pigmy_bats
- pills
- pour_water
- power_up_down
- printers
- prison
- public_space
- racquetball
- radios_static
- rain
- rc_airplane
- rc_car
- refrigerators_freezers
- regular
- respirator
- rifle
- roller_coaster
- rollerskates_rollerblades
- room_tones
- ropes_climbing
- rotary_rings
- rowboat_canoe
- rubber
- running
- sails
- sand_gravel
- screen_doors
- screens
- seats_stools
- servos
- shoes_boots
- shotgun
- shower
- sink_faucet
- sink_filling_water
- sink_run_and_off
- sink_water_splatter
- sirens
- skateboards
- ski
- skids_tires
- sled
- slides
- small_explosions
- snow
- snowmobile
- soldiers
- splash_water
- splashes_sprays
- sports_whistles
- squeaks
- squeaky
- stairs
- steam
- submarine_diesel
- swing_doors
- switches_levers
- swords
- tape
- tape_machine
- televisions_shows
- tennis_pingpong
- textile
- throw
- thunder
- ticks
- timer
- toilet_flush
- tone
- tones_noises
- toys
- tractors
- traffic
- train
- trucks_vans
- turnstiles
- typing
- umbrella
- underwater
- vampires
- various
- video_tunes
- volcano_earthquake
- watches
- water
- water_running
- werewolves
- winches_gears
- wind
- wood
- wood_boat
- woosh
- zap
- zippers
translation_key: sound

View File

@@ -1,23 +1,21 @@
{
"common": {
"data_code": "One-time password (OTP code)",
"data_description_country": "The country where your Amazon account is registered.",
"data_description_username": "The email address of your Amazon account.",
"data_description_password": "The password of your Amazon account.",
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported."
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported.",
"device_id_description": "The ID of the device to send the command to."
},
"config": {
"flow_title": "{username}",
"step": {
"user": {
"data": {
"country": "[%key:common::config_flow::data::country%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"code": "[%key:component::alexa_devices::common::data_code%]"
},
"data_description": {
"country": "[%key:component::alexa_devices::common::data_description_country%]",
"username": "[%key:component::alexa_devices::common::data_description_username%]",
"password": "[%key:component::alexa_devices::common::data_description_password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
@@ -32,6 +30,16 @@
"password": "[%key:component::alexa_devices::common::data_description_password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
}
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"code": "[%key:component::alexa_devices::common::data_code%]"
},
"data_description": {
"password": "[%key:component::alexa_devices::common::data_description_password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
}
}
},
"abort": {
@@ -39,13 +47,13 @@
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"cannot_retrieve_data": "Unable to retrieve data from Amazon. Please try again later.",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"wrong_country": "Wrong country selected. Please select the country where your Amazon account is registered.",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
},
@@ -84,12 +92,532 @@
}
}
},
"services": {
"send_sound": {
"name": "Send sound",
"description": "Sends a sound to a device",
"fields": {
"device_id": {
"name": "Device",
"description": "[%key:component::alexa_devices::common::device_id_description%]"
},
"sound": {
"name": "Alexa Skill sound file",
"description": "The sound file to play."
},
"sound_variant": {
"name": "Sound variant",
"description": "The variant of the sound to play."
}
}
},
"send_text_command": {
"name": "Send text command",
"description": "Sends a text command to a device",
"fields": {
"text_command": {
"name": "Alexa text command",
"description": "The text command to send."
},
"device_id": {
"name": "Device",
"description": "[%key:component::alexa_devices::common::device_id_description%]"
}
}
}
},
"selector": {
"sound": {
"options": {
"air_horn": "Air Horn",
"air_horns": "Air Horns",
"airboat": "Airboat",
"airport": "Airport",
"aliens": "Aliens",
"amzn_sfx_airplane_takeoff_whoosh": "Airplane Takeoff Whoosh",
"amzn_sfx_army_march_clank_7x": "Army March Clank 7x",
"amzn_sfx_army_march_large_8x": "Army March Large 8x",
"amzn_sfx_army_march_small_8x": "Army March Small 8x",
"amzn_sfx_baby_big_cry": "Baby Big Cry",
"amzn_sfx_baby_cry": "Baby Cry",
"amzn_sfx_baby_fuss": "Baby Fuss",
"amzn_sfx_battle_group_clanks": "Battle Group Clanks",
"amzn_sfx_battle_man_grunts": "Battle Man Grunts",
"amzn_sfx_battle_men_grunts": "Battle Men Grunts",
"amzn_sfx_battle_men_horses": "Battle Men Horses",
"amzn_sfx_battle_noisy_clanks": "Battle Noisy Clanks",
"amzn_sfx_battle_yells_men": "Battle Yells Men",
"amzn_sfx_battle_yells_men_run": "Battle Yells Men Run",
"amzn_sfx_bear_groan_roar": "Bear Groan Roar",
"amzn_sfx_bear_roar_grumble": "Bear Roar Grumble",
"amzn_sfx_bear_roar_small": "Bear Roar Small",
"amzn_sfx_beep_1x": "Beep 1x",
"amzn_sfx_bell_med_chime": "Bell Med Chime",
"amzn_sfx_bell_short_chime": "Bell Short Chime",
"amzn_sfx_bell_timer": "Bell Timer",
"amzn_sfx_bicycle_bell_ring": "Bicycle Bell Ring",
"amzn_sfx_bird_chickadee_chirp_1x": "Bird Chickadee Chirp 1x",
"amzn_sfx_bird_chickadee_chirps": "Bird Chickadee Chirps",
"amzn_sfx_bird_forest": "Bird Forest",
"amzn_sfx_bird_forest_short": "Bird Forest Short",
"amzn_sfx_bird_robin_chirp_1x": "Bird Robin Chirp 1x",
"amzn_sfx_boing_long_1x": "Boing Long 1x",
"amzn_sfx_boing_med_1x": "Boing Med 1x",
"amzn_sfx_boing_short_1x": "Boing Short 1x",
"amzn_sfx_bus_drive_past": "Bus Drive Past",
"amzn_sfx_buzz_electronic": "Buzz Electronic",
"amzn_sfx_buzzer_loud_alarm": "Buzzer Loud Alarm",
"amzn_sfx_buzzer_small": "Buzzer Small",
"amzn_sfx_car_accelerate": "Car Accelerate",
"amzn_sfx_car_accelerate_noisy": "Car Accelerate Noisy",
"amzn_sfx_car_click_seatbelt": "Car Click Seatbelt",
"amzn_sfx_car_close_door_1x": "Car Close Door 1x",
"amzn_sfx_car_drive_past": "Car Drive Past",
"amzn_sfx_car_honk_1x": "Car Honk 1x",
"amzn_sfx_car_honk_2x": "Car Honk 2x",
"amzn_sfx_car_honk_3x": "Car Honk 3x",
"amzn_sfx_car_honk_long_1x": "Car Honk Long 1x",
"amzn_sfx_car_into_driveway": "Car Into Driveway",
"amzn_sfx_car_into_driveway_fast": "Car Into Driveway Fast",
"amzn_sfx_car_slam_door_1x": "Car Slam Door 1x",
"amzn_sfx_car_undo_seatbelt": "Car Undo Seatbelt",
"amzn_sfx_cat_angry_meow_1x": "Cat Angry Meow 1x",
"amzn_sfx_cat_angry_screech_1x": "Cat Angry Screech 1x",
"amzn_sfx_cat_long_meow_1x": "Cat Long Meow 1x",
"amzn_sfx_cat_meow_1x": "Cat Meow 1x",
"amzn_sfx_cat_purr": "Cat Purr",
"amzn_sfx_cat_purr_meow": "Cat Purr Meow",
"amzn_sfx_chicken_cluck": "Chicken Cluck",
"amzn_sfx_church_bell_1x": "Church Bell 1x",
"amzn_sfx_church_bells_ringing": "Church Bells Ringing",
"amzn_sfx_clear_throat_ahem": "Clear Throat Ahem",
"amzn_sfx_clock_ticking": "Clock Ticking",
"amzn_sfx_clock_ticking_long": "Clock Ticking Long",
"amzn_sfx_copy_machine": "Copy Machine",
"amzn_sfx_cough": "Cough",
"amzn_sfx_crow_caw_1x": "Crow Caw 1x",
"amzn_sfx_crowd_applause": "Crowd Applause",
"amzn_sfx_crowd_bar": "Crowd Bar",
"amzn_sfx_crowd_bar_rowdy": "Crowd Bar Rowdy",
"amzn_sfx_crowd_boo": "Crowd Boo",
"amzn_sfx_crowd_cheer_med": "Crowd Cheer Med",
"amzn_sfx_crowd_excited_cheer": "Crowd Excited Cheer",
"amzn_sfx_dog_med_bark_1x": "Dog Med Bark 1x",
"amzn_sfx_dog_med_bark_2x": "Dog Med Bark 2x",
"amzn_sfx_dog_med_bark_growl": "Dog Med Bark Growl",
"amzn_sfx_dog_med_growl_1x": "Dog Med Growl 1x",
"amzn_sfx_dog_med_woof_1x": "Dog Med Woof 1x",
"amzn_sfx_dog_small_bark_2x": "Dog Small Bark 2x",
"amzn_sfx_door_open": "Door Open",
"amzn_sfx_door_shut": "Door Shut",
"amzn_sfx_doorbell": "Doorbell",
"amzn_sfx_doorbell_buzz": "Doorbell Buzz",
"amzn_sfx_doorbell_chime": "Doorbell Chime",
"amzn_sfx_drinking_slurp": "Drinking Slurp",
"amzn_sfx_drum_and_cymbal": "Drum And Cymbal",
"amzn_sfx_drum_comedy": "Drum Comedy",
"amzn_sfx_earthquake_rumble": "Earthquake Rumble",
"amzn_sfx_electric_guitar": "Electric Guitar",
"amzn_sfx_electronic_beep": "Electronic Beep",
"amzn_sfx_electronic_major_chord": "Electronic Major Chord",
"amzn_sfx_elephant": "Elephant",
"amzn_sfx_elevator_bell_1x": "Elevator Bell 1x",
"amzn_sfx_elevator_open_bell": "Elevator Open Bell",
"amzn_sfx_fairy_melodic_chimes": "Fairy Melodic Chimes",
"amzn_sfx_fairy_sparkle_chimes": "Fairy Sparkle Chimes",
"amzn_sfx_faucet_drip": "Faucet Drip",
"amzn_sfx_faucet_running": "Faucet Running",
"amzn_sfx_fireplace_crackle": "Fireplace Crackle",
"amzn_sfx_fireworks": "Fireworks",
"amzn_sfx_fireworks_firecrackers": "Fireworks Firecrackers",
"amzn_sfx_fireworks_launch": "Fireworks Launch",
"amzn_sfx_fireworks_whistles": "Fireworks Whistles",
"amzn_sfx_food_frying": "Food Frying",
"amzn_sfx_footsteps": "Footsteps",
"amzn_sfx_footsteps_muffled": "Footsteps Muffled",
"amzn_sfx_ghost_spooky": "Ghost Spooky",
"amzn_sfx_glass_on_table": "Glass On Table",
"amzn_sfx_glasses_clink": "Glasses Clink",
"amzn_sfx_horse_gallop_4x": "Horse Gallop 4x",
"amzn_sfx_horse_huff_whinny": "Horse Huff Whinny",
"amzn_sfx_horse_neigh": "Horse Neigh",
"amzn_sfx_horse_neigh_low": "Horse Neigh Low",
"amzn_sfx_horse_whinny": "Horse Whinny",
"amzn_sfx_human_walking": "Human Walking",
"amzn_sfx_jar_on_table_1x": "Jar On Table 1x",
"amzn_sfx_kitchen_ambience": "Kitchen Ambience",
"amzn_sfx_large_crowd_cheer": "Large Crowd Cheer",
"amzn_sfx_large_fire_crackling": "Large Fire Crackling",
"amzn_sfx_laughter": "Laughter",
"amzn_sfx_laughter_giggle": "Laughter Giggle",
"amzn_sfx_lightning_strike": "Lightning Strike",
"amzn_sfx_lion_roar": "Lion Roar",
"amzn_sfx_magic_blast_1x": "Magic Blast 1x",
"amzn_sfx_monkey_calls_3x": "Monkey Calls 3x",
"amzn_sfx_monkey_chimp": "Monkey Chimp",
"amzn_sfx_monkeys_chatter": "Monkeys Chatter",
"amzn_sfx_motorcycle_accelerate": "Motorcycle Accelerate",
"amzn_sfx_motorcycle_engine_idle": "Motorcycle Engine Idle",
"amzn_sfx_motorcycle_engine_rev": "Motorcycle Engine Rev",
"amzn_sfx_musical_drone_intro": "Musical Drone Intro",
"amzn_sfx_oars_splashing_rowboat": "Oars Splashing Rowboat",
"amzn_sfx_object_on_table_2x": "Object On Table 2x",
"amzn_sfx_ocean_wave_1x": "Ocean Wave 1x",
"amzn_sfx_ocean_wave_on_rocks_1x": "Ocean Wave On Rocks 1x",
"amzn_sfx_ocean_wave_surf": "Ocean Wave Surf",
"amzn_sfx_people_walking": "People Walking",
"amzn_sfx_person_running": "Person Running",
"amzn_sfx_piano_note_1x": "Piano Note 1x",
"amzn_sfx_punch": "Punch",
"amzn_sfx_rain": "Rain",
"amzn_sfx_rain_on_roof": "Rain On Roof",
"amzn_sfx_rain_thunder": "Rain Thunder",
"amzn_sfx_rat_squeak_2x": "Rat Squeak 2x",
"amzn_sfx_rat_squeaks": "Rat Squeaks",
"amzn_sfx_raven_caw_1x": "Raven Caw 1x",
"amzn_sfx_raven_caw_2x": "Raven Caw 2x",
"amzn_sfx_restaurant_ambience": "Restaurant Ambience",
"amzn_sfx_rooster_crow": "Rooster Crow",
"amzn_sfx_scifi_air_escaping": "Scifi Air Escaping",
"amzn_sfx_scifi_alarm": "Scifi Alarm",
"amzn_sfx_scifi_alien_voice": "Scifi Alien Voice",
"amzn_sfx_scifi_boots_walking": "Scifi Boots Walking",
"amzn_sfx_scifi_close_large_explosion": "Scifi Close Large Explosion",
"amzn_sfx_scifi_door_open": "Scifi Door Open",
"amzn_sfx_scifi_engines_on": "Scifi Engines On",
"amzn_sfx_scifi_engines_on_large": "Scifi Engines On Large",
"amzn_sfx_scifi_engines_on_short_burst": "Scifi Engines On Short Burst",
"amzn_sfx_scifi_explosion": "Scifi Explosion",
"amzn_sfx_scifi_explosion_2x": "Scifi Explosion 2x",
"amzn_sfx_scifi_incoming_explosion": "Scifi Incoming Explosion",
"amzn_sfx_scifi_laser_gun_battle": "Scifi Laser Gun Battle",
"amzn_sfx_scifi_laser_gun_fires": "Scifi Laser Gun Fires",
"amzn_sfx_scifi_laser_gun_fires_large": "Scifi Laser Gun Fires Large",
"amzn_sfx_scifi_long_explosion_1x": "Scifi Long Explosion 1x",
"amzn_sfx_scifi_missile": "Scifi Missile",
"amzn_sfx_scifi_motor_short_1x": "Scifi Motor Short 1x",
"amzn_sfx_scifi_open_airlock": "Scifi Open Airlock",
"amzn_sfx_scifi_radar_high_ping": "Scifi Radar High Ping",
"amzn_sfx_scifi_radar_low": "Scifi Radar Low",
"amzn_sfx_scifi_radar_medium": "Scifi Radar Medium",
"amzn_sfx_scifi_run_away": "Scifi Run Away",
"amzn_sfx_scifi_sheilds_up": "Scifi Sheilds Up",
"amzn_sfx_scifi_short_low_explosion": "Scifi Short Low Explosion",
"amzn_sfx_scifi_small_whoosh_flyby": "Scifi Small Whoosh Flyby",
"amzn_sfx_scifi_small_zoom_flyby": "Scifi Small Zoom Flyby",
"amzn_sfx_scifi_sonar_ping_3x": "Scifi Sonar Ping 3x",
"amzn_sfx_scifi_sonar_ping_4x": "Scifi Sonar Ping 4x",
"amzn_sfx_scifi_spaceship_flyby": "Scifi Spaceship Flyby",
"amzn_sfx_scifi_timer_beep": "Scifi Timer Beep",
"amzn_sfx_scifi_zap_backwards": "Scifi Zap Backwards",
"amzn_sfx_scifi_zap_electric": "Scifi Zap Electric",
"amzn_sfx_sheep_baa": "Sheep Baa",
"amzn_sfx_sheep_bleat": "Sheep Bleat",
"amzn_sfx_silverware_clank": "Silverware Clank",
"amzn_sfx_sirens": "Sirens",
"amzn_sfx_sleigh_bells": "Sleigh Bells",
"amzn_sfx_small_stream": "Small Stream",
"amzn_sfx_sneeze": "Sneeze",
"amzn_sfx_stream": "Stream",
"amzn_sfx_strong_wind_desert": "Strong Wind Desert",
"amzn_sfx_strong_wind_whistling": "Strong Wind Whistling",
"amzn_sfx_subway_leaving": "Subway Leaving",
"amzn_sfx_subway_passing": "Subway Passing",
"amzn_sfx_subway_stopping": "Subway Stopping",
"amzn_sfx_swoosh_cartoon_fast": "Swoosh Cartoon Fast",
"amzn_sfx_swoosh_fast_1x": "Swoosh Fast 1x",
"amzn_sfx_swoosh_fast_6x": "Swoosh Fast 6x",
"amzn_sfx_test_tone": "Test Tone",
"amzn_sfx_thunder_rumble": "Thunder Rumble",
"amzn_sfx_toilet_flush": "Toilet Flush",
"amzn_sfx_trumpet_bugle": "Trumpet Bugle",
"amzn_sfx_turkey_gobbling": "Turkey Gobbling",
"amzn_sfx_typing_medium": "Typing Medium",
"amzn_sfx_typing_short": "Typing Short",
"amzn_sfx_typing_typewriter": "Typing Typewriter",
"amzn_sfx_vacuum_off": "Vacuum Off",
"amzn_sfx_vacuum_on": "Vacuum On",
"amzn_sfx_walking_in_mud": "Walking In Mud",
"amzn_sfx_walking_in_snow": "Walking In Snow",
"amzn_sfx_walking_on_grass": "Walking On Grass",
"amzn_sfx_water_dripping": "Water Dripping",
"amzn_sfx_water_droplets": "Water Droplets",
"amzn_sfx_wind_strong_gusting": "Wind Strong Gusting",
"amzn_sfx_wind_whistling_desert": "Wind Whistling Desert",
"amzn_sfx_wings_flap_4x": "Wings Flap 4x",
"amzn_sfx_wings_flap_fast": "Wings Flap Fast",
"amzn_sfx_wolf_howl": "Wolf Howl",
"amzn_sfx_wolf_young_howl": "Wolf Young Howl",
"amzn_sfx_wooden_door": "Wooden Door",
"amzn_sfx_wooden_door_creaks_long": "Wooden Door Creaks Long",
"amzn_sfx_wooden_door_creaks_multiple": "Wooden Door Creaks Multiple",
"amzn_sfx_wooden_door_creaks_open": "Wooden Door Creaks Open",
"amzn_ui_sfx_gameshow_bridge": "Gameshow Bridge",
"amzn_ui_sfx_gameshow_countdown_loop_32s_full": "Gameshow Countdown Loop 32s Full",
"amzn_ui_sfx_gameshow_countdown_loop_64s_full": "Gameshow Countdown Loop 64s Full",
"amzn_ui_sfx_gameshow_countdown_loop_64s_minimal": "Gameshow Countdown Loop 64s Minimal",
"amzn_ui_sfx_gameshow_intro": "Gameshow Intro",
"amzn_ui_sfx_gameshow_negative_response": "Gameshow Negative Response",
"amzn_ui_sfx_gameshow_neutral_response": "Gameshow Neutral Response",
"amzn_ui_sfx_gameshow_outro": "Gameshow Outro",
"amzn_ui_sfx_gameshow_player1": "Gameshow Player1",
"amzn_ui_sfx_gameshow_player2": "Gameshow Player2",
"amzn_ui_sfx_gameshow_player3": "Gameshow Player3",
"amzn_ui_sfx_gameshow_player4": "Gameshow Player4",
"amzn_ui_sfx_gameshow_positive_response": "Gameshow Positive Response",
"amzn_ui_sfx_gameshow_tally_negative": "Gameshow Tally Negative",
"amzn_ui_sfx_gameshow_tally_positive": "Gameshow Tally Positive",
"amzn_ui_sfx_gameshow_waiting_loop_30s": "Gameshow Waiting Loop 30s",
"anchor": "Anchor",
"answering_machines": "Answering Machines",
"arcs_sparks": "Arcs Sparks",
"arrows_bows": "Arrows Bows",
"baby": "Baby",
"back_up_beeps": "Back Up Beeps",
"bars_restaurants": "Bars Restaurants",
"baseball": "Baseball",
"basketball": "Basketball",
"battles": "Battles",
"beeps_tones": "Beeps Tones",
"bell": "Bell",
"bikes": "Bikes",
"billiards": "Billiards",
"board_games": "Board Games",
"body": "Body",
"boing": "Boing",
"books": "Books",
"bow_wash": "Bow Wash",
"box": "Box",
"break_shatter_smash": "Break Shatter Smash",
"breaks": "Breaks",
"brooms_mops": "Brooms Mops",
"bullets": "Bullets",
"buses": "Buses",
"buzz": "Buzz",
"buzz_hums": "Buzz Hums",
"buzzers": "Buzzers",
"buzzers_pistols": "Buzzers Pistols",
"cables_metal": "Cables Metal",
"camera": "Camera",
"cannons": "Cannons",
"car_alarm": "Car Alarm",
"car_alarms": "Car Alarms",
"car_cell_phones": "Car Cell Phones",
"carnivals_fairs": "Carnivals Fairs",
"cars": "Cars",
"casino": "Casino",
"casinos": "Casinos",
"cellar": "Cellar",
"chimes": "Chimes",
"chimes_bells": "Chimes Bells",
"chorus": "Chorus",
"christmas": "Christmas",
"church_bells": "Church Bells",
"clock": "Clock",
"cloth": "Cloth",
"concrete": "Concrete",
"construction": "Construction",
"construction_factory": "Construction Factory",
"crashes": "Crashes",
"crowds": "Crowds",
"debris": "Debris",
"dining_kitchens": "Dining Kitchens",
"dinosaurs": "Dinosaurs",
"dripping": "Dripping",
"drops": "Drops",
"electric": "Electric",
"electrical": "Electrical",
"elevator": "Elevator",
"evolution_monsters": "Evolution Monsters",
"explosions": "Explosions",
"factory": "Factory",
"falls": "Falls",
"fax_scanner_copier": "Fax Scanner Copier",
"feedback_mics": "Feedback Mics",
"fight": "Fight",
"fire": "Fire",
"fire_extinguisher": "Fire Extinguisher",
"fireballs": "Fireballs",
"fireworks": "Fireworks",
"fishing_pole": "Fishing Pole",
"flags": "Flags",
"football": "Football",
"footsteps": "Footsteps",
"futuristic": "Futuristic",
"futuristic_ship": "Futuristic Ship",
"gameshow": "Gameshow",
"gear": "Gear",
"ghosts_demons": "Ghosts Demons",
"giant_monster": "Giant Monster",
"glass": "Glass",
"glasses_clink": "Glasses Clink",
"golf": "Golf",
"gorilla": "Gorilla",
"grenade_lanucher": "Grenade Lanucher",
"griffen": "Griffen",
"gyms_locker_rooms": "Gyms Locker Rooms",
"handgun_loading": "Handgun Loading",
"handgun_shot": "Handgun Shot",
"handle": "Handle",
"hands": "Hands",
"heartbeats_ekg": "Heartbeats EKG",
"helicopter": "Helicopter",
"high_tech": "High Tech",
"hit_punch_slap": "Hit Punch Slap",
"hits": "Hits",
"horns": "Horns",
"horror": "Horror",
"hot_tub_filling_up": "Hot Tub Filling Up",
"human": "Human",
"human_vocals": "Human Vocals",
"hygene": "Hygene",
"ice_skating": "Ice Skating",
"ignitions": "Ignitions",
"infantry": "Infantry",
"intro": "Intro",
"jet": "Jet",
"juggling": "Juggling",
"key_lock": "Key Lock",
"kids": "Kids",
"knocks": "Knocks",
"lab_equip": "Lab Equip",
"lacrosse": "Lacrosse",
"lamps_lanterns": "Lamps Lanterns",
"leather": "Leather",
"liquid_suction": "Liquid Suction",
"locker_doors": "Locker Doors",
"machine_gun": "Machine Gun",
"magic_spells": "Magic Spells",
"medium_large_explosions": "Medium Large Explosions",
"metal": "Metal",
"modern_rings": "Modern Rings",
"money_coins": "Money Coins",
"motorcycles": "Motorcycles",
"movement": "Movement",
"moves": "Moves",
"nature": "Nature",
"oar_boat": "Oar Boat",
"pagers": "Pagers",
"paintball": "Paintball",
"paper": "Paper",
"parachute": "Parachute",
"pay_phones": "Pay Phones",
"phone_beeps": "Phone Beeps",
"pigmy_bats": "Pigmy Bats",
"pills": "Pills",
"pour_water": "Pour Water",
"power_up_down": "Power Up Down",
"printers": "Printers",
"prison": "Prison",
"public_space": "Public Space",
"racquetball": "Racquetball",
"radios_static": "Radios Static",
"rain": "Rain",
"rc_airplane": "RC Airplane",
"rc_car": "RC Car",
"refrigerators_freezers": "Refrigerators Freezers",
"regular": "Regular",
"respirator": "Respirator",
"rifle": "Rifle",
"roller_coaster": "Roller Coaster",
"rollerskates_rollerblades": "RollerSkates RollerBlades",
"room_tones": "Room Tones",
"ropes_climbing": "Ropes Climbing",
"rotary_rings": "Rotary Rings",
"rowboat_canoe": "Rowboat Canoe",
"rubber": "Rubber",
"running": "Running",
"sails": "Sails",
"sand_gravel": "Sand Gravel",
"screen_doors": "Screen Doors",
"screens": "Screens",
"seats_stools": "Seats Stools",
"servos": "Servos",
"shoes_boots": "Shoes Boots",
"shotgun": "Shotgun",
"shower": "Shower",
"sink_faucet": "Sink Faucet",
"sink_filling_water": "Sink Filling Water",
"sink_run_and_off": "Sink Run And Off",
"sink_water_splatter": "Sink Water Splatter",
"sirens": "Sirens",
"skateboards": "Skateboards",
"ski": "Ski",
"skids_tires": "Skids Tires",
"sled": "Sled",
"slides": "Slides",
"small_explosions": "Small Explosions",
"snow": "Snow",
"snowmobile": "Snowmobile",
"soldiers": "Soldiers",
"splash_water": "Splash Water",
"splashes_sprays": "Splashes Sprays",
"sports_whistles": "Sports Whistles",
"squeaks": "Squeaks",
"squeaky": "Squeaky",
"stairs": "Stairs",
"steam": "Steam",
"submarine_diesel": "Submarine Diesel",
"swing_doors": "Swing Doors",
"switches_levers": "Switches Levers",
"swords": "Swords",
"tape": "Tape",
"tape_machine": "Tape Machine",
"televisions_shows": "Televisions Shows",
"tennis_pingpong": "Tennis PingPong",
"textile": "Textile",
"throw": "Throw",
"thunder": "Thunder",
"ticks": "Ticks",
"timer": "Timer",
"toilet_flush": "Toilet Flush",
"tone": "Tone",
"tones_noises": "Tones Noises",
"toys": "Toys",
"tractors": "Tractors",
"traffic": "Traffic",
"train": "Train",
"trucks_vans": "Trucks Vans",
"turnstiles": "Turnstiles",
"typing": "Typing",
"umbrella": "Umbrella",
"underwater": "Underwater",
"vampires": "Vampires",
"various": "Various",
"video_tunes": "Video Tunes",
"volcano_earthquake": "Volcano Earthquake",
"watches": "Watches",
"water": "Water",
"water_running": "Water Running",
"werewolves": "Werewolves",
"winches_gears": "Winches Gears",
"wind": "Wind",
"wood": "Wood",
"wood_boat": "Wood Boat",
"woosh": "Woosh",
"zap": "Zap",
"zippers": "Zippers"
}
}
},
"exceptions": {
"cannot_connect_with_error": {
"message": "Error connecting: {error}"
},
"cannot_retrieve_data_with_error": {
"message": "Error retrieving data: {error}"
},
"device_serial_number_missing": {
"message": "Device serial number missing: {device_id}"
},
"invalid_device_id": {
"message": "Invalid device ID specified: {device_id}"
},
"invalid_sound_value": {
"message": "Invalid sound {sound} with variant {variant} specified"
},
"entry_not_loaded": {
"message": "Entry not loaded: {entry}"
}
}
}

View File

@@ -16,7 +16,7 @@ from homeassistant.helpers.selector import (
SelectSelectorMode,
)
from .const import CONF_SITE_ID, CONF_SITE_NAME, DOMAIN
from .const import CONF_SITE_ID, CONF_SITE_NAME, DOMAIN, REQUEST_TIMEOUT
API_URL = "https://app.amber.com.au/developers"
@@ -64,7 +64,9 @@ class AmberElectricConfigFlow(ConfigFlow, domain=DOMAIN):
api = amberelectric.AmberApi(api_client)
try:
sites: list[Site] = filter_sites(api.get_sites())
sites: list[Site] = filter_sites(
api.get_sites(_request_timeout=REQUEST_TIMEOUT)
)
except amberelectric.ApiException as api_exception:
if api_exception.status == 403:
self._errors[CONF_API_TOKEN] = "invalid_api_token"

View File

@@ -9,7 +9,6 @@ DOMAIN: Final = "amberelectric"
CONF_SITE_NAME = "site_name"
CONF_SITE_ID = "site_id"
ATTR_CONFIG_ENTRY_ID = "config_entry_id"
ATTR_CHANNEL_TYPE = "channel_type"
ATTRIBUTION = "Data provided by Amber Electric"
@@ -22,3 +21,5 @@ SERVICE_GET_FORECASTS = "get_forecasts"
GENERAL_CHANNEL = "general"
CONTROLLED_LOAD_CHANNEL = "controlled_load"
FEED_IN_CHANNEL = "feed_in"
REQUEST_TIMEOUT = 15

View File

@@ -16,7 +16,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import LOGGER
from .const import LOGGER, REQUEST_TIMEOUT
from .helpers import normalize_descriptor
type AmberConfigEntry = ConfigEntry[AmberUpdateCoordinator]
@@ -82,7 +82,11 @@ class AmberUpdateCoordinator(DataUpdateCoordinator):
"grid": {},
}
try:
data = self._api.get_current_prices(self.site_id, next=288)
data = self._api.get_current_prices(
self.site_id,
next=288,
_request_timeout=REQUEST_TIMEOUT,
)
intervals = [interval.actual_instance for interval in data]
except ApiException as api_exception:
raise UpdateFailed("Missing price data, skipping update") from api_exception

View File

@@ -4,6 +4,7 @@ from amberelectric.models.channel import ChannelType
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_CONFIG_ENTRY_ID
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -16,7 +17,6 @@ from homeassistant.util.json import JsonValueType
from .const import (
ATTR_CHANNEL_TYPE,
ATTR_CONFIG_ENTRY_ID,
CONTROLLED_LOAD_CHANNEL,
DOMAIN,
FEED_IN_CHANNEL,

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from aioambient.util import get_public_device_id
from homeassistant.core import callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity, EntityDescription
@@ -37,6 +37,7 @@ class AmbientWeatherEntity(Entity):
identifiers={(DOMAIN, mac_address)},
manufacturer="Ambient Weather",
name=station_name.capitalize(),
connections={(CONNECTION_NETWORK_MAC, mac_address)},
)
self._attr_unique_id = f"{mac_address}_{description.key}"

View File

@@ -390,7 +390,6 @@ def _domains_from_yaml_config(yaml_configuration: dict[str, Any]) -> set[str]:
async def async_devices_payload(hass: HomeAssistant) -> dict:
"""Return the devices payload."""
integrations_without_model_id: set[str] = set()
devices: list[dict[str, Any]] = []
dev_reg = dr.async_get(hass)
# Devices that need via device info set
@@ -400,10 +399,6 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
seen_integrations = set()
for device in dev_reg.devices.values():
# Ignore services
if device.entry_type:
continue
if not device.primary_config_entry:
continue
@@ -414,13 +409,6 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
seen_integrations.add(config_entry.domain)
if not device.model_id:
integrations_without_model_id.add(config_entry.domain)
continue
if not device.manufacturer:
continue
new_indexes[device.id] = len(devices)
devices.append(
{
@@ -430,11 +418,12 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
"model": device.model,
"sw_version": device.sw_version,
"hw_version": device.hw_version,
"has_suggested_area": device.suggested_area is not None,
"has_configuration_url": device.configuration_url is not None,
"via_device": None,
"entry_type": device.entry_type.value if device.entry_type else None,
}
)
if device.via_device_id:
via_devices[device.id] = device.via_device_id
@@ -454,15 +443,12 @@ async def async_devices_payload(hass: HomeAssistant) -> dict:
for device_info in devices:
if integration := integrations.get(device_info["integration"]):
device_info["is_custom_integration"] = not integration.is_built_in
# Include version for custom integrations
if not integration.is_built_in and integration.version:
device_info["custom_integration_version"] = str(integration.version)
return {
"version": "home-assistant:1",
"no_model_id": sorted(
[
domain
for domain in integrations_without_model_id
if domain in integrations and integrations[domain].is_built_in
]
),
"home_assistant": HA_VERSION,
"devices": devices,
}

View File

@@ -30,10 +30,9 @@ class AndroidIPCamDataUpdateCoordinator(DataUpdateCoordinator[None]):
cam: PyDroidIPCam,
) -> None:
"""Initialize the Android IP Webcam."""
self.hass = hass
self.cam = cam
super().__init__(
self.hass,
hass,
_LOGGER,
config_entry=config_entry,
name=f"{DOMAIN} {config_entry.data[CONF_HOST]}",

View File

@@ -81,11 +81,15 @@ async def async_update_options(
async def async_migrate_integration(hass: HomeAssistant) -> None:
"""Migrate integration entry structure."""
entries = hass.config_entries.async_entries(DOMAIN)
# Make sure we get enabled config entries first
entries = sorted(
hass.config_entries.async_entries(DOMAIN),
key=lambda e: e.disabled_by is not None,
)
if not any(entry.version == 1 for entry in entries):
return
api_keys_entries: dict[str, ConfigEntry] = {}
api_keys_entries: dict[str, tuple[ConfigEntry, bool]] = {}
entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass)
@@ -99,30 +103,61 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
)
if entry.data[CONF_API_KEY] not in api_keys_entries:
use_existing = True
api_keys_entries[entry.data[CONF_API_KEY]] = entry
all_disabled = all(
e.disabled_by is not None
for e in entries
if e.data[CONF_API_KEY] == entry.data[CONF_API_KEY]
)
api_keys_entries[entry.data[CONF_API_KEY]] = (entry, all_disabled)
parent_entry = api_keys_entries[entry.data[CONF_API_KEY]]
parent_entry, all_disabled = api_keys_entries[entry.data[CONF_API_KEY]]
hass.config_entries.async_add_subentry(parent_entry, subentry)
conversation_entity = entity_registry.async_get_entity_id(
conversation_entity_id = entity_registry.async_get_entity_id(
"conversation",
DOMAIN,
entry.entry_id,
)
if conversation_entity is not None:
entity_registry.async_update_entity(
conversation_entity,
config_entry_id=parent_entry.entry_id,
config_subentry_id=subentry.subentry_id,
new_unique_id=subentry.subentry_id,
)
device = device_registry.async_get_device(
identifiers={(DOMAIN, entry.entry_id)}
)
if conversation_entity_id is not None:
conversation_entity_entry = entity_registry.entities[conversation_entity_id]
entity_disabled_by = conversation_entity_entry.disabled_by
if (
entity_disabled_by is er.RegistryEntryDisabler.CONFIG_ENTRY
and not all_disabled
):
# Device and entity registries don't update the disabled_by flag
# when moving a device or entity from one config entry to another,
# so we need to do it manually.
entity_disabled_by = (
er.RegistryEntryDisabler.DEVICE
if device
else er.RegistryEntryDisabler.USER
)
entity_registry.async_update_entity(
conversation_entity_id,
config_entry_id=parent_entry.entry_id,
config_subentry_id=subentry.subentry_id,
disabled_by=entity_disabled_by,
new_unique_id=subentry.subentry_id,
)
if device is not None:
# Device and entity registries don't update the disabled_by flag when
# moving a device or entity from one config entry to another, so we
# need to do it manually.
device_disabled_by = device.disabled_by
if (
device.disabled_by is dr.DeviceEntryDisabler.CONFIG_ENTRY
and not all_disabled
):
device_disabled_by = dr.DeviceEntryDisabler.USER
device_registry.async_update_device(
device.id,
disabled_by=device_disabled_by,
new_identifiers={(DOMAIN, subentry.subentry_id)},
add_config_subentry_id=subentry.subentry_id,
add_config_entry_id=parent_entry.entry_id,
@@ -147,7 +182,7 @@ async def async_migrate_integration(hass: HomeAssistant) -> None:
title=DEFAULT_CONVERSATION_NAME,
options={},
version=2,
minor_version=2,
minor_version=3,
)
@@ -173,6 +208,38 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry)
hass.config_entries.async_update_entry(entry, minor_version=2)
if entry.version == 2 and entry.minor_version == 2:
# Fix migration where the disabled_by flag was not set correctly.
# We can currently only correct this for enabled config entries,
# because migration does not run for disabled config entries. This
# is asserted in tests, and if that behavior is changed, we should
# correct also disabled config entries.
device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass)
devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
entity_entries = er.async_entries_for_config_entry(
entity_registry, entry.entry_id
)
if entry.disabled_by is None:
# If the config entry is not disabled, we need to set the disabled_by
# flag on devices to USER, and on entities to DEVICE, if they are set
# to CONFIG_ENTRY.
for device in devices:
if device.disabled_by is not dr.DeviceEntryDisabler.CONFIG_ENTRY:
continue
device_registry.async_update_device(
device.id,
disabled_by=dr.DeviceEntryDisabler.USER,
)
for entity in entity_entries:
if entity.disabled_by is not er.RegistryEntryDisabler.CONFIG_ENTRY:
continue
entity_registry.async_update_entity(
entity.entity_id,
disabled_by=er.RegistryEntryDisabler.DEVICE,
)
hass.config_entries.async_update_entry(entry, minor_version=3)
LOGGER.debug(
"Migration to version %s:%s successful", entry.version, entry.minor_version
)

View File

@@ -75,7 +75,7 @@ class AnthropicConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Anthropic."""
VERSION = 2
MINOR_VERSION = 2
MINOR_VERSION = 3
async def async_step_user(
self, user_input: dict[str, Any] | None = None

View File

@@ -20,10 +20,8 @@ RECOMMENDED_THINKING_BUDGET = 0
MIN_THINKING_BUDGET = 1024
THINKING_MODELS = [
"claude-3-7-sonnet-20250219",
"claude-3-7-sonnet-latest",
"claude-opus-4-20250514",
"claude-opus-4-0",
"claude-sonnet-4-20250514",
"claude-3-7-sonnet",
"claude-sonnet-4-0",
"claude-opus-4-0",
"claude-opus-4-1",
]

View File

@@ -2,11 +2,10 @@
from collections.abc import AsyncGenerator, Callable, Iterable
import json
from typing import Any, cast
from typing import Any
import anthropic
from anthropic import AsyncStream
from anthropic._types import NOT_GIVEN
from anthropic.types import (
InputJSONDelta,
MessageDeltaUsage,
@@ -17,7 +16,6 @@ from anthropic.types import (
RawContentBlockStopEvent,
RawMessageDeltaEvent,
RawMessageStartEvent,
RawMessageStopEvent,
RedactedThinkingBlock,
RedactedThinkingBlockParam,
SignatureDelta,
@@ -35,6 +33,7 @@ from anthropic.types import (
ToolUseBlockParam,
Usage,
)
from anthropic.types.message_create_params import MessageCreateParamsStreaming
from voluptuous_openapi import convert
from homeassistant.components import conversation
@@ -129,6 +128,28 @@ def _convert_content(
)
)
if isinstance(content.native, ThinkingBlock):
messages[-1]["content"].append( # type: ignore[union-attr]
ThinkingBlockParam(
type="thinking",
thinking=content.thinking_content or "",
signature=content.native.signature,
)
)
elif isinstance(content.native, RedactedThinkingBlock):
redacted_thinking_block = RedactedThinkingBlockParam(
type="redacted_thinking",
data=content.native.data,
)
if isinstance(messages[-1]["content"], str):
messages[-1]["content"] = [
TextBlockParam(type="text", text=messages[-1]["content"]),
redacted_thinking_block,
]
else:
messages[-1]["content"].append( # type: ignore[attr-defined]
redacted_thinking_block
)
if content.content:
messages[-1]["content"].append( # type: ignore[union-attr]
TextBlockParam(type="text", text=content.content)
@@ -152,10 +173,9 @@ def _convert_content(
return messages
async def _transform_stream( # noqa: C901 - This is complex, but better to have it in one place
async def _transform_stream(
chat_log: conversation.ChatLog,
result: AsyncStream[MessageStreamEvent],
messages: list[MessageParam],
stream: AsyncStream[MessageStreamEvent],
) -> AsyncGenerator[conversation.AssistantContentDeltaDict]:
"""Transform the response stream into HA format.
@@ -186,31 +206,25 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
Each message could contain multiple blocks of the same type.
"""
if result is None:
if stream is None:
raise TypeError("Expected a stream of messages")
current_message: MessageParam | None = None
current_block: (
TextBlockParam
| ToolUseBlockParam
| ThinkingBlockParam
| RedactedThinkingBlockParam
| None
) = None
current_tool_block: ToolUseBlockParam | None = None
current_tool_args: str
input_usage: Usage | None = None
has_content = False
has_native = False
async for response in result:
async for response in stream:
LOGGER.debug("Received response: %s", response)
if isinstance(response, RawMessageStartEvent):
if response.message.role != "assistant":
raise ValueError("Unexpected message role")
current_message = MessageParam(role=response.message.role, content=[])
input_usage = response.message.usage
elif isinstance(response, RawContentBlockStartEvent):
if isinstance(response.content_block, ToolUseBlock):
current_block = ToolUseBlockParam(
current_tool_block = ToolUseBlockParam(
type="tool_use",
id=response.content_block.id,
name=response.content_block.name,
@@ -218,75 +232,64 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
)
current_tool_args = ""
elif isinstance(response.content_block, TextBlock):
current_block = TextBlockParam(
type="text", text=response.content_block.text
)
yield {"role": "assistant"}
if has_content:
yield {"role": "assistant"}
has_native = False
has_content = True
if response.content_block.text:
yield {"content": response.content_block.text}
elif isinstance(response.content_block, ThinkingBlock):
current_block = ThinkingBlockParam(
type="thinking",
thinking=response.content_block.thinking,
signature=response.content_block.signature,
)
if has_native:
yield {"role": "assistant"}
has_native = False
has_content = False
elif isinstance(response.content_block, RedactedThinkingBlock):
current_block = RedactedThinkingBlockParam(
type="redacted_thinking", data=response.content_block.data
)
LOGGER.debug(
"Some of Claudes internal reasoning has been automatically "
"encrypted for safety reasons. This doesnt affect the quality of "
"responses"
)
if has_native:
yield {"role": "assistant"}
has_native = False
has_content = False
yield {"native": response.content_block}
has_native = True
elif isinstance(response, RawContentBlockDeltaEvent):
if current_block is None:
raise ValueError("Unexpected delta without a block")
if isinstance(response.delta, InputJSONDelta):
current_tool_args += response.delta.partial_json
elif isinstance(response.delta, TextDelta):
text_block = cast(TextBlockParam, current_block)
text_block["text"] += response.delta.text
yield {"content": response.delta.text}
elif isinstance(response.delta, ThinkingDelta):
thinking_block = cast(ThinkingBlockParam, current_block)
thinking_block["thinking"] += response.delta.thinking
yield {"thinking_content": response.delta.thinking}
elif isinstance(response.delta, SignatureDelta):
thinking_block = cast(ThinkingBlockParam, current_block)
thinking_block["signature"] += response.delta.signature
yield {
"native": ThinkingBlock(
type="thinking",
thinking="",
signature=response.delta.signature,
)
}
has_native = True
elif isinstance(response, RawContentBlockStopEvent):
if current_block is None:
raise ValueError("Unexpected stop event without a current block")
if current_block["type"] == "tool_use":
# tool block
if current_tool_block is not None:
tool_args = json.loads(current_tool_args) if current_tool_args else {}
current_block["input"] = tool_args
current_tool_block["input"] = tool_args
yield {
"tool_calls": [
llm.ToolInput(
id=current_block["id"],
tool_name=current_block["name"],
id=current_tool_block["id"],
tool_name=current_tool_block["name"],
tool_args=tool_args,
)
]
}
elif current_block["type"] == "thinking":
# thinking block
LOGGER.debug("Thinking: %s", current_block["thinking"])
if current_message is None:
raise ValueError("Unexpected stop event without a current message")
current_message["content"].append(current_block) # type: ignore[union-attr]
current_block = None
current_tool_block = None
elif isinstance(response, RawMessageDeltaEvent):
if (usage := response.usage) is not None:
chat_log.async_trace(_create_token_stats(input_usage, usage))
if response.delta.stop_reason == "refusal":
raise HomeAssistantError("Potential policy violation detected")
elif isinstance(response, RawMessageStopEvent):
if current_message is not None:
messages.append(current_message)
current_message = None
def _create_token_stats(
@@ -351,45 +354,48 @@ class AnthropicBaseLLMEntity(Entity):
thinking_budget = options.get(CONF_THINKING_BUDGET, RECOMMENDED_THINKING_BUDGET)
model = options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL)
model_args = MessageCreateParamsStreaming(
model=model,
messages=messages,
max_tokens=options.get(CONF_MAX_TOKENS, RECOMMENDED_MAX_TOKENS),
system=system.content,
stream=True,
)
if tools:
model_args["tools"] = tools
if (
model.startswith(tuple(THINKING_MODELS))
and thinking_budget >= MIN_THINKING_BUDGET
):
model_args["thinking"] = ThinkingConfigEnabledParam(
type="enabled", budget_tokens=thinking_budget
)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, RECOMMENDED_TEMPERATURE
)
# To prevent infinite loops, we limit the number of iterations
for _iteration in range(MAX_TOOL_ITERATIONS):
model_args = {
"model": model,
"messages": messages,
"tools": tools or NOT_GIVEN,
"max_tokens": options.get(CONF_MAX_TOKENS, RECOMMENDED_MAX_TOKENS),
"system": system.content,
"stream": True,
}
if model in THINKING_MODELS and thinking_budget >= MIN_THINKING_BUDGET:
model_args["thinking"] = ThinkingConfigEnabledParam(
type="enabled", budget_tokens=thinking_budget
)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, RECOMMENDED_TEMPERATURE
)
try:
stream = await client.messages.create(**model_args)
messages.extend(
_convert_content(
[
content
async for content in chat_log.async_add_delta_content_stream(
self.entity_id,
_transform_stream(chat_log, stream),
)
]
)
)
except anthropic.AnthropicError as err:
raise HomeAssistantError(
f"Sorry, I had a problem talking to Anthropic: {err}"
) from err
messages.extend(
_convert_content(
[
content
async for content in chat_log.async_add_delta_content_stream(
self.entity_id,
_transform_stream(chat_log, stream, messages),
)
if not isinstance(content, conversation.AssistantContent)
]
)
)
if not chat_log.unresponded_tool_results:
break

View File

@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["anthropic==0.52.0"]
"requirements": ["anthropic==0.62.0"]
}

View File

@@ -10,9 +10,9 @@ from homeassistant.components.binary_sensor import (
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
from .entity import APCUPSdEntity
PARALLEL_UPDATES = 0
@@ -40,22 +40,16 @@ async def async_setup_entry(
async_add_entities([OnlineStatus(coordinator, _DESCRIPTION)])
class OnlineStatus(CoordinatorEntity[APCUPSdCoordinator], BinarySensorEntity):
class OnlineStatus(APCUPSdEntity, BinarySensorEntity):
"""Representation of a UPS online status."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: APCUPSdCoordinator,
description: BinarySensorEntityDescription,
) -> None:
"""Initialize the APCUPSd binary device."""
super().__init__(coordinator, context=description.key.upper())
self.entity_description = description
self._attr_unique_id = f"{coordinator.unique_device_id}_{description.key}"
self._attr_device_info = coordinator.device_info
super().__init__(coordinator, description)
@property
def is_on(self) -> bool | None:

View File

@@ -0,0 +1,26 @@
"""Base entity for APCUPSd integration."""
from __future__ import annotations
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .coordinator import APCUPSdCoordinator
class APCUPSdEntity(CoordinatorEntity[APCUPSdCoordinator]):
"""Base entity for APCUPSd integration."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: APCUPSdCoordinator,
description: EntityDescription,
) -> None:
"""Initialize the APCUPSd entity."""
super().__init__(coordinator, context=description.key.upper())
self.entity_description = description
self._attr_unique_id = f"{coordinator.unique_device_id}_{description.key}"
self._attr_device_info = coordinator.device_info

View File

@@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/apcupsd",
"iot_class": "local_polling",
"loggers": ["apcaccess"],
"quality_scale": "bronze",
"requirements": ["aioapcaccess==0.4.2"]
}

View File

@@ -0,0 +1,90 @@
rules:
# Bronze
action-setup: done
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
The integration does not provide any actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: |
Entities of this integration does not explicitly subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: |
The integration does not provide any actions.
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
comment: |
The integration does not provide any additional options.
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: done
reauthentication-flow:
status: exempt
comment: |
The integration does not require authentication.
test-coverage:
status: todo
comment: |
Patch `aioapcaccess.request_status` where we use it.
# Gold
devices: done
diagnostics: done
discovery-update-info:
status: exempt
comment: |
This integration cannot be discovered.
discovery:
status: exempt
comment: |
This integration cannot be discovered.
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices:
status: exempt
comment: |
The integration connects to a single service per configuration entry.
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: done
repair-issues: done
stale-devices:
status: exempt
comment: |
This integration connect to a single service per configuration entry.
# Platinum
async-dependency: done
inject-websession:
status: exempt
comment: |
The integration does not connect via HTTP.
strict-typing: done

View File

@@ -23,10 +23,10 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import LAST_S_TEST
from .coordinator import APCUPSdConfigEntry, APCUPSdCoordinator
from .entity import APCUPSdEntity
PARALLEL_UPDATES = 0
@@ -490,22 +490,16 @@ def infer_unit(value: str) -> tuple[str, str | None]:
return value, None
class APCUPSdSensor(CoordinatorEntity[APCUPSdCoordinator], SensorEntity):
class APCUPSdSensor(APCUPSdEntity, SensorEntity):
"""Representation of a sensor entity for APCUPSd status values."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: APCUPSdCoordinator,
description: SensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator=coordinator, context=description.key.upper())
self.entity_description = description
self._attr_unique_id = f"{coordinator.unique_device_id}_{description.key}"
self._attr_device_info = coordinator.device_info
super().__init__(coordinator, description)
# Initial update of attributes.
self._update_attrs()

View File

@@ -14,7 +14,22 @@
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "The hostname or IP address of the APC UPS Daemon",
"port": "The port the APC UPS Daemon is listening on"
},
"description": "Enter the host and port on which the apcupsd NIS is being served."
},
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "[%key:component::apcupsd::config::step::user::data_description::host%]",
"port": "[%key:component::apcupsd::config::step::user::data_description::port%]"
},
"description": "[%key:component::apcupsd::config::step::user::description%]"
}
}
},

View File

@@ -11,7 +11,7 @@ import time
from typing import Any, Literal, final
from hassil import Intents, recognize
from hassil.expression import Expression, ListReference, Sequence
from hassil.expression import Expression, Group, ListReference
from hassil.intents import WildcardSlotList
from homeassistant.components import conversation, media_source, stt, tts
@@ -413,7 +413,7 @@ class AssistSatelliteEntity(entity.Entity):
for intent in intents.intents.values():
for intent_data in intent.data:
for sentence in intent_data.sentences:
_collect_list_references(sentence, wildcard_names)
_collect_list_references(sentence.expression, wildcard_names)
for wildcard_name in wildcard_names:
intents.slot_lists[wildcard_name] = WildcardSlotList(wildcard_name)
@@ -727,9 +727,9 @@ class AssistSatelliteEntity(entity.Entity):
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
"""Collect list reference names recursively."""
if isinstance(expression, Sequence):
seq: Sequence = expression
for item in seq.items:
if isinstance(expression, Group):
grp: Group = expression
for item in grp.items:
_collect_list_references(item, list_names)
elif isinstance(expression, ListReference):
# {list}

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/assist_satellite",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["hassil==2.2.3"]
"requirements": ["hassil==3.2.0"]
}

View File

@@ -5,15 +5,17 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from collections import namedtuple
from collections.abc import Awaitable, Callable, Coroutine
from datetime import datetime
import functools
import logging
from typing import Any, cast
from aioasuswrt.asuswrt import AsusWrt as AsusWrtLegacy
from aiohttp import ClientSession
from pyasuswrt import AsusWrtError, AsusWrtHttp
from pyasuswrt.exceptions import AsusWrtNotAvailableInfoError
from asusrouter import AsusRouter, AsusRouterError
from asusrouter.modules.client import AsusClient
from asusrouter.modules.data import AsusData
from asusrouter.modules.homeassistant import convert_to_ha_data, convert_to_ha_sensors
from asusrouter.tools.connection import get_cookie_jar
from homeassistant.const import (
CONF_HOST,
@@ -24,7 +26,7 @@ from homeassistant.const import (
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.update_coordinator import UpdateFailed
@@ -41,14 +43,13 @@ from .const import (
PROTOCOL_HTTPS,
PROTOCOL_TELNET,
SENSORS_BYTES,
SENSORS_CPU,
SENSORS_LOAD_AVG,
SENSORS_MEMORY,
SENSORS_RATES,
SENSORS_TEMPERATURES,
SENSORS_TEMPERATURES_LEGACY,
SENSORS_UPTIME,
)
from .helpers import clean_dict, translate_to_legacy
SENSORS_TYPE_BYTES = "sensors_bytes"
SENSORS_TYPE_COUNT = "sensors_count"
@@ -109,7 +110,10 @@ class AsusWrtBridge(ABC):
) -> AsusWrtBridge:
"""Get Bridge instance."""
if conf[CONF_PROTOCOL] in (PROTOCOL_HTTPS, PROTOCOL_HTTP):
session = async_get_clientsession(hass)
session = async_create_clientsession(
hass,
cookie_jar=get_cookie_jar(),
)
return AsusWrtHttpBridge(conf, session)
return AsusWrtLegacyBridge(conf, options)
@@ -310,16 +314,16 @@ class AsusWrtHttpBridge(AsusWrtBridge):
def __init__(self, conf: dict[str, Any], session: ClientSession) -> None:
"""Initialize Bridge that use HTTP library."""
super().__init__(conf[CONF_HOST])
self._api: AsusWrtHttp = self._get_api(conf, session)
self._api = self._get_api(conf, session)
@staticmethod
def _get_api(conf: dict[str, Any], session: ClientSession) -> AsusWrtHttp:
"""Get the AsusWrtHttp API."""
return AsusWrtHttp(
conf[CONF_HOST],
conf[CONF_USERNAME],
conf.get(CONF_PASSWORD, ""),
use_https=conf[CONF_PROTOCOL] == PROTOCOL_HTTPS,
def _get_api(conf: dict[str, Any], session: ClientSession) -> AsusRouter:
"""Get the AsusRouter API."""
return AsusRouter(
hostname=conf[CONF_HOST],
username=conf[CONF_USERNAME],
password=conf.get(CONF_PASSWORD, ""),
use_ssl=conf[CONF_PROTOCOL] == PROTOCOL_HTTPS,
port=conf.get(CONF_PORT),
session=session,
)
@@ -327,46 +331,90 @@ class AsusWrtHttpBridge(AsusWrtBridge):
@property
def is_connected(self) -> bool:
"""Get connected status."""
return cast(bool, self._api.is_connected)
return self._api.connected
async def async_connect(self) -> None:
"""Connect to the device."""
await self._api.async_connect()
# Collect the identity
_identity = await self._api.async_get_identity()
# get main router properties
if mac := self._api.mac:
if mac := _identity.mac:
self._label_mac = format_mac(mac)
self._firmware = self._api.firmware
self._model = self._api.model
self._firmware = str(_identity.firmware)
self._model = _identity.model
async def async_disconnect(self) -> None:
"""Disconnect to the device."""
await self._api.async_disconnect()
async def _get_data(
self,
datatype: AsusData,
force: bool = False,
) -> dict[str, Any]:
"""Get data from the device.
This is a generic method which automatically converts to
the Home Assistant-compatible format.
"""
try:
raw = await self._api.async_get_data(datatype, force=force)
return translate_to_legacy(clean_dict(convert_to_ha_data(raw)))
except AsusRouterError as ex:
raise UpdateFailed(ex) from ex
async def _get_sensors(self, datatype: AsusData) -> list[str]:
"""Get the available sensors.
This is a generic method which automatically converts to
the Home Assistant-compatible format.
"""
sensors = []
try:
data = await self._api.async_get_data(datatype)
# Get the list of sensors from the raw data
# and translate in to the legacy format
sensors = translate_to_legacy(convert_to_ha_sensors(data, datatype))
_LOGGER.debug("Available `%s` sensors: %s", datatype.value, sensors)
except AsusRouterError as ex:
_LOGGER.warning(
"Cannot get available `%s` sensors with exception: %s",
datatype.value,
ex,
)
return sensors
async def async_get_connected_devices(self) -> dict[str, WrtDevice]:
"""Get list of connected devices."""
api_devices = await self._api.async_get_connected_devices()
api_devices: dict[str, AsusClient] = await self._api.async_get_data(
AsusData.CLIENTS, force=True
)
return {
format_mac(mac): WrtDevice(dev.ip, dev.name, dev.node)
format_mac(mac): WrtDevice(
dev.connection.ip_address, dev.description.name, dev.connection.node
)
for mac, dev in api_devices.items()
if dev.connection is not None
and dev.description is not None
and dev.connection.ip_address is not None
}
async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]:
"""Return a dictionary of available sensors for this bridge."""
sensors_cpu = await self._get_available_cpu_sensors()
sensors_temperatures = await self._get_available_temperature_sensors()
sensors_loadavg = await self._get_loadavg_sensors_availability()
return {
SENSORS_TYPE_BYTES: {
KEY_SENSORS: SENSORS_BYTES,
KEY_METHOD: self._get_bytes,
},
SENSORS_TYPE_CPU: {
KEY_SENSORS: sensors_cpu,
KEY_SENSORS: await self._get_sensors(AsusData.CPU),
KEY_METHOD: self._get_cpu_usage,
},
SENSORS_TYPE_LOAD_AVG: {
KEY_SENSORS: sensors_loadavg,
KEY_SENSORS: await self._get_sensors(AsusData.SYSINFO),
KEY_METHOD: self._get_load_avg,
},
SENSORS_TYPE_MEMORY: {
@@ -382,95 +430,44 @@ class AsusWrtHttpBridge(AsusWrtBridge):
KEY_METHOD: self._get_uptime,
},
SENSORS_TYPE_TEMPERATURES: {
KEY_SENSORS: sensors_temperatures,
KEY_SENSORS: await self._get_sensors(AsusData.TEMPERATURE),
KEY_METHOD: self._get_temperatures,
},
}
async def _get_available_cpu_sensors(self) -> list[str]:
"""Check which cpu information is available on the router."""
try:
available_cpu = await self._api.async_get_cpu_usage()
available_sensors = [t for t in SENSORS_CPU if t in available_cpu]
except AsusWrtError as exc:
_LOGGER.warning(
(
"Failed checking cpu sensor availability for ASUS router"
" %s. Exception: %s"
),
self.host,
exc,
)
return []
return available_sensors
async def _get_available_temperature_sensors(self) -> list[str]:
"""Check which temperature information is available on the router."""
try:
available_temps = await self._api.async_get_temperatures()
available_sensors = [
t for t in SENSORS_TEMPERATURES if t in available_temps
]
except AsusWrtError as exc:
_LOGGER.warning(
(
"Failed checking temperature sensor availability for ASUS router"
" %s. Exception: %s"
),
self.host,
exc,
)
return []
return available_sensors
async def _get_loadavg_sensors_availability(self) -> list[str]:
"""Check if load avg is available on the router."""
try:
await self._api.async_get_loadavg()
except AsusWrtNotAvailableInfoError:
return []
except AsusWrtError:
pass
return SENSORS_LOAD_AVG
@handle_errors_and_zip(AsusWrtError, SENSORS_BYTES)
async def _get_bytes(self) -> Any:
"""Fetch byte information from the router."""
return await self._api.async_get_traffic_bytes()
return await self._get_data(AsusData.NETWORK)
@handle_errors_and_zip(AsusWrtError, SENSORS_RATES)
async def _get_rates(self) -> Any:
"""Fetch rates information from the router."""
return await self._api.async_get_traffic_rates()
data = await self._get_data(AsusData.NETWORK)
# Convert from bits/s to Bytes/s for compatibility with legacy sensors
return {
key: (
value / 8
if key in SENSORS_RATES and isinstance(value, (int, float))
else value
)
for key, value in data.items()
}
@handle_errors_and_zip(AsusWrtError, SENSORS_LOAD_AVG)
async def _get_load_avg(self) -> Any:
"""Fetch cpu load avg information from the router."""
return await self._api.async_get_loadavg()
return await self._get_data(AsusData.SYSINFO)
@handle_errors_and_zip(AsusWrtError, None)
async def _get_temperatures(self) -> Any:
"""Fetch temperatures information from the router."""
return await self._api.async_get_temperatures()
return await self._get_data(AsusData.TEMPERATURE)
@handle_errors_and_zip(AsusWrtError, None)
async def _get_cpu_usage(self) -> Any:
"""Fetch cpu information from the router."""
return await self._api.async_get_cpu_usage()
return await self._get_data(AsusData.CPU)
@handle_errors_and_zip(AsusWrtError, None)
async def _get_memory_usage(self) -> Any:
"""Fetch memory information from the router."""
return await self._api.async_get_memory_usage()
return await self._get_data(AsusData.RAM)
async def _get_uptime(self) -> dict[str, Any]:
"""Fetch uptime from the router."""
try:
uptimes = await self._api.async_get_uptime()
except AsusWrtError as exc:
raise UpdateFailed(exc) from exc
last_boot = datetime.fromisoformat(uptimes["last_boot"])
uptime = uptimes["uptime"]
return dict(zip(SENSORS_UPTIME, [last_boot, uptime], strict=False))
return await self._get_data(AsusData.BOOTTIME)

View File

@@ -7,7 +7,7 @@ import os
import socket
from typing import Any, cast
from pyasuswrt import AsusWrtError
from asusrouter import AsusRouterError
import voluptuous as vol
from homeassistant.components.device_tracker import (
@@ -189,7 +189,7 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
try:
await api.async_connect()
except (AsusWrtError, OSError):
except (AsusRouterError, OSError):
_LOGGER.error(
"Error connecting to the AsusWrt router at %s using protocol %s",
host,

View File

@@ -0,0 +1,56 @@
"""Helpers for AsusWRT integration."""
from __future__ import annotations
from typing import Any, TypeVar
T = TypeVar("T", dict[str, Any], list[Any], None)
TRANSLATION_MAP = {
"wan_rx": "sensor_rx_bytes",
"wan_tx": "sensor_tx_bytes",
"total_usage": "cpu_total_usage",
"usage": "mem_usage_perc",
"free": "mem_free",
"used": "mem_used",
"wan_rx_speed": "sensor_rx_rates",
"wan_tx_speed": "sensor_tx_rates",
"2ghz": "2.4GHz",
"5ghz": "5.0GHz",
"5ghz2": "5.0GHz_2",
"6ghz": "6.0GHz",
"cpu": "CPU",
"datetime": "sensor_last_boot",
"uptime": "sensor_uptime",
**{f"{num}_usage": f"cpu{num}_usage" for num in range(1, 9)},
**{f"load_avg_{load}": f"sensor_load_avg{load}" for load in ("1", "5", "15")},
}
def clean_dict(raw: dict[str, Any]) -> dict[str, Any]:
"""Cleans dictionary from None values.
The `state` key is always preserved regardless of its value.
"""
return {k: v for k, v in raw.items() if v is not None or k.endswith("state")}
def translate_to_legacy(raw: T) -> T:
"""Translate raw data to legacy format for dicts and lists."""
if raw is None:
return None
if isinstance(raw, dict):
return {TRANSLATION_MAP.get(k, k): v for k, v in raw.items()}
if isinstance(raw, list):
return [
TRANSLATION_MAP[item]
if isinstance(item, str) and item in TRANSLATION_MAP
else item
for item in raw
]
return raw

View File

@@ -1,11 +1,11 @@
{
"domain": "asuswrt",
"name": "ASUSWRT",
"codeowners": ["@kennedyshead", "@ollo69"],
"codeowners": ["@kennedyshead", "@ollo69", "@Vaskivskyi"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioasuswrt", "asyncssh"],
"requirements": ["aioasuswrt==1.4.0", "pyasuswrt==0.1.21"]
"loggers": ["aioasuswrt", "asusrouter", "asyncssh"],
"requirements": ["aioasuswrt==1.4.0", "asusrouter==1.20.0"]
}

View File

@@ -5,9 +5,9 @@ from __future__ import annotations
from collections.abc import Callable, Mapping
from datetime import datetime, timedelta
import logging
from typing import Any
from typing import TYPE_CHECKING, Any
from pyasuswrt import AsusWrtError
from asusrouter import AsusRouterError
from homeassistant.components.device_tracker import (
CONF_CONSIDER_HOME,
@@ -40,6 +40,9 @@ from .const import (
SENSORS_CONNECTED_DEVICE,
)
if TYPE_CHECKING:
from . import AsusWrtConfigEntry
CONF_REQ_RELOAD = [CONF_DNSMASQ, CONF_INTERFACE, CONF_REQUIRE_IP]
SCAN_INTERVAL = timedelta(seconds=30)
@@ -52,10 +55,13 @@ _LOGGER = logging.getLogger(__name__)
class AsusWrtSensorDataHandler:
"""Data handler for AsusWrt sensor."""
def __init__(self, hass: HomeAssistant, api: AsusWrtBridge) -> None:
def __init__(
self, hass: HomeAssistant, api: AsusWrtBridge, entry: AsusWrtConfigEntry
) -> None:
"""Initialize a AsusWrt sensor data handler."""
self._hass = hass
self._api = api
self._entry = entry
self._connected_devices = 0
async def _get_connected_devices(self) -> dict[str, int]:
@@ -91,6 +97,7 @@ class AsusWrtSensorDataHandler:
update_method=method,
# Polling interval. Will only be polled if there are subscribers.
update_interval=SCAN_INTERVAL if should_poll else None,
config_entry=self._entry,
)
await coordinator.async_refresh()
@@ -222,7 +229,7 @@ class AsusWrtRouter:
"""Set up a AsusWrt router."""
try:
await self._api.async_connect()
except (AsusWrtError, OSError) as exc:
except (AsusRouterError, OSError) as exc:
raise ConfigEntryNotReady from exc
if not self._api.is_connected:
raise ConfigEntryNotReady
@@ -277,7 +284,7 @@ class AsusWrtRouter:
_LOGGER.debug("Checking devices for ASUS router %s", self.host)
try:
wrt_devices = await self._api.async_get_connected_devices()
except (OSError, AsusWrtError) as exc:
except (OSError, AsusRouterError) as exc:
if not self._connect_error:
self._connect_error = True
_LOGGER.error(
@@ -321,7 +328,9 @@ class AsusWrtRouter:
if self._sensors_data_handler:
return
self._sensors_data_handler = AsusWrtSensorDataHandler(self.hass, self._api)
self._sensors_data_handler = AsusWrtSensorDataHandler(
self.hass, self._api, self._entry
)
self._sensors_data_handler.update_device_count(self._connected_devices)
sensors_types = await self._api.async_get_available_sensors()

View File

@@ -6,18 +6,21 @@ from pathlib import Path
from typing import cast
from aiohttp import ClientResponseError
from yalexs.const import Brand
from yalexs.exceptions import AugustApiAIOHTTPError
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
from yalexs.manager.gateway import Config as YaleXSConfig
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, issue_registry as ir
from homeassistant.helpers import (
config_entry_oauth2_flow,
device_registry as dr,
issue_registry as ir,
)
from .const import DOMAIN, PLATFORMS
from .const import DEFAULT_AUGUST_BRAND, DOMAIN, PLATFORMS
from .data import AugustData
from .gateway import AugustGateway
from .util import async_create_august_clientsession
@@ -25,30 +28,21 @@ from .util import async_create_august_clientsession
type AugustConfigEntry = ConfigEntry[AugustData]
@callback
def _async_create_yale_brand_migration_issue(
hass: HomeAssistant, entry: AugustConfigEntry
) -> None:
"""Create an issue for a brand migration."""
ir.async_create_issue(
hass,
DOMAIN,
"yale_brand_migration",
breaks_in_ha_version="2024.9",
learn_more_url="https://www.home-assistant.io/integrations/yale",
translation_key="yale_brand_migration",
is_fixable=False,
severity=ir.IssueSeverity.CRITICAL,
translation_placeholders={
"migrate_url": "https://my.home-assistant.io/redirect/config_flow_start?domain=yale"
},
)
async def async_setup_entry(hass: HomeAssistant, entry: AugustConfigEntry) -> bool:
"""Set up August from a config entry."""
# Check if this is a legacy config entry that needs migration to OAuth
if "auth_implementation" not in entry.data:
# This is a legacy entry using username/password, trigger reauth
raise ConfigEntryAuthFailed("Migration to OAuth required")
session = async_create_august_clientsession(hass)
august_gateway = AugustGateway(Path(hass.config.config_dir), session)
implementation = (
await config_entry_oauth2_flow.async_get_config_entry_implementation(
hass, entry
)
)
oauth_session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
august_gateway = AugustGateway(Path(hass.config.config_dir), session, oauth_session)
try:
await async_setup_august(hass, entry, august_gateway)
except (RequireValidation, InvalidAuth) as err:
@@ -76,9 +70,7 @@ async def async_setup_august(
) -> None:
"""Set up the August component."""
config = cast(YaleXSConfig, entry.data)
await august_gateway.async_setup(config)
if august_gateway.api.brand == Brand.YALE_HOME:
_async_create_yale_brand_migration_issue(hass, entry)
await august_gateway.async_setup({**config, "brand": DEFAULT_AUGUST_BRAND})
await august_gateway.async_authenticate()
await august_gateway.async_refresh_access_token_if_needed()
data = entry.runtime_data = AugustData(hass, august_gateway)

View File

@@ -0,0 +1,15 @@
"""application_credentials platform for the august integration."""
from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant
OAUTH2_AUTHORIZE = "https://auth.august.com/authorization"
OAUTH2_TOKEN = "https://auth.august.com/access_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,284 +1,86 @@
"""Config flow for August integration."""
from collections.abc import Mapping
from dataclasses import dataclass
import logging
from pathlib import Path
from typing import Any
import aiohttp
import voluptuous as vol
from yalexs.authenticator_common import ValidationResult
from yalexs.const import BRANDS_WITHOUT_OAUTH, DEFAULT_BRAND, Brand
from yalexs.manager.exceptions import CannotConnect, InvalidAuth, RequireValidation
import jwt
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from .const import (
CONF_ACCESS_TOKEN_CACHE_FILE,
CONF_BRAND,
CONF_LOGIN_METHOD,
DEFAULT_LOGIN_METHOD,
DOMAIN,
LOGIN_METHODS,
VERIFICATION_CODE_KEY,
)
from .gateway import AugustGateway
from .util import async_create_august_clientsession
# The Yale Home Brand is not supported by the August integration
# anymore and should migrate to the Yale integration
AVAILABLE_BRANDS = BRANDS_WITHOUT_OAUTH.copy()
del AVAILABLE_BRANDS[Brand.YALE_HOME]
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
from homeassistant.helpers import config_entry_oauth2_flow
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_validate_input(
data: dict[str, Any], august_gateway: AugustGateway
) -> dict[str, Any]:
"""Validate the user input allows us to connect.
Data has the keys from DATA_SCHEMA with values provided by the user.
Request configuration steps from the user.
"""
assert august_gateway.authenticator is not None
authenticator = august_gateway.authenticator
if (code := data.get(VERIFICATION_CODE_KEY)) is not None:
result = await authenticator.async_validate_verification_code(code)
_LOGGER.debug("Verification code validation: %s", result)
if result != ValidationResult.VALIDATED:
raise RequireValidation
try:
await august_gateway.async_authenticate()
except RequireValidation:
_LOGGER.debug(
"Requesting new verification code for %s via %s",
data.get(CONF_USERNAME),
data.get(CONF_LOGIN_METHOD),
)
if code is None:
await august_gateway.authenticator.async_send_verification_code()
raise
return {
"title": data.get(CONF_USERNAME),
"data": august_gateway.config_entry(),
}
@dataclass(slots=True)
class ValidateResult:
"""Result from validation."""
validation_required: bool
info: dict[str, Any]
errors: dict[str, str]
description_placeholders: dict[str, str]
class AugustConfigFlow(ConfigFlow, domain=DOMAIN):
class AugustConfigFlow(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
"""Handle a config flow for August."""
VERSION = 1
DOMAIN = DOMAIN
def __init__(self) -> None:
"""Store an AugustGateway()."""
self._august_gateway: AugustGateway | None = None
self._aiohttp_session: aiohttp.ClientSession | None = None
self._user_auth_details: dict[str, Any] = {}
self._needs_reset = True
super().__init__()
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
return await self.async_step_user_validate()
async def async_step_user_validate(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle authentication."""
errors: dict[str, str] = {}
description_placeholders: dict[str, str] = {}
if user_input is not None:
self._user_auth_details.update(user_input)
validate_result = await self._async_auth_or_validate()
description_placeholders = validate_result.description_placeholders
if validate_result.validation_required:
return await self.async_step_validation()
if not (errors := validate_result.errors):
return await self._async_update_or_create_entry(validate_result.info)
return self.async_show_form(
step_id="user_validate",
data_schema=vol.Schema(
{
vol.Required(
CONF_BRAND,
default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND),
): vol.In(AVAILABLE_BRANDS),
vol.Required(
CONF_LOGIN_METHOD,
default=self._user_auth_details.get(
CONF_LOGIN_METHOD, DEFAULT_LOGIN_METHOD
),
): vol.In(LOGIN_METHODS),
vol.Required(
CONF_USERNAME,
default=self._user_auth_details.get(CONF_USERNAME),
): str,
vol.Required(CONF_PASSWORD): str,
}
),
errors=errors,
description_placeholders=description_placeholders,
)
async def async_step_validation(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle validation (2fa) step."""
if user_input:
if self.source == SOURCE_REAUTH:
return await self.async_step_reauth_validate(user_input)
return await self.async_step_user_validate(user_input)
previously_failed = VERIFICATION_CODE_KEY in self._user_auth_details
return self.async_show_form(
step_id="validation",
data_schema=vol.Schema(
{vol.Required(VERIFICATION_CODE_KEY): vol.All(str, vol.Strip)}
),
errors={"base": "invalid_verification_code"} if previously_failed else None,
description_placeholders={
CONF_BRAND: self._user_auth_details[CONF_BRAND],
CONF_USERNAME: self._user_auth_details[CONF_USERNAME],
CONF_LOGIN_METHOD: self._user_auth_details[CONF_LOGIN_METHOD],
},
)
@callback
def _async_get_gateway(self) -> AugustGateway:
"""Set up the gateway."""
if self._august_gateway is not None:
return self._august_gateway
self._aiohttp_session = async_create_august_clientsession(self.hass)
self._august_gateway = AugustGateway(
Path(self.hass.config.config_dir), self._aiohttp_session
)
return self._august_gateway
@callback
def _async_shutdown_gateway(self) -> None:
"""Shutdown the gateway."""
if self._aiohttp_session is not None:
self._aiohttp_session.detach()
self._august_gateway = None
@property
def logger(self) -> logging.Logger:
"""Return logger."""
return _LOGGER
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle configuration by re-auth."""
self._user_auth_details = dict(entry_data)
return await self.async_step_reauth_validate()
return await self.async_step_user()
async def async_step_reauth_validate(
self, user_input: dict[str, Any] | None = None
def _async_decode_jwt(self, encoded: str) -> dict[str, Any]:
"""Decode JWT token."""
return jwt.decode(
encoded,
"",
verify=False,
options={"verify_signature": False},
algorithms=["HS256"],
)
async def _async_handle_reauth(
self, data: dict, decoded: dict[str, Any], user_id: str
) -> ConfigFlowResult:
"""Handle reauth and validation."""
errors: dict[str, str] = {}
description_placeholders: dict[str, str] = {}
if user_input is not None:
self._user_auth_details.update(user_input)
validate_result = await self._async_auth_or_validate()
description_placeholders = validate_result.description_placeholders
if validate_result.validation_required:
return await self.async_step_validation()
if not (errors := validate_result.errors):
return await self._async_update_or_create_entry(validate_result.info)
"""Handle reauth flow."""
reauth_entry = self._get_reauth_entry()
assert reauth_entry.unique_id is not None
# Check if this is a migration from username (contains @) to user ID
if "@" not in reauth_entry.unique_id:
# This is a normal oauth reauth, enforce ID matching for security
await self.async_set_unique_id(user_id)
self._abort_if_unique_id_mismatch(reason="reauth_invalid_user")
return self.async_update_reload_and_abort(reauth_entry, data=data)
return self.async_show_form(
step_id="reauth_validate",
data_schema=vol.Schema(
{
vol.Required(
CONF_BRAND,
default=self._user_auth_details.get(CONF_BRAND, DEFAULT_BRAND),
): vol.In(BRANDS_WITHOUT_OAUTH),
vol.Required(CONF_PASSWORD): str,
}
),
errors=errors,
description_placeholders=description_placeholders
| {
CONF_USERNAME: self._user_auth_details[CONF_USERNAME],
},
# This is a one-time migration from username to user ID
# Only validate if the account has emails
emails: list[str]
if emails := decoded.get("email", []):
# Validate that the email matches before allowing migration
email_to_check_lower = reauth_entry.unique_id.casefold()
if not any(email.casefold() == email_to_check_lower for email in emails):
# Email doesn't match - this is a different account
return self.async_abort(reason="reauth_invalid_user")
# Email matches or no emails on account, update with new unique ID
return self.async_update_reload_and_abort(
reauth_entry, data=data, unique_id=user_id
)
async def _async_reset_access_token_cache_if_needed(
self, gateway: AugustGateway, username: str, access_token_cache_file: str | None
) -> None:
"""Reset the access token cache if needed."""
# We need to configure the access token cache file before we setup the gateway
# since we need to reset it if the brand changes BEFORE we setup the gateway
gateway.async_configure_access_token_cache_file(
username, access_token_cache_file
)
if self._needs_reset:
self._needs_reset = False
await gateway.async_reset_authentication()
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
"""Create an entry for the flow."""
# Decode JWT once
access_token = data["token"]["access_token"]
decoded = self._async_decode_jwt(access_token)
user_id = decoded["userId"]
async def _async_auth_or_validate(self) -> ValidateResult:
"""Authenticate or validate."""
user_auth_details = self._user_auth_details
gateway = self._async_get_gateway()
assert gateway is not None
await self._async_reset_access_token_cache_if_needed(
gateway,
user_auth_details[CONF_USERNAME],
user_auth_details.get(CONF_ACCESS_TOKEN_CACHE_FILE),
)
await gateway.async_setup(user_auth_details)
if self.source == SOURCE_REAUTH:
return await self._async_handle_reauth(data, decoded, user_id)
errors: dict[str, str] = {}
info: dict[str, Any] = {}
description_placeholders: dict[str, str] = {}
validation_required = False
try:
info = await async_validate_input(user_auth_details, gateway)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except RequireValidation:
validation_required = True
except Exception as ex:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unhandled"
description_placeholders = {"error": str(ex)}
return ValidateResult(
validation_required, info, errors, description_placeholders
)
async def _async_update_or_create_entry(
self, info: dict[str, Any]
) -> ConfigFlowResult:
"""Update existing entry or create a new one."""
self._async_shutdown_gateway()
existing_entry = await self.async_set_unique_id(
self._user_auth_details[CONF_USERNAME]
)
if not existing_entry:
return self.async_create_entry(title=info["title"], data=info["data"])
return self.async_update_reload_and_abort(existing_entry, data=info["data"])
await self.async_set_unique_id(user_id)
self._abort_if_unique_id_configured()
return await super().async_oauth_create_entry(data)

View File

@@ -1,5 +1,7 @@
"""Constants for August devices."""
from yalexs.const import Brand
from homeassistant.const import Platform
DEFAULT_TIMEOUT = 25
@@ -9,6 +11,8 @@ CONF_BRAND = "brand"
CONF_LOGIN_METHOD = "login_method"
CONF_INSTALL_ID = "install_id"
DEFAULT_AUGUST_BRAND = Brand.YALE_AUGUST
VERIFICATION_CODE_KEY = "verification_code"
NOTIFICATION_ID = "august_notification"

View File

@@ -1,30 +1,43 @@
"""Handle August connection setup and authentication."""
from typing import Any
import logging
from pathlib import Path
from yalexs.const import DEFAULT_BRAND
from aiohttp import ClientSession
from yalexs.authenticator_common import Authentication, AuthenticationState
from yalexs.manager.gateway import Gateway
from homeassistant.const import CONF_USERNAME
from homeassistant.helpers import config_entry_oauth2_flow
from .const import (
CONF_ACCESS_TOKEN_CACHE_FILE,
CONF_BRAND,
CONF_INSTALL_ID,
CONF_LOGIN_METHOD,
)
_LOGGER = logging.getLogger(__name__)
class AugustGateway(Gateway):
"""Handle the connection to August."""
def config_entry(self) -> dict[str, Any]:
"""Config entry."""
assert self._config is not None
return {
CONF_BRAND: self._config.get(CONF_BRAND, DEFAULT_BRAND),
CONF_LOGIN_METHOD: self._config[CONF_LOGIN_METHOD],
CONF_USERNAME: self._config[CONF_USERNAME],
CONF_INSTALL_ID: self._config.get(CONF_INSTALL_ID),
CONF_ACCESS_TOKEN_CACHE_FILE: self._access_token_cache_file,
}
def __init__(
self,
config_path: Path,
aiohttp_session: ClientSession,
oauth_session: config_entry_oauth2_flow.OAuth2Session,
) -> None:
"""Init the connection."""
super().__init__(config_path, aiohttp_session)
self._oauth_session = oauth_session
async def async_get_access_token(self) -> str:
"""Get access token."""
await self._oauth_session.async_ensure_token_valid()
return self._oauth_session.token["access_token"]
async def async_refresh_access_token_if_needed(self) -> None:
"""Refresh the access token if needed."""
await self._oauth_session.async_ensure_token_valid()
async def async_authenticate(self) -> Authentication:
"""Authenticate with the details provided to setup."""
await self._oauth_session.async_ensure_token_valid()
self.authentication = Authentication(
AuthenticationState.AUTHENTICATED, None, None, None
)
return self.authentication

View File

@@ -3,6 +3,7 @@
"name": "August",
"codeowners": ["@bdraco"],
"config_flow": true,
"dependencies": ["application_credentials", "cloud"],
"dhcp": [
{
"hostname": "connect",
@@ -28,5 +29,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==8.10.0", "yalexs-ble==3.1.0"]
"requirements": ["yalexs==8.12.0", "yalexs-ble==3.1.2"]
}

View File

@@ -6,42 +6,34 @@
}
},
"config": {
"error": {
"unhandled": "Unhandled error: {error}",
"invalid_verification_code": "Invalid verification code",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]",
"data": {
"implementation": "[%key:common::config_flow::data::implementation%]"
},
"data_description": {
"implementation": "[%key:common::config_flow::description::implementation%]"
}
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"missing_credentials": "[%key:common::config_flow::abort::oauth2_missing_credentials%]",
"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%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"reauth_invalid_user": "Reauthenticate must use the same account."
},
"step": {
"validation": {
"title": "Two-factor authentication",
"data": {
"verification_code": "Verification code"
},
"description": "Please check your {login_method} ({username}) and enter the verification code below. Codes may take a few minutes to arrive."
},
"user_validate": {
"description": "It is recommended to use the 'email' login method as some brands may not work with the 'phone' method. If the Login Method is 'email', Username is the email address. If the Login Method is 'phone', Username is the phone number in the format '+NNNNNNNNN'. If you choose the wrong brand, you may be able to authenticate initially; however, you will not be able to operate devices. If you are unsure of the brand, create the integration again and try another brand.",
"data": {
"brand": "Brand",
"login_method": "Login Method",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
},
"title": "Set up an August account"
},
"reauth_validate": {
"description": "Choose the correct brand for your device, and enter the password for {username}. If you choose the wrong brand, you may be able to authenticate initially; however, you will not be able to operate devices. If you are unsure of the brand, create the integration again and try another brand.",
"data": {
"brand": "[%key:component::august::config::step::user_validate::data::brand%]",
"password": "[%key:common::config_flow::data::password%]"
},
"title": "Reauthenticate an August account"
}
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
}
},
"entity": {

View File

@@ -199,23 +199,19 @@ class AuthProvidersView(HomeAssistantView):
)
def _prepare_result_json(
result: AuthFlowResult,
) -> AuthFlowResult:
"""Convert result to JSON."""
def _prepare_result_json(result: AuthFlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
data = result.copy()
data.pop("result")
data.pop("data")
return data
return {
key: val for key, val in result.items() if key not in ("result", "data")
}
if result["type"] != data_entry_flow.FlowResultType.FORM:
return result
return result # type: ignore[return-value]
data = result.copy()
if (schema := data["data_schema"]) is None:
data["data_schema"] = [] # type: ignore[typeddict-item] # json result type
data = dict(result)
if (schema := result["data_schema"]) is None:
data["data_schema"] = []
else:
data["data_schema"] = voluptuous_serialize.convert(schema)
@@ -268,7 +264,7 @@ class LoginFlowBaseView(HomeAssistantView):
result.pop("data")
result.pop("context")
result_obj: Credentials = result.pop("result")
result_obj = result.pop("result")
# Result can be None if credential was never linked to a user before.
user = await hass.auth.async_get_user_by_credentials(result_obj)
@@ -281,7 +277,8 @@ class LoginFlowBaseView(HomeAssistantView):
)
process_success_login(request)
result["result"] = self._store_result(client_id, result_obj)
# We overwrite the Credentials object with the string code to retrieve it.
result["result"] = self._store_result(client_id, result_obj) # type: ignore[typeddict-item]
return self.json(result)

View File

@@ -149,20 +149,16 @@ def websocket_depose_mfa(
hass.async_create_task(async_depose(msg))
def _prepare_result_json(
result: data_entry_flow.FlowResult,
) -> data_entry_flow.FlowResult:
"""Convert result to JSON."""
def _prepare_result_json(result: data_entry_flow.FlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
return result.copy()
return dict(result)
if result["type"] != data_entry_flow.FlowResultType.FORM:
return result
return result # type: ignore[return-value]
data = result.copy()
if (schema := data["data_schema"]) is None:
data["data_schema"] = [] # type: ignore[typeddict-item] # json result type
data = dict(result)
if (schema := result["data_schema"]) is None:
data["data_schema"] = []
else:
data["data_schema"] = voluptuous_serialize.convert(schema)

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
API_ABS_HUMID = "abs_humid"
API_CO2 = "carbon_dioxide"
API_DEW_POINT = "dew_point"
API_DUST = "dust"

View File

@@ -18,6 +18,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_CONNECTIONS,
ATTR_SW_VERSION,
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
@@ -33,6 +34,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
API_ABS_HUMID,
API_CO2,
API_DEW_POINT,
API_DUST,
@@ -120,6 +122,14 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
AwairSensorEntityDescription(
key=API_ABS_HUMID,
device_class=SensorDeviceClass.ABSOLUTE_HUMIDITY,
native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
unique_id_tag="absolute_humidity",
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
)
SENSOR_TYPES_DUST: tuple[AwairSensorEntityDescription, ...] = (

View File

@@ -29,7 +29,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["axis"],
"requirements": ["axis==64"],
"requirements": ["axis==65"],
"ssdp": [
{
"manufacturer": "AXIS"

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/azure_devops",
"iot_class": "cloud_polling",
"loggers": ["aioazuredevops"],
"requirements": ["aioazuredevops==2.2.1"]
"requirements": ["aioazuredevops==2.2.2"]
}

View File

@@ -127,7 +127,6 @@ class BackupConfigData:
schedule=BackupSchedule(
days=days,
recurrence=ScheduleRecurrence(data["schedule"]["recurrence"]),
state=ScheduleState(data["schedule"].get("state", ScheduleState.NEVER)),
time=time,
),
)
@@ -453,7 +452,6 @@ class StoredBackupSchedule(TypedDict):
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState
time: str | None
@@ -462,7 +460,6 @@ class ScheduleParametersDict(TypedDict, total=False):
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState
time: dt.time | None
@@ -486,32 +483,12 @@ class ScheduleRecurrence(StrEnum):
CUSTOM_DAYS = "custom_days"
class ScheduleState(StrEnum):
"""Represent the schedule recurrence.
This is deprecated and can be remove in HA Core 2025.8.
"""
NEVER = "never"
DAILY = "daily"
MONDAY = "mon"
TUESDAY = "tue"
WEDNESDAY = "wed"
THURSDAY = "thu"
FRIDAY = "fri"
SATURDAY = "sat"
SUNDAY = "sun"
@dataclass(kw_only=True)
class BackupSchedule:
"""Represent the backup schedule."""
days: list[Day] = field(default_factory=list)
recurrence: ScheduleRecurrence = ScheduleRecurrence.NEVER
# Although no longer used, state is kept for backwards compatibility.
# It can be removed in HA Core 2025.8.
state: ScheduleState = ScheduleState.NEVER
time: dt.time | None = None
cron_event: CronSim | None = field(init=False, default=None)
next_automatic_backup: datetime | None = field(init=False, default=None)
@@ -610,7 +587,6 @@ class BackupSchedule:
return StoredBackupSchedule(
days=self.days,
recurrence=self.recurrence,
state=self.state,
time=self.time.isoformat() if self.time else None,
)

View File

@@ -1119,7 +1119,7 @@ class BackupManager:
)
if unavailable_agents:
LOGGER.warning(
"Backup agents %s are not available, will backupp to %s",
"Backup agents %s are not available, will backup to %s",
unavailable_agents,
available_agents,
)

View File

@@ -331,9 +331,6 @@ async def handle_config_info(
"""Send the stored backup config."""
manager = hass.data[DATA_MANAGER]
config = manager.config.data.to_dict()
# Remove state from schedule, it's not needed in the frontend
# mypy doesn't like deleting from TypedDict, ignore it
del config["schedule"]["state"] # type: ignore[misc]
connection.send_result(
msg["id"],
{

View File

@@ -93,7 +93,7 @@
}
},
"preset1": {
"name": "Favourite 1",
"name": "Favorite 1",
"state_attributes": {
"event_type": {
"state": {
@@ -107,7 +107,7 @@
}
},
"preset2": {
"name": "Favourite 2",
"name": "Favorite 2",
"state_attributes": {
"event_type": {
"state": {
@@ -121,7 +121,7 @@
}
},
"preset3": {
"name": "Favourite 3",
"name": "Favorite 3",
"state_attributes": {
"event_type": {
"state": {
@@ -135,7 +135,7 @@
}
},
"preset4": {
"name": "Favourite 4",
"name": "Favorite 4",
"state_attributes": {
"event_type": {
"state": {

View File

@@ -25,7 +25,6 @@ SERVICE_TRIGGER = "trigger_camera"
SERVICE_SAVE_VIDEO = "save_video"
SERVICE_SAVE_RECENT_CLIPS = "save_recent_clips"
SERVICE_SEND_PIN = "send_pin"
ATTR_CONFIG_ENTRY_ID = "config_entry_id"
PLATFORMS = [
Platform.ALARM_CONTROL_PANEL,

View File

@@ -5,12 +5,12 @@ from __future__ import annotations
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_PIN
from homeassistant.const import ATTR_CONFIG_ENTRY_ID, CONF_PIN
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
from .const import ATTR_CONFIG_ENTRY_ID, DOMAIN, SERVICE_SEND_PIN
from .const import DOMAIN, SERVICE_SEND_PIN
from .coordinator import BlinkConfigEntry
SERVICE_SEND_PIN_SCHEMA = vol.Schema(

View File

@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/blue_current",
"iot_class": "cloud_push",
"loggers": ["bluecurrent_api"],
"requirements": ["bluecurrent-api==1.2.4"]
"requirements": ["bluecurrent-api==1.3.1"]
}

View File

@@ -6,7 +6,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bluesound",
"iot_class": "local_polling",
"requirements": ["pyblu==2.0.1"],
"requirements": ["pyblu==2.0.4"],
"zeroconf": [
{
"type": "_musc._tcp.local."

View File

@@ -388,12 +388,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE
scanner = HaScanner(mode, adapter, address)
scanner.async_setup()
try:
await scanner.async_start()
except (RuntimeError, ScannerStartError) as err:
raise ConfigEntryNotReady(
f"{adapter_human_name(adapter, address)}: {err}"
) from err
adapters = await manager.async_get_bluetooth_adapters()
details = adapters[adapter]
if entry.title == address:
@@ -401,8 +395,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry, title=adapter_title(adapter, details)
)
slots: int = details.get(ADAPTER_CONNECTION_SLOTS) or DEFAULT_CONNECTION_SLOTS
# Register the scanner before starting so
# any raw advertisement data can be processed
entry.async_on_unload(async_register_scanner(hass, scanner, connection_slots=slots))
await async_update_device(hass, entry, adapter, details)
try:
await scanner.async_start()
except (RuntimeError, ScannerStartError) as err:
raise ConfigEntryNotReady(
f"{adapter_human_name(adapter, address)}: {err}"
) from err
entry.async_on_unload(entry.add_update_listener(async_update_listener))
entry.async_on_unload(scanner.async_stop)
return True

View File

@@ -235,10 +235,9 @@ class HomeAssistantBluetoothManager(BluetoothManager):
def _async_save_scanner_history(self, scanner: BaseHaScanner) -> None:
"""Save the scanner history."""
if isinstance(scanner, BaseHaRemoteScanner):
self.storage.async_set_advertisement_history(
scanner.source, scanner.serialize_discovered_devices()
)
self.storage.async_set_advertisement_history(
scanner.source, scanner.serialize_discovered_devices()
)
def _async_unregister_scanner(
self, scanner: BaseHaScanner, unregister: CALLBACK_TYPE
@@ -285,9 +284,8 @@ class HomeAssistantBluetoothManager(BluetoothManager):
connection_slots: int | None = None,
) -> CALLBACK_TYPE:
"""Register a scanner."""
if isinstance(scanner, BaseHaRemoteScanner):
if history := self.storage.async_get_advertisement_history(scanner.source):
scanner.restore_discovered_devices(history)
if history := self.storage.async_get_advertisement_history(scanner.source):
scanner.restore_discovered_devices(history)
unregister = super().async_register_scanner(scanner, connection_slots)
return partial(self._async_unregister_scanner, scanner, unregister)

View File

@@ -16,11 +16,11 @@
"quality_scale": "internal",
"requirements": [
"bleak==1.0.1",
"bleak-retry-connector==4.0.0",
"bleak-retry-connector==4.3.0",
"bluetooth-adapters==2.0.0",
"bluetooth-auto-recovery==1.5.2",
"bluetooth-data-tools==1.28.2",
"dbus-fast==2.44.2",
"habluetooth==4.0.1"
"dbus-fast==2.44.3",
"habluetooth==5.1.0"
]
}

View File

@@ -39,7 +39,13 @@ def async_setup(hass: HomeAssistant) -> None:
def serialize_service_info(
service_info: BluetoothServiceInfoBleak, time_diff: float
) -> dict[str, Any]:
"""Serialize a BluetoothServiceInfoBleak object."""
"""Serialize a BluetoothServiceInfoBleak object.
The raw field is included for:
1. Debugging - to see the actual advertisement packet
2. Data freshness - manufacturer_data and service_data are aggregated
across multiple advertisements, raw shows the latest packet only
"""
return {
"name": service_info.name,
"address": service_info.address,
@@ -57,6 +63,7 @@ def serialize_service_info(
"connectable": service_info.connectable,
"time": service_info.time + time_diff,
"tx_power": service_info.tx_power,
"raw": service_info.raw.hex() if service_info.raw else None,
}

View File

@@ -6,4 +6,3 @@ CONF_INSTALLER_CODE = "installer_code"
CONF_USER_CODE = "user_code"
ATTR_DATETIME = "datetime"
SERVICE_SET_DATE_TIME = "set_date_time"
ATTR_CONFIG_ENTRY_ID = "config_entry_id"

View File

@@ -9,12 +9,13 @@ from typing import Any
import voluptuous as vol
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_CONFIG_ENTRY_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.util import dt as dt_util
from .const import ATTR_CONFIG_ENTRY_ID, ATTR_DATETIME, DOMAIN, SERVICE_SET_DATE_TIME
from .const import ATTR_DATETIME, DOMAIN, SERVICE_SET_DATE_TIME
from .types import BoschAlarmConfigEntry

View File

@@ -95,7 +95,7 @@
"name": "Battery missing"
},
"panel_fault_ac_fail": {
"name": "AC Failure"
"name": "AC failure"
},
"panel_fault_parameter_crc_fail_in_pif": {
"name": "CRC failure in panel configuration"

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