Compare commits

...

1185 Commits

Author SHA1 Message Date
Paul Bottein
c9537f7357 Merge branch 'dev' into state_attr_translated 2026-03-11 17:49:04 +01:00
Josef Zweck
2eb65ab314 Buffer backup upload progress events (#165249)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-11 17:29:35 +01:00
ams2990
402a37b435 Change light.toggle service call to invoke LightEntity.async_toggle (#156196)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-03-11 17:17:10 +01:00
Erik Montnemery
aa66e8ef0c Improve humidity triggers (#165323) 2026-03-11 17:11:27 +01:00
noambav
f1a1e284b7 Add support for Fish Audio s2-pro model (#165269) 2026-03-11 17:07:56 +01:00
hanwg
08594f4e0c Update migration message for Telegram bot (#165299) 2026-03-11 17:04:16 +01:00
Joakim Plate
8d810588f8 Move secondary zone of arcam to sub-device (#165336) 2026-03-11 16:57:47 +01:00
Sid
70faad15d5 Add binary_sensor to eheimdigital (#165035)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-11 16:21:16 +01:00
TheJulianJES
d447843687 Bump python-otbr-api to 2.9.0 (#165298) 2026-03-11 16:15:35 +01:00
Steve Easley
83b64e29fa Bump pyjvcprojector to 2.0.3 (#165327) 2026-03-11 16:13:26 +01:00
tronikos
4558a10e05 Improve test coverage in Opower to make it silver (#165124) 2026-03-11 15:56:31 +01:00
johanzander
5ad9e81082 Add reauthentication flow to growatt_server (silver quality scale) (#164993)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 15:51:25 +01:00
cdheiser
ba00a14772 Fix flakiness in lutron tests and isolate platforms per test file (#165328) 2026-03-11 15:08:00 +01:00
J. Diego Rodríguez Royo
49f4d07eeb Add fan entity for air conditioner to Home Connect (#155983)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-11 14:29:01 +01:00
Dan Raper
5d271a0d30 Bump ohme to 1.7.0 (#165318) 2026-03-11 12:49:07 +01:00
Joakim Plate
474b683d3c Update gardena to 2.1.0 (#165322) 2026-03-11 12:48:24 +01:00
Erik Montnemery
d37106a360 Add gate triggers (#165228) 2026-03-11 10:59:53 +01:00
Paul Bottein
57a33dd34d Add state_attr_translated template filter and function 2026-03-11 10:24:28 +01:00
epenet
e115c90719 Reduce internal testing in arcam_fmj tests (#165315) 2026-03-11 10:14:24 +01:00
epenet
6ad3adf0c3 Remove duplicate fixture in arcam_fmj tests (#165312) 2026-03-11 09:51:51 +01:00
dependabot[bot]
2a8d59be4c Bump docker/login-action from 3.7.0 to 4.0.0 (#165302) 2026-03-11 09:16:34 +01:00
dependabot[bot]
6e6e35bc3b Bump actions/dependency-review-action from 4.8.3 to 4.9.0 (#165304) 2026-03-11 09:15:36 +01:00
epenet
795b4c8414 Fix incorrect type annotations in tests (#165305) 2026-03-11 08:38:58 +01:00
Luke Lashley
16389dc18e Bump python-roborock to 4.20.0 (#165292) 2026-03-11 08:21:28 +01:00
Erik Montnemery
e7a1c8d001 Remove triggers binary_sensor.occupancy_cleared and occupancy_detected (#165181) 2026-03-11 07:37:40 +01:00
Luke Lashley
4efb10dae1 Remove an extra roborock trait from updating (#165297) 2026-03-11 02:31:10 +01:00
Erik Montnemery
f163576e78 Fail more tests when pytest_socket.SocketBlockedError is raised (#155398)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-11 00:00:00 +01:00
Erik Montnemery
cad8f97e97 Prevent network access in telegram_bot tests (#165284) 2026-03-10 21:53:35 +01:00
Jeef
4ae6099d84 Add local/cloud option to Intellifire (#162739)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-10 21:11:57 +01:00
epenet
60dc88fa15 Move NUT coordinator to separate module (#164848)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 09:13:00 -10:00
Josh Gustafson
2d2c6d676d Address Arcam FMJ post-merge feedback (#165277)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:09:54 +01:00
Matthias Alphart
f3879335ab KNX: add config for unit_of_measurement for yaml sensor entities (#165082) 2026-03-10 19:27:59 +01:00
Matthias Alphart
11bc00038e KNX: add config for device_class and unit_of_measurement for yaml number entities (#165083) 2026-03-10 19:27:48 +01:00
David Bonnes
6845e8b880 Extend RESET_SYSTEM action to all Evohome controller types (#164459) 2026-03-10 19:27:35 +01:00
cdheiser
5741016931 Bump pylutron version to 0.3.0 (#164707)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-10 18:10:28 +01:00
WardZhou
6cbc4e7f62 Add support for Thread Integration to Display Icons for Aeotec SmartThings TBRs (#165275) 2026-03-10 18:07:50 +01:00
Joost Lekkerkerker
4064df0114 Create reset HEPA filter button for main component in SmartThings (#165262) 2026-03-10 18:00:55 +01:00
Troels Schwarz-Linnet
789f850691 Implement 2 new sensors in pyvicare (#164523) 2026-03-10 17:59:36 +01:00
Abílio Costa
efca71852b Implement exception-translations for whirlpool integration (#165017) 2026-03-10 17:56:59 +01:00
A. Gideonse
1967e9f309 Add reconfiguration flow to Indevolt integration (#165132) 2026-03-10 17:43:19 +01:00
Artur Pragacz
6ac0c163aa Improve group entities (#160860) 2026-03-10 17:34:52 +01:00
Norbert Rittel
bbe20fd698 Improve descriptions of bond actions (#164744) 2026-03-10 17:08:23 +01:00
hanwg
f576743340 Fix proxy settings not applied for Telegram bot (#165240) 2026-03-10 16:42:46 +01:00
John O'Nolan
3b4a1fba5f Update Ghost integration quality scale to gold (#165215) 2026-03-10 16:25:15 +01:00
Artur Pragacz
1677a9bfa6 Add clean area intent for vacuum (#165182) 2026-03-10 16:24:18 +01:00
Jordan Harvey
0d9c458705 Anglian Water: Add last meter reading processed sensor (#159144)
Co-authored-by: Ariel Ebersberger <31776703+justanotherariel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-03-10 16:18:11 +01:00
epenet
57026a862d Ensure actions have name and description translations (#158243)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-10 16:06:51 +01:00
Josh Gustafson
fd05be4c52 Refactor Arcam FMJ to use coordinator pattern (#165232)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:37:09 +01:00
Dave Love
b1f038849e Add Midea Smart Inverter Window AC to Matter Fan Only mode list (#165170) 2026-03-10 15:28:09 +01:00
Ariel Ebersberger
b46c9ccc65 Influxdb: Add reconfigure flow (#165186) 2026-03-10 15:06:31 +01:00
epenet
80601426cf Move spotify coordinator to separate module (#164927) 2026-03-10 15:01:04 +01:00
Michael
9519bd2428 Add turned off and turned on triggers to input boolean (#158824)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-03-10 14:26:15 +01:00
Manu
be0b7f06a8 Bump pyrate-limiter to 4.0.2, PSNAWP to 3.0.3, python-roborock to 4.17.2 (#164133)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-03-10 13:54:37 +01:00
Joost Lekkerkerker
d30c6de168 Add another air purifier fixture to SmartThings (#165261) 2026-03-10 12:30:12 +01:00
Sab44
0fa666518e Dynamically add new devices to Libre Hardware Monitor (#165250) 2026-03-10 09:19:50 +01:00
Josef Zweck
cf454a1fa3 Bump onedrive-personal-sdk to 0.1.6 (#165219) 2026-03-10 09:13:07 +01:00
Panda-NZ
a36733c4dc Add ambient temperature range controls to ToGrill integration (#165235) 2026-03-09 23:40:30 +01:00
Bram Kragten
bf846e0756 Validate reorder is only used when multiple is true (#165216) 2026-03-09 22:32:02 +01:00
Erik Montnemery
c037dad093 Add humidity triggers (#165197) 2026-03-09 20:34:26 +01:00
Erik Montnemery
ce11e66e1f Add cover triggers (#165188)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-09 19:37:36 +01:00
David Bishop
f38ca7b04a Add unique_id to Whisker (Litter-Robot) config entries (#164766)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-09 19:35:34 +01:00
Tor André Roland
01200ef0a8 Optimizations to Adax local device control (#162109)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-09 19:29:43 +01:00
mettolen
c5e0c78cbc Minor Saunum integration improvements (#164705) 2026-03-09 19:22:27 +01:00
g4bri3lDev
7681caa936 Add diagnostics to OpenDisplay integration (#165222) 2026-03-09 19:05:52 +01:00
Bram Kragten
230a2ff045 Add reorder support to area selector (#165211) 2026-03-09 17:40:34 +01:00
A. Gideonse
9d828502a3 Fix code owner for indevolt integration (#165214) 2026-03-09 17:40:00 +01:00
Samuel Xiao
28088a7e1a Switchbot Cloud: Compatible with new device types (#165191) 2026-03-09 17:12:39 +01:00
epenet
9e8171fb77 Improve test coverage in Tuya light (#164954) 2026-03-09 17:11:26 +01:00
John O'Nolan
1660d3b28a Add stale device removal to Ghost integration (#165134) 2026-03-09 17:10:13 +01:00
Josef Zweck
2ef81a54a5 Allow backups to report the upload progress (#163608)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-09 17:12:49 +02:00
Samuel Xiao
ce6154839e Switchbot Cloud: Fixed light mode settings error (#164723) 2026-03-09 15:50:02 +01:00
Erik Montnemery
a25300b8e1 Fix import in cover (#165199) 2026-03-09 15:27:12 +01:00
Leon Grave
6fa8e71b21 Add freshr integration, based on pyfreshr (#164538)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-09 15:26:03 +01:00
tronikos
c983978a10 Remove type: ignore in Android TV Remote (#165126) 2026-03-09 14:42:51 +01:00
Joost Lekkerkerker
68b8b6b675 Add fixture for Air Purifier to SmartThings (#165187) 2026-03-09 14:21:34 +01:00
Martin Hjelmare
ee4d313b10 Fix update tests for Python 3.14.3 (#165196) 2026-03-09 14:21:18 +01:00
Erik Montnemery
5e665093c9 Revert "Add number.changed trigger" (#165193) 2026-03-09 13:55:08 +01:00
A. Gideonse
9a5f509ab9 Fix missing Gen-2 sensor for the Indevolt integration (#165133) 2026-03-09 13:49:54 +01:00
Erik Montnemery
8d0cd5edaa Remove some climate and humidifier triggers (#165192) 2026-03-09 13:37:31 +01:00
epenet
71726272f5 Speed up SmartThings tests (#165184)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-09 13:25:14 +01:00
epenet
9c6c27ab56 Avoid duplicate id/label in smartthings device fixtures (#165190) 2026-03-09 12:40:11 +01:00
Joost Lekkerkerker
db20cf8161 Rename SmartThings devices to maintain uniqueness (#165189) 2026-03-09 12:16:07 +01:00
John O'Nolan
59b6270157 Add reconfigure flow to Ghost integration (#165131) 2026-03-09 11:57:40 +01:00
epenet
a65ba01bbe Mark climate type hints as mandatory (#164982)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-03-09 11:50:42 +01:00
Erik Montnemery
a5d0350560 Add garage_door triggers (#165144)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-09 11:42:09 +01:00
Shai Ungar
368993556f Bump pyseventeentrack to 1.1.2 (#165089)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 10:38:48 +01:00
Daniel Shneyder
23ea17eaef Bump kaiterra-async-client to 1.1.0 (#165166) 2026-03-09 09:59:55 +01:00
g4bri3lDev
6ace93e45b Bump py-opendisplay to 5.5.0 (#165138)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-09 09:29:57 +01:00
epenet
237a0ae03f Improve type hints in ecobee climate (#165178) 2026-03-09 09:16:43 +01:00
epenet
6067be6f49 Improve type hints in lightwave climate (#165179) 2026-03-09 09:16:29 +01:00
J. Nick Koston
a35c3d5de5 Bump yalexs-ble to 3.3.0 (#165168) 2026-03-08 16:39:30 -10:00
J. Nick Koston
e9c3634cb6 Bump habluetooth to 5.9.1 and bleak-retry-connector to 4.6.0 (#165022) 2026-03-08 16:16:53 -10:00
J. Nick Koston
2ba4544180 Bump yalexs-ble to 3.2.8 (#165018) 2026-03-09 03:07:49 +01:00
Artur Pragacz
5235ce7ae4 Lower ssdp discovery timeout log severity in Onkyo (#165156) 2026-03-09 02:19:42 +01:00
Oscar
56b601e577 Add basic auth support to remote_calendar (#158075) 2026-03-08 16:52:58 -07:00
Justin Boyd
f01a0586cb Bump airtouch5py to 0.4.0 (#161640)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-03-08 21:47:06 +01:00
Erwin Douna
ca641a097b Fix forced VERIFY_SSL in Portainer (#165079) 2026-03-08 13:19:45 +01:00
Åke Strandberg
df2f9d9ef8 Add missing code for Miele dryer (#165122) 2026-03-08 13:18:54 +01:00
Bouwe Westerdijk
501301f4e0 Bump plugwise to v1.11.3 (#165053) 2026-03-08 13:15:44 +01:00
Joakim Plate
89231a1a29 Update pychromecast to 14.0.10 (#165069) 2026-03-08 13:14:34 +01:00
John O'Nolan
fe11a6d38f Add diagnostics to Ghost integration (#165130) 2026-03-08 13:03:57 +01:00
Artur Pragacz
3154c3c962 Make restore state resilient to extra_restore_state_data errors (#165086) 2026-03-08 10:39:53 +01:00
mettolen
5031323dea Add description strings to Huum integration (#165094) 2026-03-08 10:24:15 +01:00
Henning Kerstan
017a9e6938 Bump enocean-async to 0.4.2 (#165084) 2026-03-08 09:02:51 +00:00
tronikos
9e974ab30e Add diagnostics in Opower (#165113) 2026-03-08 09:14:15 +01:00
Norbert Rittel
30c0d6792a Make spelling of "auto-empty dock" consistent in roborock (#165117) 2026-03-08 09:12:56 +01:00
Erwin Douna
9ffb9aa824 Bump pyportainer to 1.0.33 (#165080) 2026-03-08 08:33:33 +01:00
A. Gideonse
9ad71711da Add diagnostics to Indevolt integration (#165096) 2026-03-08 08:32:18 +01:00
Steve Easley
ef83165159 Bump jvc_projector dependency to 2.0.2 (#165099) 2026-03-08 08:29:53 +01:00
Jordan Harvey
f0108c1175 Bump pyanglianwater to 3.1.1 (#165097) 2026-03-08 08:28:06 +01:00
Richard Kroegel
802aa991a9 Remove broken BMW & Mini integrations (#165075) 2026-03-08 00:00:03 +00:00
Sab44
f055c6c7fd Add quality scale exemptions for discovery in Libre Hardware Monitor (#165085) 2026-03-07 23:29:07 +01:00
Joel Hawksley
2a8b045f43 Update weatherkit to fetch hourly data for 7 days (#164494) 2026-03-07 19:08:13 +00:00
Erik Montnemery
281f439bc9 Add trigger door.closed (#165057) 2026-03-07 13:18:46 +00:00
Erik Montnemery
71b420b433 Add trigger door.opened (#164728) 2026-03-07 12:59:09 +01:00
J. Nick Koston
2f02d0f0dc Bump bleak-esphome to 3.7.1 (#165025) 2026-03-07 11:27:59 +00:00
Allen Porter
37cb3cbd50 Bump pyrainbird to 6.1.1 (#165030) 2026-03-07 11:27:28 +00:00
AlCalzone
beec21c4a9 Fix cover state updates for legacy Multilevel Switch based Z-Wave covers (#165003) 2026-03-07 12:16:30 +01:00
Pete Sage
642f603ea2 Add binary_sensors for Rehlko load shedding (#164984) 2026-03-07 11:59:44 +01:00
Abílio Costa
a3d8d76678 Simplify AGENTS.md (#164894) 2026-03-07 06:27:44 +01:00
J. Nick Koston
c25feaa62b Bump aioesphomeapi to 44.3.1 (#165023) 2026-03-06 19:02:18 -10:00
Glenn Waters
50bde6fccd Hunter Douglas Powerview: Fix missing class in hierarchy. (#164264)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-06 21:16:38 +01:00
Karl Beecken
1b7398c271 Bump teltasync to 0.2.0 (#164995) 2026-03-06 21:16:19 +01:00
Sid
7e4b8e802e Add support for the reeflexUV+e to eheimdigital (#163656) 2026-03-06 20:28:39 +01:00
Joost Lekkerkerker
4bcea27151 Bump spotifyaio to 2.0.2 (#164114)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-03-06 20:28:04 +01:00
konsulten
ffca43027f Add reconfigure flow for systemnexa2 (#164361) 2026-03-06 20:23:17 +01:00
Joshua Leaper
01e94ca5b2 Update ness_alarm scan interval to 5 secs (#164835) 2026-03-06 20:12:35 +01:00
Petro31
b8ea6b4162 Update template light test framework (#164688) 2026-03-06 20:12:10 +01:00
epenet
1471cb93bc Move smart_meter_texas coordinator to separate module (#164926)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:11:38 +01:00
Erwin Douna
2f7ac2b439 Migrate Smartthings OAuth exceptions (#164939)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-06 20:10:41 +01:00
epenet
0accb403be Move WattTime coordinator to separate module (#164726)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:10:14 +01:00
epenet
f49a323faf Move wolflink coordinator to separate module (#164929)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-06 20:08:29 +01:00
TimL
21d303dbbc Fix button entity creation for devices with more than two radios (#164699) 2026-03-06 20:07:56 +01:00
Antonio Mello
c080a460a2 Fix IntesisHome outdoor_temp not reported when value is 0.0 (#164703)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:07:11 +01:00
epenet
75d675f299 Move AirVisual coordinator to separate module (#164738)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:06:18 +01:00
epenet
a7e7d01b7a Move launch_library coordinator to separate module (#164747)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:05:42 +01:00
epenet
8a0569e279 Move AirVisual Pro coordinator to separate module (#164742)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-06 20:05:30 +01:00
epenet
e8279bd20f Move LED BLE coordinator to separate module (#164749)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:04:51 +01:00
epenet
852dbf8986 Move peco coordinator to separate module (#164851)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 20:04:34 +01:00
hanwg
6f0eb1d07a Upgrade IQS to gold for Telegram bot (#164911) 2026-03-06 20:04:01 +01:00
epenet
6f68d91593 Move DataUpdateCoordinator to coordinator module in tesla_wall_connector (#164937)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:01:16 +01:00
epenet
ffc17b6e91 Move whois coordinator to separate module (#164936)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-06 20:00:18 +01:00
epenet
0d04d79844 Move DataUpdateCoordinator to separate module in reolink (#164914)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:59:56 +01:00
epenet
f57884cb95 Move kraken API wrapper class to coordinator module (#164942)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:54:20 +01:00
Manu
3a83fe5c72 Change setpoint step size in IronOS integration (#164979) 2026-03-06 19:38:26 +01:00
Willem-Jan van Rootselaar
973feb71c1 Bump python-bsblan to 5.1.2 (#164963) 2026-03-06 19:37:55 +01:00
epenet
ecee23fc7a Move pi_hole coordinator to separate module (#164869)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-06 19:36:52 +01:00
epenet
442d2282dc Improve type hints in maxcube climate (#164978) 2026-03-06 18:10:51 +01:00
Robert Resch
8853d3e17d Add lawn mower started_returning trigger (#164834) 2026-03-06 18:08:28 +01:00
epenet
6d1e387911 Improve type hints in airtouch4 climate (#164977) 2026-03-06 18:05:27 +01:00
epenet
13fe135e7f Improve type hints in nexia climate (#164976) 2026-03-06 18:04:56 +01:00
epenet
618687ea05 Improve type hints in nuheat climate (#164975) 2026-03-06 18:04:24 +01:00
epenet
8b545a6e76 Improve type hints in oem climate (#164974) 2026-03-06 18:04:07 +01:00
epenet
42fa13200d Improve type hints in proliphix climate (#164972) 2026-03-06 18:03:39 +01:00
epenet
d56e944a86 Improve type hints in schluter climate (#164970) 2026-03-06 18:03:17 +01:00
epenet
fb357390ce Remove disabled Tfiac integration (#164966) 2026-03-06 18:00:42 +01:00
Shay Levy
702450e209 Bump aioswitcher to 6.1.1 (#164981) 2026-03-06 17:54:38 +01:00
g4bri3lDev
bbe45e0759 Add OpenDisplay integration (#164048)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2026-03-06 16:23:09 +01:00
epenet
92902c7aa1 Improve type hints in smarttub climate (#164968) 2026-03-06 16:07:41 +01:00
epenet
5d92dd7760 Use shorthand attributes in zhong_hong climate (#164964) 2026-03-06 16:00:14 +01:00
Joost Lekkerkerker
0ab62dabde Create Chess.com integration (#164960) 2026-03-06 15:55:59 +01:00
Sean O'Keeffe
fc68828c78 more programs for Miele steam ovens (#164768)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-06 15:15:43 +01:00
Sab44
7644036592 Add diagnostics to Libre Hardware Monitor (#164958)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-06 15:15:18 +01:00
epenet
f19068f7de Mark device_info type hint as mandatory (#164951) 2026-03-06 15:15:05 +01:00
Robin Lintermann
13d2211755 Add sensor entity for total swing time (#164334) 2026-03-06 15:10:06 +01:00
epenet
87e63591d1 Use shorthand attributes in heatmiser climate (#164957) 2026-03-06 15:00:51 +01:00
epenet
fc02bbcdd0 Improve type hints in coolmaster climate (#164956) 2026-03-06 15:00:13 +01:00
Simone Chemelli
388d619604 Bump aiovodafone to 3.1.3 (#164955) 2026-03-06 14:59:51 +01:00
Daniel Hjelseth Høyer
3777acff95 Fix energy unit in Homevolt (#164959)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-03-06 14:58:44 +01:00
Jamie Magee
e0fd6784cf Test aladdin_connect stale device cleanup (#164119) 2026-03-06 13:03:09 +01:00
epenet
305463d882 Move DataUpdateCoordinator to coordinator module in nsw_fuel_station (#164940)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:25:07 +01:00
Erwin Douna
de16edc55b Replace assert in Proxmox coordinator (#164892) 2026-03-06 11:16:14 +01:00
Erwin Douna
bd6438937b Adjust read-only parallel updates for Portainer (#164890) 2026-03-06 11:14:58 +01:00
Erwin Douna
45e453791e Update Proxmox code owners (#164941) 2026-03-06 11:11:06 +01:00
epenet
152137a3a2 Move DataUpdateCoordinator to separate module in simplisafe (#164917)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:10:31 +01:00
epenet
e059c51b1d Move wiz coordinator to separate module (#164931)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:21:07 +01:00
epenet
9ef66a3a90 Move supla coordinator to separate module (#164928)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:20:42 +01:00
Petro31
494f8c32d5 Fix 'this' variable in template options flow (#164866) 2026-03-06 09:39:42 +01:00
dependabot[bot]
51f90a328b Bump actions/attest-build-provenance from 3.2.0 to 4.1.0 (#164909)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-06 09:38:33 +01:00
epenet
b7bdb7b32a Move DataUpdateCoordinator to separate module in subaru (#164918)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:09:03 +01:00
epenet
76c8bae098 Use typed coordinator in powerwall (#164887)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 08:35:09 +01:00
Erwin Douna
59a75e74fe Bump proxmoxer 2.3.0 (#164884) 2026-03-06 08:34:45 +01:00
Christopher Fenner
a4af1ce5f8 Translate device name in Season integration (#164882) 2026-03-06 08:33:20 +01:00
Erwin Douna
30ea0b4923 Proxmoxve add parallel updates (#164889) 2026-03-06 08:32:36 +01:00
Erwin Douna
fb889dd524 Optimize init proxmox (#164891) 2026-03-06 08:32:18 +01:00
epenet
31055c5cde Move DataUpdateCoordinator to separate module in recollect_waste (#164913)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:31:15 +01:00
epenet
a264e5949f Move DataUpdateCoordinator to separate module in senz (#164916)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:30:29 +01:00
Colin
84260ac3f7 Use shared aiohttp session in openevse (#164552) 2026-03-06 07:49:53 +01:00
epenet
f50a35877d Move RDW DataUpdateCoordinator to separate module (#164910)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 07:47:08 +01:00
Luke Lashley
6bc94a318a Pass in Base Url during Roborock reauth (#164903) 2026-03-05 20:24:59 -08:00
Blake Messer
b0904917ca Fix Rain Bird controllers updated by Rain Bird 2.x (#163915) 2026-03-05 19:37:15 -08:00
Michael
536cfc4c67 Add number.changed trigger (#163984) 2026-03-05 21:36:39 +01:00
Erwin Douna
27b647fa36 Add backoff/max retries in Portainer API (#164805)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-03-05 21:26:22 +01:00
Michael
16fb2dfa91 Add domain driven triggers to schedule helper (#159325) 2026-03-05 21:26:05 +01:00
Josef Zweck
664b75e060 Bump onedrive-personal-sdk to 0.1.5 (#164880) 2026-03-05 20:19:19 +00:00
Erik Montnemery
1cd302eb17 Fix flaky bang_olufsen tests (#164868) 2026-03-05 21:18:10 +01:00
Dan Carroll
8da86796d2 Bump pyeconet to 0.2.2 (#164859) 2026-03-05 20:17:57 +00:00
Denis Shulyaka
33c0edc994 Add GPT-5.4 support to OpenAI conversation (#164883) 2026-03-05 20:16:53 +00:00
epenet
3e8833da54 Refactor Tuya wrappers to use generics (#164587) 2026-03-05 19:22:48 +01:00
Michael Hansen
3858d557b3 Add missing parameters from handle REST API (#164687)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-03-05 11:48:57 -06:00
Renat Sibgatulin
0923bed4b6 Add zeroconf support for air-Q (#164727)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-05 17:55:34 +01:00
Marc Mueller
9b8432eac3 Fix volvo test RuntimeWarning (#164845) 2026-03-05 17:51:12 +01:00
Tucker Kern
5232c05702 Ensure Snapcast client has a valid current group before accessing group attributes. (#164683) 2026-03-05 17:50:31 +01:00
Erik Montnemery
e5f77801a7 Unconditionally set up base platform integrations (#164863) 2026-03-05 17:30:34 +01:00
Erik Montnemery
bc138b3485 Fix incomplete device info in laundrify sensor (#164824)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-05 17:08:31 +01:00
Andrew Jackson
ae90c5fa92 Update Mastodon quality scale to gold (#164842) 2026-03-05 16:50:45 +01:00
Matthias Alphart
2fce45abe1 Fix KNX sensor default attributes for energy and volume DPTs (#164838)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-05 16:48:47 +01:00
karwosts
e4417f7b00 Add unique_id to demo water_heater (#164857) 2026-03-05 16:40:17 +01:00
Ariel Ebersberger
b57c7f8a95 Fix ffmpeg fixture (#164860) 2026-03-05 16:37:43 +01:00
Henning Kerstan
0618460d73 Replace enocean library (#164272)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-05 16:30:06 +01:00
epenet
92dd045772 Move Mullvad VPN coordinator to separate module (#164750)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-05 16:20:58 +01:00
Michael Hansen
fc723e1a42 Add missing features to Wyoming conversation agent (#164278) 2026-03-05 15:56:21 +01:00
Joshua Monta
5907356309 Add new influenza index sensor to Uhoo (#164710) 2026-03-05 15:37:22 +01:00
J. Diego Rodríguez Royo
1c221b4714 Bump aiohomeconnect to 0.30.0 (#164846)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-05 15:34:12 +01:00
Retha Runolfsson
05d57167d2 Add support for switchbot keypad vision (#160484)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-05 14:54:07 +01:00
epenet
69a98dd53e Move nuheat coordinator to separate module (#164833)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 14:16:55 +01:00
John O'Nolan
3c7dd93c7f Add reauthentication flow to Ghost integration (Silver) (#164847) 2026-03-05 14:16:03 +01:00
reneboer
1327712be4 Add sensor charging settings mode (#164455)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-03-05 13:24:23 +01:00
epenet
933e57ba6a Simplify Netgear entity initialisation (#164837) 2026-03-05 13:17:19 +01:00
John O'Nolan
77d54aadc6 Fix Ghost config flow using wrong field name for site UUID (#164836) 2026-03-05 12:46:59 +01:00
Andreas Jakl
5fe2ab93ff Add device tracker to NRGkick integration (#164804)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-05 12:00:30 +01:00
Glenn de Haan
0e4698eb99 Add device class to active_liter_lpm sensor (#164809) 2026-03-05 11:50:37 +01:00
epenet
698c5eca00 Migrate remaining netgear coordinators to separate module (#164826) 2026-03-05 11:49:28 +01:00
Raphael Hehl
c7776057b7 Enforce SSRF redirect protection only for connector allowed_protocol_schema_set (#164769)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-03-05 11:45:05 +01:00
Erik Montnemery
e87c677cc4 Improve homee tests (#164820) 2026-03-05 11:15:50 +01:00
Erik Montnemery
c3858a0841 Improve tuya diagnostic tests (#164819) 2026-03-05 11:13:01 +01:00
Michael
42bc5c3a5f Add remote.turned_on and remote.turned_off triggers (#164535)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-05 10:52:29 +01:00
epenet
76bc58da2c Add base NetgearDataCoordinator to netgear (#164816) 2026-03-05 10:52:12 +01:00
epenet
fc8719ce35 Remove caio from licenses exception list (#164806) 2026-03-05 10:18:08 +01:00
dependabot[bot]
60a4a97d9c Bump dawidd6/action-download-artifact from 14 to 16 (#164790)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 10:16:23 +01:00
Erwin Douna
284721e1df Bump pyportainer 1.0.32 (#164803) 2026-03-05 09:06:46 +01:00
Norbert Rittel
bfa707d79e Use common string for "host" in devialet config flow (#164798) 2026-03-05 08:32:46 +01:00
Norbert Rittel
633e2e7469 Use common state for "medium" in smartthings (#164799) 2026-03-05 08:32:35 +01:00
dependabot[bot]
ad1c6846e7 Bump actions/upload-artifact from 6.0.0 to 7.0.0 (#164791) 2026-03-05 07:29:59 +01:00
Erwin Douna
f75140b626 Add const to Portainer for endpoint up (#164746) 2026-03-05 00:38:59 +01:00
rappenze
f83757da7c Use unique fibaro_id in test fixtures (#164763) 2026-03-04 22:04:38 +00:00
Norbert Rittel
ca338c98f3 Clarify description of vacuum.clean_area action (#164764) 2026-03-04 21:57:59 +00:00
Ian Foster
18a8afb017 Update keyboard_remote dependencies (#164755) 2026-03-04 19:47:17 +01:00
Italo Lombardi
0136e9c7eb ISS integration: better entity handling (#159050)
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-03-04 17:46:48 +01:00
Erik Montnemery
d88c736016 Add is_closed state attribute to cover (#164739) 2026-03-04 16:54:06 +01:00
Robert Resch
780dc178a1 Use Python version file in CI for setting the default python version (#164751) 2026-03-04 16:53:31 +01:00
Petro31
b7ba945dfc Fix this variable preview issue with template entities from the UI (#164740) 2026-03-04 16:01:41 +01:00
Magnus Øverli
01de7052af Add deprecation timeline to flexit_bacnet fireplace switch (#164450) 2026-03-04 15:47:40 +01:00
Allen Porter
3fe6a31ee9 Improve Roborock device info creation and enhance device registration for disabled or failed devices. (#164553) 2026-03-04 15:45:51 +01:00
rappenze
95570643ec Fix handling of several thermostat QuickApp's in fibaro (#164344)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-04 15:40:49 +01:00
starkillerOG
e3210b0ab9 Fix Reolink entity unique_id migration when unique_id already exists (#164667) 2026-03-04 15:12:26 +01:00
Artur Pragacz
2edabf903a Add backup integration to recovery mode (#164734) 2026-03-04 14:33:28 +01:00
Stefan Agner
0e4e703b64 Ignore transient empty segments in Matter vacuum (#164737)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-04 14:24:28 +01:00
tobiaswaldvogel
88624f5179 Use jog up/down in motionblinds if no tilt position is available (#164694)
Signed-off-by: Tobias Waldvogel <tobias.waldvogel@gmail.com>
Co-authored-by: starkillerOG <starkiller.og@gmail.com>
2026-03-04 13:27:47 +01:00
Erwin Douna
4a5fdfc0ec Bump pyportainer 1.0.31 (#164733) 2026-03-04 13:26:10 +01:00
Bram Kragten
c6e91afae4 Update frontend to 20260304.0 (#164736) 2026-03-04 13:25:57 +01:00
Kamil Breguła
db5e7e4521 Refactor AWS S3 tests (#164098)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-04 13:13:43 +01:00
Joakim Plate
25489c224b Restore handling of is active input for chromecast (#164735) 2026-03-04 13:10:10 +01:00
Tom
c4f64598a0 Add informative errors to Proxmox VE buttons (#164417) 2026-03-04 12:48:17 +01:00
starkillerOG
59e579cf5a Bump reolink-aio to 0.19.1 (#164732) 2026-03-04 12:46:38 +01:00
epenet
831c28cf2c Migrate netgear to use runtime_data (#164718) 2026-03-04 11:37:05 +01:00
Erik Montnemery
be1affc6ba Pin exact Python version in .python-version (#164722) 2026-03-04 11:21:44 +01:00
J. Diego Rodríguez Royo
94a25b5688 Improve mobile_app notify.notify with not connected targets (#161855) 2026-03-04 11:11:02 +01:00
AlCalzone
382940d661 Support Z-Wave Hoppe eHandle tilt sensor (#164689) 2026-03-04 11:00:24 +01:00
Brett Adams
b8e1c0cf2c Fix teslemetry time_of_use service tariff double-wrapping (#164702)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-04 09:59:52 +01:00
TheJulianJES
0d23d8dc09 Bump ZHA to 1.0.1 (#164709) 2026-03-04 09:57:07 +01:00
dependabot[bot]
b750de1e3e Bump actions/ai-inference from 2.0.6 to 2.0.7 (#164713)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 09:45:00 +01:00
hanwg
7d7e8e0bde Add support for http webhook for Telegram bot (#162690) 2026-03-04 09:18:02 +01:00
Joost Lekkerkerker
d6f355355f Add cleaning type select to SmartThings (#164472)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-03-04 07:18:18 +01:00
Simone Chemelli
5dad64e54c Bump aioamazondevices to 13.0.0 (#164618) 2026-03-03 22:16:07 +00:00
Robert Resch
c311ff0464 Fix wheels building by using arch dependent requirements_all file (#164675) 2026-03-03 21:55:59 +01:00
Dave T
c45675a01f Add additional diagnostic sensors to aurora_abb_powerone PV inverter (#164622) 2026-03-03 21:34:44 +01:00
erikbadman
9d92141812 Add support for active power limit in Kostal Plenticore (#164674) 2026-03-03 21:33:54 +01:00
Robin Lintermann
501b973a98 Add send diagnostics button to smarla (#164335) 2026-03-03 21:31:31 +01:00
Kamil Breguła
fd4d8137da Change reconfiguration-flow status to 'todo' in WebDAV (#164637)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-03 21:23:24 +01:00
Miguel Angel Nubla
33881c1912 Fix infinite loop in esphome assist_satellite (#163097)
Co-authored-by: Artur Pragacz <artur@pragacz.com>
2026-03-03 20:44:36 +01:00
Robin Lintermann
9bdb03dbe8 Set device classes and measurement units for Smarla (#164682) 2026-03-03 18:36:02 +00:00
epenet
d2178ba458 Cleanup deprecated tuya entities (#164657) 2026-03-03 19:31:09 +01:00
Abílio Costa
06cdf3c5d2 Add PR review Claude skill (#164626) 2026-03-03 18:21:51 +00:00
r2xj
84c994ab80 Add support for samsungce.lamp as light entity and when not under main component (#164448)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-03 18:29:36 +01:00
Abílio Costa
1d5913d7a5 Simplify copilot-instructions.md script to use file refs (#164686) 2026-03-03 17:17:25 +00:00
epenet
05acba37c7 Remove deprecated YAML import from nederlandse_spoorwegen (#164662) 2026-03-03 17:59:29 +01:00
Samuel Xiao
7496406156 Bumb switchbot api to v2.11.0 (#164663) 2026-03-03 17:59:03 +01:00
epenet
543f2b1396 Improve type hints in meteoclimatic (#164651) 2026-03-03 17:57:54 +01:00
epenet
3df2bbda80 Bump tuya-device-handlers to 0.0.11 (#164586) 2026-03-03 17:57:36 +01:00
epenet
b661d37a86 Move mutesync coordinator to separate module (#164600) 2026-03-03 17:57:11 +01:00
Ariel Ebersberger
2102babc6d Influxdb repair issue follow up (#164684) 2026-03-03 17:57:09 +01:00
epenet
f3a1cab582 Migrate motionblinds_ble to runtime_data (#164601) 2026-03-03 17:56:54 +01:00
epenet
03c9ce25c8 Simplify access to motioneye client (#164599) 2026-03-03 17:56:16 +01:00
Christian Lackas
8fcabcec16 Fix HomematicIP heating group availability with unreachable members (#162571) 2026-03-03 17:34:14 +01:00
Michael Hansen
2a33096074 Bump intents to 2026.3.3 (#164676) 2026-03-03 17:26:44 +01:00
Ariel Ebersberger
14a9eada09 Add repair issue after importing influxdb yaml config (#164145)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-03 16:33:25 +01:00
tobiaswaldvogel
4a00f78e90 Add missing cover entity features to motion_blinds (#164673)
Signed-off-by: Tobias Waldvogel <tobias.waldvogel@gmail.com>
2026-03-03 16:30:55 +01:00
starkillerOG
abef46864e Fix key error in Reolink DHCP if still setting up (#164619) 2026-03-03 16:12:30 +01:00
Willem-Jan van Rootselaar
73b28f1ee2 Bump python-bsblan to 5.1.1 (#164591) 2026-03-03 15:56:07 +01:00
epenet
7379d41393 Migrate met_eireann to runtime_data (#164607) 2026-03-03 15:55:12 +01:00
epenet
89acb02519 Migrate monoprice to runtime_data (#164604) 2026-03-03 15:54:48 +01:00
Paul Tarjan
e343e90da2 Fix Reolink camera updates persisting in UI (#161149)
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-03 15:40:32 +01:00
Daniel Schneider
e9a576494b Bump ring-doorbell to 0.9.14 (#158074)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-03 15:36:26 +01:00
TimL
4e047b56d8 Bump pysmlight to v0.2.16 (#164665)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-03-03 14:47:54 +01:00
epenet
a1e95c483d Migrate metoffice to runtime_data (#164606) 2026-03-03 14:19:57 +01:00
Andreas Jakl
9cb6e02c5f Add binary sensor platform and tests to NRGkick integration (#164629)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-03 13:55:10 +01:00
epenet
2c75e3289a Improve device_info type hints in mobile_app (#164655) 2026-03-03 13:40:56 +01:00
reneboer
348012a6b8 Bump renault-api to 0.5.6 (#164664) 2026-03-03 12:52:41 +01:00
Michael
e0db00e089 Allow the creation of multi-domain triggers (#164628) 2026-03-03 12:52:27 +01:00
Thomas Pfeiffer
b2280198d9 Add equalizer switch for Cambridge Audio devices (#162956) 2026-03-03 12:51:24 +01:00
Artur Pragacz
9cc4a3e427 Trigger recovery mode on registry major version downgrade (#164340) 2026-03-03 11:46:32 +01:00
Raman Gupta
f94a075641 Decouple Vizio apps coordinator from config entry (#163923)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-03 11:22:41 +01:00
hanwg
f1856e6ef6 Update subentry description for Telegram bot (#164642)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-03 11:21:01 +01:00
mettolen
ed35bafa6c Bump pysaunum to 0.6.0 (#164530) 2026-03-03 11:18:02 +01:00
Manu
66e16d728b Bump python-xbox to 0.2.0 (#164616) 2026-03-03 11:10:14 +01:00
Matthias Alphart
a806efa7e2 Update knx-frontend to 2026.3.2.183756 (#164623) 2026-03-03 11:08:20 +01:00
Norman Yee
ad4b4bd221 Enhance GV5140 test to assert temperature and humidity sensors (#164644) 2026-03-03 11:05:32 +01:00
David Recordon
c9c9a149b6 Bump pylutron-caseta to 0.27.0 (#164614) 2026-03-03 11:03:12 +01:00
epenet
0f9fdfe2de Fix invalid device registry identifiers in eafm (#164654)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-03 11:02:59 +01:00
Abílio Costa
a76b63912d Add Ubisys virtual integration (#164314) 2026-03-03 10:00:57 +00:00
Joshua Monta
bc03e13d38 Bump uhooapi to 1.2.8 (#164648) 2026-03-03 10:59:32 +01:00
Colin
450aa9757d Bump python-openevse-http to 0.2.5 (#164641) 2026-03-03 10:54:58 +01:00
Tom Matheussen
158389a4f2 Remove deprecated YAML import from Satel Integra (#164469) 2026-03-03 10:24:23 +01:00
Raman Gupta
95e89d5ef1 Redact zwave_js dsk key from diagnostics (#164636)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:01:35 +01:00
dependabot[bot]
e107b8e5cd Bump actions/download-artifact from 7.0.0 to 8.0.0 (#164647)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 08:34:36 +01:00
epenet
f875b43ede Remove unnecessary suppress in importlib helper (#164323) 2026-03-03 01:00:32 +01:00
Jeff Terrace
6242ef78c4 Move ONVIF event parsing into a module outside core (#164550)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-03-02 12:18:05 -10:00
Abílio Costa
3c342c0768 Add infrared platform to ESPHome (#162346)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 22:00:47 +00:00
Norman Yee
5dba5fc79d Add Govee H5140 CO2 monitor support to govee_ble (#164365)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-03-02 20:12:48 +00:00
James
713b7cf36d Check Daikin zone temp keys before represent (#164297)
Co-authored-by: barneyonline <barneyonline@users.noreply.github.com>
2026-03-02 19:48:39 +00:00
Bram Kragten
cb016b014b Update frontend to 20260302.0 (#164612) 2026-03-02 18:53:01 +01:00
Michael Hansen
afb4523f63 Add device_id and satellite_id to conversation HTTP/websocket APIs (#164414) 2026-03-02 17:01:51 +01:00
Alex Brown
05ad4986ac Fix Matter clear lock user (#164493) 2026-03-02 16:28:49 +01:00
epenet
42dbd5f98f Migrate moat to runtime_data (#164605) 2026-03-02 16:14:25 +01:00
epenet
f58a514ce7 Migrate monzo to runtime_data (#164603) 2026-03-02 16:14:10 +01:00
Artur Pragacz
8fb384a5e1 Raise on vacuum area mapping not configured (#164595) 2026-03-02 15:36:48 +01:00
Samuel Xiao
c24302b5ce Switchbot Cloud: Fixed Smart Radiator Thermostat off line (#162714)
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-03-02 14:44:34 +01:00
Jan-Philipp Benecke
999ad9b642 Bump aiotankerkoenig to 0.5.1 (#164590) 2026-03-02 14:44:29 +01:00
Pierre Sassoulas
36d6b4dafe Use clearer number notation for very small and very large literals (#164521) 2026-03-02 14:06:19 +01:00
Norbert Rittel
06870a2e25 Replace "the lock" with "a lock" in matter action descriptions (#164585) 2026-03-02 12:56:45 +01:00
willemstuursma
85eba2bb15 Bump DSMR parser to 1.5.0 (#164484) 2026-03-02 12:52:37 +01:00
Joost Lekkerkerker
5dd6dcc215 Add select for SmartThings Water spray level (#164520) 2026-03-02 12:17:31 +01:00
epenet
8bf894a514 Migrate microbees to runtime_data (#164564) 2026-03-02 12:04:34 +01:00
epenet
d3c67f2ae1 Migrate medcom_ble to runtime_data (#164557) 2026-03-02 12:03:35 +01:00
epenet
b60a282b60 Move motioneye coordinator to separate module (#164568) 2026-03-02 11:57:19 +01:00
epenet
0da1d40a19 Migrate meteoclimatic to runtime_data (#164559) 2026-03-02 11:50:46 +01:00
Robert Resch
aa3be915a0 Bump aiogithubapi to 26.0.0 (#164579) 2026-03-02 11:49:32 +01:00
Manu
0d97bfbc59 Bump pyloadapi to 2.0.0 (#164495) 2026-03-02 11:47:13 +01:00
epenet
fe830337c9 Migrate modem_callerid to runtime_data (#164566) 2026-03-02 11:45:58 +01:00
epenet
5210b7d847 Migrate moehlenhoff_alpha2 to runtime_data (#164571) 2026-03-02 11:45:10 +01:00
Mike Ryan
2f7ed4040b Bump python-fullykiosk from 0.0.14 to 0.0.15 (#164511) 2026-03-02 11:42:56 +01:00
Simone Chemelli
6376ba93a7 Bump aioamazondevices to 12.0.2 (#164518) 2026-03-02 11:37:39 +01:00
J. Nick Koston
fd3a1cc9f4 Bump yalexs-ble to 3.2.7 (#164555) 2026-03-02 11:36:05 +01:00
epenet
208013ab76 Move metoffice coordinators to separate module (#164562)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 11:31:57 +01:00
Alex Brown
770b3f910e Fix Matter lock credential slot iteration bound (#164478)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:56:03 +01:00
Norbert Rittel
5dce4a8eda Change one remaining string from "Overseerr" to "Seerr" (#164569) 2026-03-02 10:22:49 +01:00
Jan-Philipp Benecke
6fcc9da948 Fix large WebDAV backup metadata download (#164563)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 10:17:18 +01:00
epenet
bf93580ff9 Migrate modern_forms to runtime_data (#164570) 2026-03-02 10:10:03 +01:00
Jan-Philipp Benecke
0c2fe045d5 Bump aiowebdav2 to 0.6.1 (#164560) 2026-03-02 10:09:33 +01:00
Joost Lekkerkerker
e14a3a6b0e Fix SmartThings EHS power (#164395) 2026-03-02 08:35:37 +01:00
Joost Lekkerkerker
e032740e90 Add time platform to SmartThings (#164451) 2026-03-02 08:34:53 +01:00
Joost Lekkerkerker
78ad1e102d Add binary sensor for full dust bag in SmartThings (#164457) 2026-03-02 08:34:19 +01:00
Joost Lekkerkerker
4f97cc7b68 Add sound detection sensitivity select to SmartThings (#164466) 2026-03-02 08:33:47 +01:00
dependabot[bot]
df8f135532 Bump github/codeql-action from 4.32.3 to 4.32.4 (#164554) 2026-03-02 07:30:23 +01:00
J. Nick Koston
0066801b0f Bump yarl to 1.23.0 (#164542) 2026-03-02 07:22:37 +01:00
Joost Lekkerkerker
0aa66ed6cb Add select for SmartThings driving mode (#164522) 2026-03-01 19:11:58 +01:00
HadiAyache
6903463f14 Fix AccuWeather daily forecast crash when humidity average is missing (#163968)
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-01 17:19:15 +01:00
Brett Adams
a473010fee Update Tessie quality scale to silver (#164104)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-01 16:53:39 +01:00
Robin Lintermann
ddf7a783a8 Bump smarla quality scale to silver (#164325) 2026-03-01 11:52:11 +01:00
Joost Lekkerkerker
513e4d52fe Add button to reset HEPA filter to SmartThings (#164464) 2026-03-01 07:33:10 +01:00
Klaas Schoute
17bb14e260 Update error handling messages for Powerfox Local integration (#164465) 2026-03-01 07:32:36 +01:00
Brett Adams
cd1258464b Fix OAuth token type narrowing in Teslemetry (#164505) 2026-03-01 07:31:34 +01:00
Allen Porter
d3f5e0e6d7 Update nest access token error handling to use specific OAuth2 token request exceptions (#164506) 2026-03-01 07:26:07 +01:00
Joost Lekkerkerker
e124829364 Rename Overseerr integration to Seerr (#164060) 2026-02-28 23:07:31 +01:00
Jan Bouwhuis
87b83dcc1b Remove the MQTT object_id option after 6 months of deprecation (#164460)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-28 20:12:23 +01:00
Erik Montnemery
be9b47539d Revert "Remove unnecessary volume_up/volume_down overrides from frontier_silicon media player" (#164463) 2026-02-28 20:11:52 +01:00
Joost Lekkerkerker
be6ddc314c Add sound detection switch to SmartThings (#164470) 2026-02-28 20:11:13 +01:00
David Bonnes
c6f8a7b7e4 Harden test of an invalid service call for Evohome (#164458) 2026-02-28 20:10:11 +01:00
Joost Lekkerkerker
53da5612e9 Add fan speed to SmartThings vacuum (#164452)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-28 20:09:43 +01:00
Michael Davie
6cc56b76f9 Bump env-canada to 0.13.2 (#164480)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 20:08:17 +01:00
Tom Matheussen
03cb65d555 Require user code to be set when toggling Satel Integra switches (#164483) 2026-02-28 20:06:56 +01:00
Abílio Costa
73dd024933 Add merged PR count sensor to Github integration (#164405) 2026-02-28 15:13:17 +01:00
Barry vd. Heuvel
1c8c92bf8f Bump weheat to 2026.2.28 (#164456) 2026-02-28 14:40:58 +01:00
Khole
7e041a6759 Hive - Bump pyhive-integration to v1.0.8 (#164453) 2026-02-28 12:32:37 +00:00
Alex Brown
ee05f14530 Add Matter lock user and credential management services (#161936)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 10:43:09 +01:00
Simone Chemelli
f0ba5178b7 Fix RpcSensorDescription for Shelly (#150719) 2026-02-28 09:28:53 +01:00
Denis Shulyaka
df51ac932b Improve Anthropic service exceptions (#164418)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-28 09:20:17 +01:00
Paulus Schoutsen
e96b5f2eb1 Remove unnecessary volume_up/volume_down overrides from mpd media player (#164428)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 09:16:53 +01:00
Paulus Schoutsen
4e59c89327 Remove unnecessary volume_up/volume_down overrides from bluesound media player (#164426)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:57:53 +01:00
Paulus Schoutsen
15676021a9 Remove unnecessary volume_up/volume_down overrides from demo media player (#164424)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:57:30 +01:00
Paulus Schoutsen
d3197a0d1e Remove unnecessary volume_up/volume_down overrides from aquostv media player (#164431)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:56:09 +01:00
Paulus Schoutsen
35692b335c Remove unnecessary volume_up/volume_down overrides from frontier_silicon media player (#164430)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:49:47 +01:00
Paulus Schoutsen
cc5c810501 Remove unnecessary volume_up/volume_down overrides from NADtcp media player (#164434)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:47:08 +01:00
Paulus Schoutsen
f2681f2dc8 Remove unnecessary volume_up/volume_down overrides from monoprice media player (#164429)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 08:45:43 +01:00
Brett Adams
fe0a22c790 Complete strict typing for Teslemetry integration (#164416) 2026-02-28 08:33:45 +01:00
Norman Yee
186ab50458 Bump govee-ble to 1.2.0 (#164438) 2026-02-28 08:24:38 +01:00
mettolen
b524c40176 Remove error translation placeholders from Airobot (#164436) 2026-02-28 06:18:19 +01:00
Klaas Schoute
642864959a Update translatable exceptions for Powerfox integration (#164322) 2026-02-28 01:57:02 +00:00
Franck Nijhof
7ef6c34149 Reject relative paths in SFTP storage backup location config flow (#164408) 2026-02-27 19:25:04 -05:00
Franck Nijhof
5b32e42b8c Add aioclient_mock to ssdp tests to prevent real HTTP requests (#164403) 2026-02-27 19:24:13 -05:00
Franck Nijhof
1be8b8e525 Add discovery mocks to tplink init tests (#164386) 2026-02-27 19:23:47 -05:00
Franck Nijhof
3fae15c430 Fix fixture ordering in esphome dashboard tests (#164367) 2026-02-27 19:23:13 -05:00
Franck Nijhof
c7e78568d0 Enable real sockets in default_config setup test (#164366) 2026-02-27 19:22:29 -05:00
Stefan Agner
492b542136 Fix Matter vacuum crash on nullable ServiceArea location info (#164411) 2026-02-28 00:11:32 +01:00
Franck Nijhof
0f4852d8c2 Enable sockets for http integration tests (#164404) 2026-02-27 22:22:15 +01:00
nopoz
737c0c1823 Google Cast: detect state and attributes when device is doing active non-media casting (#160819)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-02-27 22:07:09 +01:00
Petro31
5fadcb01e9 Fix int vs float template sensor issue (#164339) 2026-02-27 22:06:37 +01:00
TheJulianJES
2b4f46a739 Fix ZHA update entities not working after reload (#164290) 2026-02-27 22:04:51 +01:00
Franck Nijhof
44fe37da1f Mock ConnectionContextBuilder in homematicip_cloud tests (#164356) 2026-02-27 22:00:37 +01:00
Joost Lekkerkerker
abd4e89577 Sync SmartThings vacuum fixture (#164360) 2026-02-27 21:43:30 +01:00
Franck Nijhof
033798835a Refactor adguard tests to use proper fixtures for mocking (#164402)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-27 21:34:10 +01:00
Franck Nijhof
83c77957c1 Add missing mock fixtures to telegram_bot polling init test (#164398) 2026-02-27 21:29:10 +01:00
dependabot[bot]
b1bc1dc102 Bump actions/dependency-review-action from 4.8.2 to 4.8.3 (#164296)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-27 21:21:15 +01:00
Jason Hunter
40b8a2c380 Remove Duke Energy (#164282)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-02-27 20:19:03 +00:00
Glenn de Haan
fb23a6fbf8 Add HDFury audio offset numbers (#164315) 2026-02-27 21:02:34 +01:00
Joost Lekkerkerker
faad3de02c Bump pySmartThings to 3.6.0 (#164397) 2026-02-27 21:00:33 +01:00
Franck Nijhof
5f30f532e5 Mock async_setup_entry in unifiprotect reauth tests (#164375) 2026-02-27 20:53:52 +01:00
Franck Nijhof
667e8c4d38 Mock async_setup_entry in jvc_projector config flow tests (#164401) 2026-02-27 20:53:38 +01:00
Franck Nijhof
74240ecd26 Mock async_setup_entry in lametric DHCP discovery test (#164400) 2026-02-27 20:50:11 +01:00
Franck Nijhof
c81ee53265 Mock TodoistAPIAsync in todoist failed coordinator update test (#164390) 2026-02-27 20:49:02 +01:00
Franck Nijhof
8835f1d5e6 Mock async_setup_entry in youless config flow test (#164399) 2026-02-27 20:46:48 +01:00
Franck Nijhof
2ca84182d8 Patch discovery in elkm1 invalid auth and reconfigure tests (#164396) 2026-02-27 20:46:45 +01:00
Franck Nijhof
3f0d1bc071 Mock PyMochad controller in mochad tests (#164394) 2026-02-27 20:43:08 +01:00
Franck Nijhof
350f462bdf Prevent real setup during DHCP discovery test in fully_kiosk tests (#164342) 2026-02-27 20:42:32 +01:00
Franck Nijhof
2f98e68ed8 Mock async_setup_entry in arcam_fmj config flow tests (#164351) 2026-02-27 20:42:12 +01:00
Franck Nijhof
5b7fac94e5 Mock async_setup_entry in ccm15 config flow tests (#164352) 2026-02-27 20:42:02 +01:00
Franck Nijhof
c32ce3da5c Add missing rest_api fixture in samsungtv setup test (#164353) 2026-02-27 20:41:39 +01:00
Franck Nijhof
0e1d1fbaed Fix fixture ordering in jvc_projector integration setup (#164354) 2026-02-27 20:41:17 +01:00
Franck Nijhof
57d7f364f4 Mock async_setup_entry in wilight SSDP flow test (#164393) 2026-02-27 20:40:35 +01:00
Franck Nijhof
7cc5777b47 Fix fixture ordering in madVR tests to ensure proper mocking (#164350) 2026-02-27 20:38:42 +01:00
Franck Nijhof
5e3f23b6a2 Fix mock target for Met Office config flow error test (#164391) 2026-02-27 20:37:24 +01:00
Franck Nijhof
6873a40407 Mock async_setup_entry in forked_daapd config flow tests (#164370) 2026-02-27 20:36:33 +01:00
Franck Nijhof
ddaa2fb293 Mock async_setup_entry in daikin config flow tests (#164371) 2026-02-27 20:36:23 +01:00
Franck Nijhof
53b6223459 Mock async_setup_entry in emulated_roku config flow tests (#164368) 2026-02-27 20:35:50 +01:00
Franck Nijhof
7329cfb927 Mock async_setup_entry in home_connect migration tests (#164357) 2026-02-27 20:33:54 +01:00
Franck Nijhof
44b80dde0c Mock async_setup_entry in radarr config flow tests (#164359) 2026-02-27 20:33:19 +01:00
Joost Lekkerkerker
8c125e4e4f Add do not disturb switch to SmartThings (#164364) 2026-02-27 20:31:56 +01:00
Franck Nijhof
227a258382 Add missing client mocks to tplink_omada service tests (#164389) 2026-02-27 20:30:54 +01:00
Franck Nijhof
addc2a6766 Mock async_setup_entry in speedtestdotnet config flow test (#164387) 2026-02-27 20:30:47 +01:00
Franck Nijhof
97bcea9727 Mock async_setup_entry in tautulli config flow tests (#164388) 2026-02-27 20:30:38 +01:00
Franck Nijhof
4f05c807b0 Mock async_setup_entry in panasonic_viera config flow tests (#164385) 2026-02-27 20:30:25 +01:00
Franck Nijhof
177a918c26 Mock async_setup_entry in onvif DHCP host update test (#164384) 2026-02-27 20:30:15 +01:00
Franck Nijhof
9705770c6c Remove unnecessary config entry from velux validation error test (#164383) 2026-02-27 20:30:12 +01:00
Franck Nijhof
7309351165 Mock async_setup_entry in lunatone config flow tests (#164382) 2026-02-27 20:29:22 +01:00
Franck Nijhof
d0401de70d Mock HMConnection in homematic notify tests (#164381) 2026-02-27 20:29:14 +01:00
Franck Nijhof
6b89359a73 Mock async_setup_entry in sharkiq setup test (#164380) 2026-02-27 20:27:40 +01:00
Franck Nijhof
b31bafab99 Mock async_setup_entry in roku options flow test (#164377) 2026-02-27 20:27:13 +01:00
Franck Nijhof
84c556bb63 Mock setup and client in sma config flow tests (#164374) 2026-02-27 20:26:59 +01:00
Franck Nijhof
225ea02d9a Fix axis setup failure test to mock at correct layer (#164373) 2026-02-27 20:26:46 +01:00
Franck Nijhof
ebd1cc994c Add missing mock_transmission_client to transmission init tests (#164369) 2026-02-27 20:26:33 +01:00
Franck Nijhof
9ec22ba158 Mock async_setup_entry in kostal_plenticore reconfigure test (#164372) 2026-02-27 20:26:18 +01:00
Paulus Schoutsen
2ff85d2134 Add missing volume supported features to dunehd (#164343)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:50:42 +01:00
reneboer
3eb7f04510 Add tests for Megane e-Tech (#164358)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-27 19:47:22 +01:00
Kamil Breguła
54613ac8d9 Add mik-laj as codeowner to WLED (#164349)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2026-02-27 18:31:37 +01:00
Joost Lekkerkerker
044522a8ab Add state for washing mop in SmartThings (#164348) 2026-02-27 18:26:20 +01:00
Willem-Jan van Rootselaar
19bf41496a Set entity_registry_enabled_default to False for total energy sensor (#164197) 2026-02-27 18:03:17 +01:00
Johnny Willemsen
a7efba098d Update state labels to use common keys in indevolt (#164308) 2026-02-27 17:57:02 +01:00
Arie Catsman
042ad3b759 Add missing production ct data, total-consumption and new CT to enphase_envoy (#164270) 2026-02-27 17:43:46 +01:00
Franck Nijhof
4270e4c793 Mock firmware data during reauth flow init in airos tests (#164341) 2026-02-27 17:21:22 +01:00
Erwin Douna
cb11c22e76 SMA add data descriptions (#164331) 2026-02-27 16:34:45 +01:00
Norbert Rittel
c6e23fec93 Replace "service" with "action" in evohome exception string (#164333) 2026-02-27 16:32:15 +01:00
epenet
553cecb397 Ensure future is marked as retrieved in frontend storage (#164320) 2026-02-27 15:51:34 +02:00
Erwin Douna
bb7d5897d1 Portainer redact CONF_HOST in diagnostics (#164301) 2026-02-27 13:54:12 +01:00
7eaves
3e050ebe59 Bump PySwitchBot to 1.1.0 (#164298) 2026-02-27 13:11:14 +01:00
Ye Zhiling
856a9e695a Pass encoding to AtomicWriter in write_utf8_file_atomic (#164015) 2026-02-27 11:40:58 +01:00
Artur Pragacz
1944a8bd3a Remove vacuum area mapping not configured issue (#164259) 2026-02-27 11:20:46 +01:00
epenet
3f11af8084 Drop single-use service name constants in bsblan (#164311) 2026-02-27 10:59:02 +01:00
David Bonnes
46a87cd9dd Migrate evohome's zone services to entity-level services (#164105)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-02-27 10:16:35 +01:00
Erwin Douna
f8a657cf01 Proxmox expand data descriptions (#164304) 2026-02-27 09:59:43 +01:00
Norbert Rittel
75ed7b2fa2 Improve descriptions of schlage actions (#164299) 2026-02-27 08:46:08 +01:00
hanwg
e63e54820c Remove redundant exception messages from Telegram bot (#164289) 2026-02-27 08:19:10 +01:00
Kamil Breguła
37d2c946e8 Add diagnostics platform to AWS S3 (#164118)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-02-27 08:16:58 +01:00
James
e8a35ea69d Handle missing Daikin zone temperature keys (#164170)
Co-authored-by: barneyonline <barneyonline@users.noreply.github.com>
2026-02-26 22:15:55 +00:00
Erwin Douna
28b950c64a Simplify entity init in Proxmox (#164265) 2026-02-26 21:26:29 +01:00
Denis Shulyaka
e7cf6cbe72 Create reauth flow for Anthropic for auth errors during conversation (#164267)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-26 21:16:11 +01:00
Raphael Hehl
5ad71453b8 Bump uiprotect to version 10.2.2 (#164269)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-02-26 21:12:30 +01:00
Andrew Grimberg
ab9c8093c3 Add services for managing Schlage door codes (#151014)
Signed-off-by: Andrew Grimberg <tykeal@bardicgrove.org>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-02-26 20:54:57 +01:00
peteS-UK
51acdeb563 Add config flow support to Orvibo legacy integration (#155115)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-26 19:59:13 +01:00
Johnny Willemsen
bf60d57cc2 Update state labels to use common keys in compit (#164261) 2026-02-26 18:56:11 +01:00
Kamil Breguła
d94f15b985 Update IQS for AWS S3 (#164117)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2026-02-26 18:54:04 +01:00
Erwin Douna
8a621e6570 Remove kw arg for Portainer (#164260) 2026-02-26 18:43:56 +01:00
Bram Kragten
dd44b15b7b Update frontend to 20260226.0 (#164262) 2026-02-26 18:42:48 +01:00
epenet
23ec28bbbf Simplify portainer entity initialisation (#164256) 2026-02-26 17:00:35 +01:00
epenet
7a6a479b53 Rename local constants in device_automation test (#164143) 2026-02-26 16:41:02 +01:00
epenet
f9ffaad7f1 Drop single-use service name constants in abode (#164146) 2026-02-26 16:40:43 +01:00
epenet
d4aa52ecc3 Drop single-use service name constants in alarmdecoder (#164150) 2026-02-26 16:40:28 +01:00
epenet
1b5eea5fae Drop single-use service name constants in amberelectric (#164152) 2026-02-26 16:40:13 +01:00
epenet
39dce8eb31 Drop single-use service name constants in androidtv (#164153) 2026-02-26 16:39:53 +01:00
epenet
b651e62c7f Drop single-use service name constants in advantage_air (#164148) 2026-02-26 16:39:32 +01:00
Denis Shulyaka
1e807dc9da Update reasoning options for gpt-5.3-codex (#164179) 2026-02-26 16:39:04 +01:00
epenet
cba69e7e69 Drop single-use service name constants in agent_dvr (#164149) 2026-02-26 16:37:58 +01:00
Denis Shulyaka
802a7aafec Disable code interpreter with minimal reasoning for OpenAI (#164254) 2026-02-26 16:37:31 +01:00
Joost Lekkerkerker
db5e7b3e3b Remove invalid color mode from philips_js (#164204) 2026-02-26 16:33:35 +01:00
Erwin Douna
75798bfb5e Fix stack devices merging with container devices in Portainer (#164135)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-02-26 16:14:50 +01:00
Kevin Stillhammer
06a25de0d5 Remove redundant DEFAULT_TIME_DELTA in waze_travel_time (#164227) 2026-02-26 15:43:16 +01:00
AlCalzone
892da4a03e Rename "Z-Wave Supervisor app" to "Z-Wave JS app" (#164147) 2026-02-26 15:38:03 +01:00
epenet
91e8e3da7a Use constants in default_config tests (#164144) 2026-02-26 15:31:48 +01:00
Kevin Stillhammer
144b8768a1 Add time_delta option to waze_travel_time (#161803) 2026-02-26 14:28:05 +01:00
Brett Adams
cb6d86f86d Add energy price calendar platform to Teslemetry (#145848)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-26 13:42:45 +01:00
epenet
422007577e Use constant in diagnostics test (#164139) 2026-02-26 12:58:21 +01:00
Norbert Rittel
7c2904bf48 Replace "add-ons" with "apps" in backup issues (#164129) 2026-02-26 12:57:09 +01:00
epenet
3240fd7fc8 Drop single-use service name constants in amcrest (#164156) 2026-02-26 12:54:21 +01:00
epenet
7dc2dff4e7 Drop single-use service name constants in alexa_devices (#164151) 2026-02-26 12:53:18 +01:00
Abílio Costa
7e8de9bb9c Add infrared entity integration (#162251)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-02-26 11:45:21 +00:00
Luca Angemi
9eff12605c Add minimum state duration variable to history_stats (#151643) 2026-02-26 11:21:37 +01:00
Erik Montnemery
784ac85759 Require full coverage for backup platforms (#164137) 2026-02-26 11:16:32 +01:00
Amit Finkelstein
31f7961437 Add HassOS "mount_reload" action (#155996)
Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2026-02-26 08:04:58 +01:00
mettolen
eaae64fa12 Remove error translation placeholders from Saunum (#164121) 2026-02-26 07:44:19 +01:00
Paulus Schoutsen
88b276f3a4 Simplify Anthropic integration name (#164124)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-26 07:43:44 +01:00
Kamil Breguła
f5c996e243 Add support for S3 prefix in AWS S3 integration (#162836)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-26 07:39:50 +01:00
Michael Hansen
4863df00a1 Avoid invalid cache future state (#164081) 2026-02-25 22:36:53 -05:00
Maciej Bieniek
9fadfecf14 Bump accuweather to 5.1.0 (#164034) 2026-02-26 02:00:10 +01:00
Liquidmasl
dae7f73f53 Sonarr post merge changes (#164112) 2026-02-26 01:57:14 +01:00
Jamie Magee
c46d0382c3 Add diagnostics to aladdin_connect for easier troubleshooting (#164110) 2026-02-26 00:17:38 +01:00
Artur Pragacz
c21e9cb24c Fix Matter vacuum clean area status check (#164108) 2026-02-25 23:49:14 +01:00
David Bonnes
928732af40 Clean up evohome constants (#164102) 2026-02-25 20:23:17 +00:00
Franck Nijhof
51dc6d7c26 Bump version to 2026.4.0dev0 (#164101) 2026-02-25 21:08:17 +01:00
Przemko92
02972579aa Add Compit fan (#164049)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 20:52:01 +01:00
Denis Shulyaka
80574f7ae0 Change icon for Anthropic entities to mdi:asterisk (#164099)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 20:33:33 +01:00
Klaas Schoute
390b62551d Add PowerfoxPrivacyError handling for Powerfox integration (#164100) 2026-02-25 20:28:56 +01:00
Denis Shulyaka
17e0fd1885 Add Code execution tool to Anthropic (#164065) 2026-02-25 20:01:34 +01:00
Brett Adams
4eb3e77891 Remove redundant get_status call from Tessie coordinator (#163219)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:58:35 +01:00
Willem-Jan van Rootselaar
324ed65999 add codeowner to homevolt (#164097) 2026-02-25 19:46:41 +01:00
Maikel Punie
42428b91bb Bump velbusaio to 2026.2.0 (#164093) 2026-02-25 19:41:17 +01:00
Glenn de Haan
c41dd3e3a8 Bump hdfury to 1.6.0 (#164088) 2026-02-25 19:40:11 +01:00
Joost Lekkerkerker
02171a1da0 Add Zinvolt power sensor (#164092) 2026-02-25 18:58:25 +01:00
konsulten
19c7f663ca Add diagnostic to systemnexa2 integration (#164090) 2026-02-25 18:51:51 +01:00
Matthias Alphart
87bd04af5a Update knx-frontend to 2026.2.25.165736 (#164089) 2026-02-25 18:50:21 +01:00
Jamie Magee
5af6227ad7 Add action exceptions for cover commands in aladdin_connect (#164087) 2026-02-25 18:45:04 +01:00
Robert Resch
9b56f936fd Bump uv to 0.10.6 (#164086) 2026-02-25 18:36:07 +01:00
Joost Lekkerkerker
f2afd324d9 Make Zinvolt battery state a non diagnostic sensor (#164071) 2026-02-25 18:22:23 +01:00
Joost Lekkerkerker
173aab5233 Refresh coordinator in Zinvolt after setting value (#164069) 2026-02-25 18:19:58 +01:00
Joost Lekkerkerker
1d97729547 Use different name source in Zinvolt (#164072) 2026-02-25 18:18:52 +01:00
konsulten
91ca674a36 Add sensor platform to systemnexa2 (#163961) 2026-02-25 18:18:12 +01:00
Joost Lekkerkerker
6157802fb5 Set initiate flow for Zinvolt (#164054) 2026-02-25 18:18:10 +01:00
Joost Lekkerkerker
7e3b7a0c02 Add integration_type device to zerproc (#163998) 2026-02-25 18:17:56 +01:00
Joost Lekkerkerker
6a5455d7a5 Add integration_type device to wiffi (#163978) 2026-02-25 18:17:23 +01:00
Kamil Breguła
09765fe53d Fix AWS S3 config flow endpoint URL validation (#164085)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2026-02-25 18:17:04 +01:00
Felix Eckhofer
2fccbd6e47 dwd_weather_warnings: Filter expired warnings (#163096) 2026-02-25 18:16:44 +01:00
Jamie Magee
ef7cccbe3f Handle coordinator update errors in aladdin_connect (#164084) 2026-02-25 18:15:40 +01:00
Jamie Magee
a704c2d44b Add parallel updates to aladdin_connect (#164082) 2026-02-25 18:06:43 +01:00
Robert Resch
f12c5b627d Remove building wheels for Python 3.13 (#164083) 2026-02-25 18:05:32 +01:00
Bram Kragten
b241054a96 Update frontend to 20260225.0 (#164076) 2026-02-25 17:55:00 +01:00
Erik Montnemery
0fd515404d Fix smarla test snapshots (#164078) 2026-02-25 17:50:06 +01:00
Erik Montnemery
52382b7fe5 Fix ntfy test snapshots (#164079) 2026-02-25 17:49:46 +01:00
Thomas D
209af5dccc Adjust service description for Volvo integration (#164073) 2026-02-25 17:46:34 +01:00
Liquidmasl
227d2e8de6 Sonarr coordinator refactor (#164077)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-25 17:46:18 +01:00
Erwin Douna
96d50565f9 Portainer optimize switch (#163520)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-25 17:39:49 +01:00
Tom
80fc3691d8 Align airOS add_entities consumption in sensor (#164055)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-25 17:25:51 +01:00
Christian Lackas
15e00f6ffa Add siren support for HmIP-MP3P (Combination Signalling Device) (#161634)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-25 17:16:56 +01:00
Brett Adams
f25b437832 Add quality scale to Tessie integration (#160499)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Tom <CoMPaTech@users.noreply.github.com>
2026-02-25 17:10:41 +01:00
Franck Nijhof
2e34d4d3a6 Add brands system integration to proxy brand images through local API (#163960)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 17:10:28 +01:00
Liquidmasl
b81b12f094 Sonarr service calls instead of sensor attributes (#161199)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 17:09:06 +01:00
Erwin Douna
7446d5ea7c Add reconfigure flow to Fully Kiosk (#161840) 2026-02-25 17:08:43 +01:00
Matt Zimmerman
7b811cddce Use has_entity_name in SmartTub entities (#162374)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-25 16:45:48 +01:00
Paul Bottein
19545f29dc Use show in sidebar property instead of removing panel title and icon (#164025) 2026-02-25 16:37:15 +01:00
Jamie Magee
e591291cbe Add platform tests for aladdin_connect cover and sensor (#164011) 2026-02-25 16:20:19 +01:00
Joost Lekkerkerker
cb990823cd Improve platforms pylint plugin (#164067) 2026-02-25 16:15:28 +01:00
Willem-Jan van Rootselaar
2cfafc04ce Bump python-bsblan to 5.1.0 (#164064) 2026-02-25 15:57:07 +01:00
Ludovic BOUÉ
0563037c5a Fix MatterValve state handling and allow None values for attributes (#164066) 2026-02-25 15:57:05 +01:00
Joost Lekkerkerker
70f5f2c1ee Add binary sensor platform to Zinvolt (#164050) 2026-02-25 15:38:53 +01:00
Robin Lintermann
c5b31d6782 Add Update Platform to Smarla Integration (#163255) 2026-02-25 15:36:48 +01:00
Joost Lekkerkerker
925bcea1c0 Add number platform to Zinvolt (#164058) 2026-02-25 15:30:45 +01:00
Manu
01f0e4fe48 Add update platform to ntfy integration (#164018)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 15:28:47 +01:00
mettolen
f9a61e5412 Mark docs-examples done for Liebherr integration (#163034) 2026-02-25 15:26:08 +01:00
Andreas Jakl
caf40f9d25 Add diagnostics to NRGkick integration (#164047) 2026-02-25 15:20:34 +01:00
Manu
89c5511558 Improve configuration url in Uptime Kuma (#164057)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 15:02:05 +01:00
Joost Lekkerkerker
fc79e0cbfa Bump zinvolt to 0.3.0 (#164046) 2026-02-25 14:56:21 +01:00
Thomas D
317f95ff0f Add a service to retrieve images for the Volvo integration (#159603)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-25 14:41:03 +01:00
Manu
0cb34d2888 Categorize update entity as diagnostic in Uptime Kuma (#164022) 2026-02-25 14:14:03 +01:00
Manu
b8df61fc5f Categorize update entity as diagnostic in IronOS integration (#164023) 2026-02-25 14:13:40 +01:00
epenet
44a4be012d Use constants in counter tests (#164020) 2026-02-25 14:13:24 +01:00
Joost Lekkerkerker
8dcaed62b5 Add base entity to Zinvolt (#164051) 2026-02-25 14:12:32 +01:00
epenet
195e55097b Drop single-use service name constants in Renault (#164043) 2026-02-25 13:16:20 +01:00
Tom Quist
910f501194 Fix ingress compression breaking SSE and streaming responses (#160704) 2026-02-25 12:58:12 +01:00
kang
f0edfbf053 Enrich DeviceInfo with meter metadata in route_b_smart_meter (#164006)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-25 11:49:52 +01:00
epenet
834227a762 Use constants in calendar test (#164021) 2026-02-25 10:51:58 +01:00
Ludovic BOUÉ
3426846361 Add CLEAN_AREA feature to Matter vacuum entity (#163570)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: Artur Pragacz <artur@pragacz.com>
2026-02-25 10:47:47 +01:00
Artur Pragacz
50f39621e9 Add vacuum area mapping not configured issue (#163965) 2026-02-25 10:45:44 +01:00
epenet
dc133bf7cc Move Tuya helpers to external library (#158791) 2026-02-25 10:35:12 +01:00
TheJulianJES
3219417a7d Bump ZHA to 1.0.0 (#164013) 2026-02-25 09:51:30 +01:00
Joost Lekkerkerker
9a23a518ed Add integration_type device to ws66i (#163987) 2026-02-25 09:50:16 +01:00
Joost Lekkerkerker
7e62852723 Add integration_type hub to watts (#163973) 2026-02-25 09:45:33 +01:00
Zhephyr
0a1027391f Add pet last seen flap device id and user id sensors to Sure Petcare (#160215) 2026-02-25 08:59:22 +01:00
Allen Porter
7644fc4325 Update MCP client integration to use new OAuth spec (#161611)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-24 23:18:25 -08:00
Yangqian Yan
2f80720730 Add Full support for roborock Zeo washing/drying machines (#159575)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 23:17:56 -08:00
Joost Lekkerkerker
644c74f311 Add integration_type hub to zwave_me (#164000) 2026-02-25 07:29:36 +01:00
Joost Lekkerkerker
29370add66 Add integration_type service to zamg (#163997) 2026-02-25 07:28:32 +01:00
Joost Lekkerkerker
fc4680ad86 Add integration_type device to youless (#163996) 2026-02-25 07:28:11 +01:00
Joost Lekkerkerker
174076ba76 Add integration_type hub to yolink (#163995) 2026-02-25 07:27:37 +01:00
Joost Lekkerkerker
f3590bd9cf Add integration_type device to yeelight (#163994) 2026-02-25 07:27:08 +01:00
Joost Lekkerkerker
ae7f71219f Add integration_type device to yardian (#163993) 2026-02-25 07:26:27 +01:00
Joost Lekkerkerker
e1529620db Add integration_type hub to yale (#163989) 2026-02-25 07:25:20 +01:00
Joost Lekkerkerker
9a56d30924 Add integration_type hub to yale_smart_alarm (#163990) 2026-02-25 07:24:54 +01:00
Joost Lekkerkerker
d6df2b3c4c Add integration_type device to yamaha_musiccast (#163992) 2026-02-25 07:24:28 +01:00
Joost Lekkerkerker
9740dc65aa Add integration_type device to yalexs_ble (#163991) 2026-02-25 07:23:47 +01:00
Joost Lekkerkerker
b914971531 Add integration_type device to wolflink (#163982) 2026-02-25 07:23:14 +01:00
Joost Lekkerkerker
9007c65b50 Add integration_type hub to wilight (#163979) 2026-02-25 07:22:35 +01:00
Joost Lekkerkerker
a4a2847b03 Add integration_type hub to weheat (#163977) 2026-02-25 07:21:04 +01:00
Joost Lekkerkerker
9a11db2ad5 Add integration_type service to weatherkit (#163976) 2026-02-25 07:20:32 +01:00
Joost Lekkerkerker
2d445f8f53 Add integration_type hub to weatherflow_cloud (#163975) 2026-02-25 07:20:06 +01:00
Joost Lekkerkerker
f07c386529 Add integration_type device to watergate (#163972) 2026-02-25 07:18:54 +01:00
Joost Lekkerkerker
3cd79581dc Add integration_type hub to zimi (#163999) 2026-02-25 07:07:50 +01:00
Joost Lekkerkerker
e82df86dda Add integration_type hub to xiaomi_aqara (#163988) 2026-02-25 07:07:25 +01:00
Joost Lekkerkerker
1629d2b204 Add integration_type service to worldclock (#163986) 2026-02-25 07:07:05 +01:00
Joost Lekkerkerker
a6e60d8b73 Add integration_type hub to withings (#163980) 2026-02-25 07:06:45 +01:00
Joost Lekkerkerker
ef6650548e Add integration_type service to waze_travel_time (#163974) 2026-02-25 07:06:14 +01:00
Manu
52a2e94fc4 Bump aiontfy to 0.8.1 (#164010) 2026-02-25 07:05:36 +01:00
Klaas Schoute
6bba7e7583 Bump powerfox to v2.1.1 (#164004) 2026-02-25 02:14:27 +01:00
MizterB
58e8a8d398 Ecobee username/password authentication (#161716)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-25 01:41:36 +01:00
Klaas Schoute
6b0303a1ef Set quality scale to platinum for Powerfox Local integration (#164003) 2026-02-25 01:22:27 +01:00
Klaas Schoute
249e6c2f3d Add reconfiguration flow for Powerfox Local integration (#164002) 2026-02-25 01:05:30 +01:00
Simone Chemelli
7ae0380b33 Update IQS to gold for UptimeRobot (#162926) 2026-02-25 01:05:17 +01:00
Tom
889faa5a5c Add v6 firmware support to airOS (#163889)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-25 01:02:26 +01:00
Klaas Schoute
9b810c64d9 Add diagnostics support for Powerfox Local integration (#163985) 2026-02-25 00:24:33 +01:00
Joost Lekkerkerker
1e3bed9864 Add integration_type device to wiz (#163981) 2026-02-25 00:04:34 +01:00
Tom
eac3fb651e Update airOS quality_scale (#163895)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 23:47:18 +01:00
Karl Beecken
8b285239f0 Update Teltonika IQS to silver (#163943) 2026-02-24 23:37:46 +01:00
Andreas Jakl
d0a74ad539 Update quality scale to silver for nrgkick integration (#163964) 2026-02-24 23:35:06 +01:00
Andreas Jakl
0f071c1ae5 Fix accessing optional username and password for nrgkick integration (#163963) 2026-02-24 23:33:40 +01:00
mettolen
e671e4408b Implement dynamic devices for Liebherr integration (#163951)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-24 23:32:51 +01:00
Klaas Schoute
697441969b Add reauthentication flow for Powerfox Local integration (#163966) 2026-02-24 23:29:19 +01:00
nic
bc324a1a6e Add ZoneMinder integration test suite (#163115)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 23:27:13 +01:00
Robin Lintermann
e505ad9003 Update availability of entities when connection changes (#163252) 2026-02-24 23:25:57 +01:00
Jan Čermák
6a91771f04 Use native ARM runner for builder action, update to builder 2026.02.1 (#163942) 2026-02-24 23:14:50 +01:00
Christian Lackas
e7df4356f4 Fix HmIP-RGBW monochrome mode FEATURE_NOT_SUPPORTED error (#161917) 2026-02-24 22:38:47 +01:00
Luke Lashley
a41207d369 Implement changes for Clean area for Roborock. (#163956) 2026-02-24 22:34:56 +01:00
cdheiser
28e8d7c3eb Add tests to lutron (#162055)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 22:30:31 +01:00
mettolen
e514faf0bc Fix Saunum session parameters to use timedelta (#163962) 2026-02-24 22:14:09 +01:00
Erwin Douna
7894a80728 Proxmox separate errors and patch tests (#163922) 2026-02-24 22:08:50 +01:00
Przemko92
6751f6f4a2 Add sensor for compit integration (#161527) 2026-02-24 21:49:47 +01:00
Erwin Douna
ce0dd0eb7b Fix small typo in Portainer containers (#163957) 2026-02-24 21:45:34 +01:00
Erwin Douna
7cb595f768 Add sensor platform to Proxmox (#163404) 2026-02-24 21:45:07 +01:00
Kamil Breguła
dfbd4ffb2d Add diagnostics to met (#157805)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-24 21:34:54 +01:00
Willem-Jan van Rootselaar
6abefc852d Add quality scale to bsblan integration (#146323)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 21:30:41 +01:00
konsulten
9ba28150e9 Add light platform to systemnexa2 (#163710)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 21:29:46 +01:00
Erwin Douna
adfe4f2b62 Add stack management to Portainer (#163612)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 21:28:16 +01:00
Christian Lackas
dc3dc116d2 Handle 403 authentication errors in HomematicIP Cloud (#162579) 2026-02-24 21:21:29 +01:00
Willem-Jan van Rootselaar
f16e7aaec4 bugfix tests to use model_validate_json for device time (#163950) 2026-02-24 21:20:03 +01:00
A. Gideonse
ea68152f32 Add select platform to Indevolt integration (#163955) 2026-02-24 21:18:43 +01:00
wollew
c75c9d9dd8 Add diagnostics to Velux integration (#163896) 2026-02-24 21:17:56 +01:00
rlippmann
4760f9b8eb Restart SimpliSafe websocket after request failures (#160974)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 21:11:12 +01:00
Denis Shulyaka
9bb879e061 Fix API key check during config flow for openai_conversation (#163025) 2026-02-24 20:53:19 +01:00
Blake Messer
f2c87f96a2 Bump pyrainbird to 6.1.0 (#163919) 2026-02-24 20:48:33 +01:00
Denis Shulyaka
30fffafceb Add STT support for OpenAI (#162931)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 19:32:13 +01:00
Thomas55555
ff916a783b Disable seconds in Husqvarna Automower services (#163948) 2026-02-24 19:24:10 +01:00
Maciej Bieniek
0fcfc3f070 Bump imgw_pib to 2.0.2 (#163940) 2026-02-24 19:15:41 +01:00
Przemko92
413506276c Add binary sensor for Compit (#161709) 2026-02-24 18:58:15 +01:00
Willem-Jan van Rootselaar
4a4e077d40 Add button platform for BSB-Lan integration (#160243) 2026-02-24 18:52:33 +01:00
Robin Lintermann
8f824b566e Add reauthentication flow to smarla (#163250) 2026-02-24 18:52:03 +01:00
Willem-Jan van Rootselaar
610aaa6eee Update BSB-LAN strings, error handling, and code cleanup (#163480) 2026-02-24 18:09:32 +01:00
Martin Arndt
ecb7ab238c Allow worxlandroid PIN to contain letters (#163266) 2026-02-24 18:07:15 +01:00
Simone Chemelli
9013b7835e Resolve pylance complaints for Fritz (#163313) 2026-02-24 18:06:19 +01:00
Erwin Douna
5363638c7e OAuth helper enhance response text logger (#163371)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-02-24 16:50:40 +01:00
Andreas Jakl
164b1cbb8c Add reconfiguration flow to NRGkick (#163828) 2026-02-24 16:46:23 +01:00
Mattias Michaux
b5a55ec032 Fix Sonos browse album art lookup for multi-segment A:ALBUM IDs (#163786) 2026-02-24 16:45:27 +01:00
Karl Beecken
0c6d635e83 Teltonika quality scale: mark unavailable rules done (#163705) 2026-02-24 16:43:48 +01:00
Christian Lackas
9259db0b85 Centralize ViCare error handling in base entity class (#162619) 2026-02-24 16:43:16 +01:00
Denis Shulyaka
6f1a021197 Add IQS to Anthropic (#163891) 2026-02-24 16:27:51 +01:00
Christian Lackas
8dbf7f7ad7 Add diagnostics support to homematicip_cloud (#163829) 2026-02-24 16:25:04 +01:00
Jamie Magee
3854c8e261 Econet friedrich support (#163904)
Co-authored-by: w1ll1am23 <6432770+w1ll1am23@users.noreply.github.com>
2026-02-24 16:20:35 +01:00
On Freund
7adfb0a40b Add bus support to MTA integration (#163220)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 16:11:13 +01:00
Zoltán Farkasdi
b4705e4a45 Fix flaky netatmo test (#163941) 2026-02-24 16:02:00 +01:00
Tom
a0176d18cf Add DHCP ip_addresses update to airOS (#163936) 2026-02-24 15:36:52 +01:00
Kevin Stillhammer
5543107f6c Allow to disable seconds in DurationSelector (#163803) 2026-02-24 15:11:26 +01:00
Klaas Schoute
6dc8840932 Rename Powerfox integration to Powerfox Cloud (#163723) 2026-02-24 14:42:43 +01:00
Stefan Agner
76902aa7fa Avoid adding Content-Type to non-body responses (#163885) 2026-02-24 14:31:04 +01:00
Erwin Douna
07b9877f64 Add button platform to Proxmox (#163791)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-24 14:24:20 +01:00
Erik Montnemery
40e2f79e60 Add support for reading backups using securetar v3 (#163920) 2026-02-24 14:23:00 +01:00
Christopher Fenner
aa707fcf41 Add gateway discovery via USB for EnOcean integration (#162756)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-24 11:58:01 +01:00
Willem-Jan van Rootselaar
4b53bc243d Add energy sensor to bsblan (#163879) 2026-02-24 11:56:27 +01:00
Robert Resch
220e94d029 Fix nightlies by reverting the builder to a version instead of a sha (#163935) 2026-02-24 11:48:19 +01:00
Erik Montnemery
b1f943ccda Replace discovery with user flow in Philips Hue BLE (#163924) 2026-02-24 11:06:31 +01:00
Brett Adams
e37d84049a Update Splunk integration to bronze quality scale (#163616)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-24 10:56:05 +01:00
Marc Mueller
209473e376 Remove myself as codeowner for fritzbox_callmonitor (#163927) 2026-02-24 10:45:58 +01:00
MoonDevLT
334c3af448 Bump lunatone-rest-api-client to 0.7.0 (#163594) 2026-02-24 10:10:04 +01:00
hanwg
5560139d24 Clean up duplicated code in Telegram bot (#163917) 2026-02-24 10:04:21 +01:00
Erik Montnemery
d4dec5d1d3 Improve backup_restore tests (#163921) 2026-02-24 10:03:42 +01:00
J. Nick Koston
6cb63a60bc Skip unknown entity types in ESPHome integration (#163887) 2026-02-24 08:48:27 +01:00
Franck Nijhof
991301e79e Merge branch 'master' into dev 2026-02-24 07:07:39 +00:00
andreimoraru
06e2b4633a Bump yt-dlp to 2026.2.21 (#163916) 2026-02-24 07:30:54 +01:00
Manu
048d8d217c Update strings in ntfy integration (#163912) 2026-02-24 06:24:18 +01:00
Kyle Johnson
3693bc5878 Make Google Assistant fan speed percent and step speeds mutually exclusive (#162770) 2026-02-23 22:26:09 +00:00
Denis Shulyaka
af9ea5ea7a Bump anthropic to 0.83.0 (#163899) 2026-02-23 21:43:07 +00:00
Robert Resch
977d29956b Add clean_area support for Ecovacs mqtt vacuums (#163580) 2026-02-23 22:42:25 +01:00
Jamie Magee
fc9bdb3cb1 Bring aladdin_connect to Bronze quality scale (#163221)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-23 22:16:51 +01:00
Erwin Douna
bb1956c738 Portainer Platinum score (#163898) 2026-02-23 22:15:59 +01:00
J. Nick Koston
9212279c2c Bump aioesphomeapi 44.1.0 (#163894) 2026-02-23 22:14:40 +01:00
Denis Shulyaka
7e162cfda2 Update Anthropic models (#163897) 2026-02-23 22:13:31 +01:00
Tom Matheussen
5611b4564f Add debounce to Satel Integra alarm panel state (#163602) 2026-02-23 21:57:39 +01:00
Manu
1a16674f86 Update quality scale of Xbox integration to platinum 🏆️ (#155577)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 21:56:05 +01:00
Paul Tarjan
bae4de3753 Add Hikvision integration quality scale (#159252)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 21:53:22 +01:00
mettolen
8f2bfa1bb0 Add select entities to Liebherr integration (#163581) 2026-02-23 21:52:50 +01:00
Manu
fb118ed516 Add support for action buttons to ntfy integration (#152014) 2026-02-23 21:46:00 +01:00
Markus Adrario
bea84151b1 homee: add one-button-remote to event platform (#163690) 2026-02-23 21:42:08 +01:00
Markus Adrario
d581d65c8b Add handling of 2 IP addresses to homee (#162731)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 21:36:49 +01:00
Erwin Douna
bc1837d09d Portainer gold standard review (#155231)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-23 21:34:06 +01:00
Daniel Hjelseth Høyer
9cc3c850aa Homevolt switch platform (#163415)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-02-23 21:16:43 +01:00
Markus
8927960fca fix(snapcast): do not crash when stream is not found (#162439)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-23 21:09:14 +01:00
Erwin Douna
49b8232260 Add stale device removal to portainer (#160017)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 21:05:52 +01:00
Barry vd. Heuvel
1d5e8a9e5a Weheat energy logs update (#163621)
Co-authored-by: Jesper Raemaekers <jesper.raemaekers@wefabricate.com>
2026-02-23 21:00:35 +01:00
dvdinth
501e095578 Add IntelliClima Select platform (#163637)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-23 20:41:41 +01:00
Jeef
dc5eab6810 Allow support of Graph QL 4.0 / Bump pytibber 0.36.0 (#163305)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 20:41:05 +01:00
Manu
25787d2b75 Add DeviceInfo to Google Translate (#163762) 2026-02-23 20:29:49 +01:00
Denis Shulyaka
e57613af65 Anthropic interleaved thinking (#163583)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 20:24:40 +01:00
Erwin Douna
89ff86a941 Add diagnostics to Proxmox (#163800)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-23 20:17:49 +01:00
Brett Adams
c62ceee8fc Update Teslemetry quality scale to silver (#163611)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-23 20:12:38 +01:00
J. Nick Koston
d732e3d5ae Add climate platform to Trane Local integration (#163571)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:03:08 +01:00
Denis Shulyaka
dd78da929e Improve config flow tests for Anthropic (#163757) 2026-02-23 19:15:46 +01:00
Christopher Fenner
c2b74b7612 Correct EnOcean integration type (#163725) 2026-02-23 19:11:12 +01:00
Tom
6570b413d4 Add discovery for airOS devices (#154568)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 18:59:50 +01:00
Christian Lackas
ea7732e9ee Add heat pump sensors to ViCare integration (#161422) 2026-02-23 18:54:12 +01:00
TheJulianJES
4c885e7ce8 Fix ZHA number entity not using device class and mode (#163827) 2026-02-23 18:53:58 +01:00
Christian Lackas
67395f1cf5 Handle PyViCare device communication and server errors in ViCare integration (#162618) 2026-02-23 18:53:00 +01:00
Joost Lekkerkerker
a552266bfc Bump python-overseerr to 0.9.0 (#163883) 2026-02-23 18:52:56 +01:00
Bouwe Westerdijk
e6c2d54232 Improve Plugwise set_hvac_mode() logic (#163713) 2026-02-23 18:52:29 +01:00
Willem-Jan van Rootselaar
994eae8412 Bump python-bsblan to 5.0.1 (#163840) 2026-02-23 18:50:49 +01:00
Abílio Costa
b712207b75 Add refrigerator temperature level select to whirlpool (#162110)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-23 18:45:48 +01:00
wollew
fa38f25d4f Enable strict typing in Velux integration (#163798) 2026-02-23 18:05:50 +01:00
Karl Beecken
3a27fa782e Teltonika quality scale: mark test-coverage done (#163707) 2026-02-23 18:03:11 +01:00
Nathan Spencer
ffeb759aba Rename Litter-Robot integration to Whisker (#163826) 2026-02-23 17:46:15 +01:00
Denis Shulyaka
e96da42996 Fix notification service exceptions fot Telegram bot (#163882) 2026-02-23 17:40:22 +01:00
Tom
ce71e540ae Add airOS device reboot button (#163718)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-23 17:37:24 +01:00
Steve Easley
9b2bcaed92 Bump Kaleidescape integration dependency to v1.1.3 (#163884) 2026-02-23 17:36:44 +01:00
Ludovic BOUÉ
f564ad3ebe Add Matter KNX bridge fixture (#163875) 2026-02-23 17:30:51 +01:00
Joost Lekkerkerker
bd1b060718 Add integration_type device to solarlog (#163628) 2026-02-23 17:26:26 +01:00
Willem-Jan van Rootselaar
f4cab72228 Minor type fixes (#163606) 2026-02-23 17:26:07 +01:00
Leo Periou
733d381a7c Add new MyNeomitis integration (#151377)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 17:14:30 +01:00
Ingo Fischer
6fba886edb Replace Matter python client (#163704) 2026-02-23 17:02:39 +01:00
epenet
2f95d1ef78 Mark lock entity type hints as mandatory (#163796) 2026-02-23 16:50:52 +01:00
jesperraemaekers
6d6727ed58 Change weheat codeowner (#163860) 2026-02-23 16:49:41 +01:00
epenet
9c0c9758f0 Mark light entity type hints as mandatory (#163794) 2026-02-23 16:48:30 +01:00
epenet
bfa2da32fc Mark geo_location entity type hints as mandatory (#163790) 2026-02-23 16:48:12 +01:00
Paul Bottein
dfb17c2187 Add configurable panel properties to frontend (#162742)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2026-02-23 16:15:44 +01:00
Klaas Schoute
ac65163ebb Bump forecast-solar to v5.0.0 (#163841) 2026-02-23 15:58:54 +01:00
Sab44
f3042741bf Deprecate Libre Hardware Monitor versions below v0.9.5 (#163838) 2026-02-23 15:57:17 +01:00
Joost Lekkerkerker
80936497ce Add Zinvolt integration (#163449) 2026-02-23 15:55:15 +01:00
Joost Lekkerkerker
5e3d2bec68 Add integration_type device to sia (#163393) 2026-02-23 15:18:54 +01:00
Michael
e1667bd5c6 Increase request timeout from 10 to 20s in FRITZ!SmartHome (#163818) 2026-02-23 15:02:10 +01:00
Ludovic BOUÉ
cdb92a54b0 Fix Matter speaker mute toggle (#161128)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 14:38:37 +01:00
Erik Montnemery
74a3f4bbb9 Bump securetar to 2026.2.0 (#163226) 2026-02-23 14:03:43 +01:00
Nic Eggert
6299e8cb77 Add support for current sensors to egauge integration (#163728) 2026-02-23 13:29:20 +01:00
Joost Lekkerkerker
0f6a3a8328 Add integration_type service to snapcast (#163401) 2026-02-23 13:17:31 +01:00
Joost Lekkerkerker
77a56a3e60 Add integration_type device to smart_meter_texas (#163398) 2026-02-23 13:17:02 +01:00
Joost Lekkerkerker
cf5733de97 Add integration_type device to tilt_pi (#163667) 2026-02-23 13:16:37 +01:00
Joost Lekkerkerker
fe377befa6 Add integration_type hub to wallbox (#163752) 2026-02-23 13:12:40 +01:00
Joost Lekkerkerker
9d54236f7d Add integration_type hub to waqi (#163754) 2026-02-23 13:12:11 +01:00
Karl Beecken
bd6b8a812c Teltonika integration: add reauth config flow (#163712) 2026-02-23 13:07:19 +01:00
kshypachov
85eeac6812 Fix Matter energy sensor discovery when value is null (#162044)
Co-authored-by: Ludovic BOUÉ <lboue@users.noreply.github.com>
2026-02-23 11:52:05 +01:00
Robert Resch
ea71c40b0a Bump deebot-client to 18.0.0 (#163835) 2026-02-23 11:45:55 +01:00
Ludovic BOUÉ
99bd66194d Add allow_none_value=True to MatterDiscoverySchema for electrical power attributes (#163195)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2026-02-23 11:33:57 +01:00
Sab44
13737ff2e6 Bump librehardwaremonitor-api to version 1.10.1 (#163572) 2026-02-23 11:01:58 +01:00
Tom
55c1d52310 Bump airOS to 0.6.4 (#163716) 2026-02-23 09:12:45 +01:00
hanwg
d5ef379caf Refactoring for Telegram bot (#163767) 2026-02-23 08:35:39 +01:00
Ludovic BOUÉ
a5d59decef Ikea bilresa dual button fixture (#163781)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 08:33:52 +01:00
Nathan Spencer
c75c5c0773 Adjust buttons to support new Litter-Robot lineup (#163825) 2026-02-23 08:32:33 +01:00
Nathan Spencer
f1fc6d10ad Adjust selects to support new Litter-Robot lineup (#163824) 2026-02-23 08:31:59 +01:00
Nathan Spencer
c3376df227 Adjust sensors to support new Litter-Robot lineup (#163823) 2026-02-23 08:30:46 +01:00
epenet
463003fc33 Add test for Tuya event (#163812) 2026-02-23 08:28:23 +01:00
Michael
b9b45c9994 Bump pyfritzhome to 0.6.20 (#163817) 2026-02-23 08:25:35 +01:00
Sebastiaan Speck
eed3b9fb89 Bump renault-api to 0.5.5 (#163821) 2026-02-23 07:35:37 +01:00
Erwin Douna
88d7954d7c Typing fix for Proxmox coordinator (#163808) 2026-02-23 06:58:43 +01:00
andarotajo
ce2afd85d4 Remove myself as code owner from dwd_weather_warnings (#163810) 2026-02-23 06:54:59 +01:00
Raphael Hehl
be96606b2c Bump uiprotect to 10.2.1 (#163816) 2026-02-23 01:05:23 +01:00
Maciej Bieniek
5afad9cabc Use async_add_executor_job in Fitbit to prevent event loop blocking (#163815) 2026-02-22 22:35:12 +01:00
epenet
19b606841d Mark fan entity type hints as mandatory (#163789) 2026-02-22 21:44:53 +01:00
Maciej Bieniek
abdd51c266 Allow unit of measurement translation in Analytics Insights (#163811) 2026-02-22 20:56:27 +01:00
Norbert Rittel
959bafe78b Fix grammar of amcrest.ptz_control action description (#163802) 2026-02-22 19:47:13 +01:00
Raphael Hehl
383f9c203d Unifiprotect ptz support (#161353)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-02-22 10:48:22 -06:00
Harry Heymann
b5d8c1e893 Require product_id for Inovelli LED intensity Matter Number entities (#163680) 2026-02-22 17:47:59 +01:00
epenet
11edd214a1 Improve type hints in igloohome lock (#163795) 2026-02-22 17:13:14 +01:00
Norbert Rittel
15d0241158 Replace "add-on" with "app" in zwave_me (user-facing strings only) (#163703) 2026-02-22 17:12:27 +01:00
Norbert Rittel
309b439744 Replace "add-on" with "app" in recorder (#163714) 2026-02-22 17:11:00 +01:00
Norbert Rittel
49f7c24601 Replace "add-on" with "app" in homeassistant_yellow (#163715) 2026-02-22 17:10:27 +01:00
Ludovic BOUÉ
9f25b4702d Remove CumulativeEnergyExported in fixtures where not needed (#163775) 2026-02-22 17:09:49 +01:00
epenet
a312f9f5bc Improve type hints in lights (#163792) 2026-02-22 17:08:42 +01:00
Marc Mueller
d767a1ca65 Update pillow to 12.1.1 (#163773) 2026-02-22 10:06:08 -06:00
Marc Mueller
d04fb59d56 Update sqlparse to 0.5.5 (#163774) 2026-02-22 10:05:45 -06:00
Marc Mueller
00e441b90d Update pylint to 4.0.5 (#163777) 2026-02-22 10:05:20 -06:00
Aidan Timson
e1fd60aa18 Bump systembridgeconnector to 5.4.3 (#163784) 2026-02-22 10:04:46 -06:00
Luke Lashley
8c41e21b7f Bump python-robroock to 4.17.1 (#163765)
Co-authored-by: Ludovic BOUÉ <lboue@users.noreply.github.com>
2026-02-22 07:44:29 -08:00
Ludovic BOUÉ
b7fd1276aa Roborock: Q7 Model Split and Refactor (#163769)
Co-authored-by: Luke Lashley <conway220@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-22 07:32:11 -08:00
David Bonnes
12d06e80ad Rename evohome's test_evo_services.py to test_services.py (#163731) 2026-02-22 14:10:59 +01:00
Simone Chemelli
a377907fd6 Buomp aiovodafone to 3.1.2 (#163779) 2026-02-22 13:58:05 +01:00
Joost Lekkerkerker
16f4f5d54f Add integration_type device to volumio (#163751) 2026-02-22 10:52:43 +01:00
Joost Lekkerkerker
70585d1e23 Add integration_type device to vilfo (#163748) 2026-02-22 10:51:19 +01:00
Joost Lekkerkerker
2f82c3127d Add integration_type device to venstar (#163745) 2026-02-22 10:50:30 +01:00
Joost Lekkerkerker
af4d9cfac8 Add integration_type hub to vera (#163747) 2026-02-22 10:49:00 +01:00
Joost Lekkerkerker
a9abeb6ca5 Add integration_type device to v2c (#163742) 2026-02-22 10:48:24 +01:00
Joost Lekkerkerker
539ad6bf2b Add integration_type hub to uhoo (#163737) 2026-02-22 10:47:59 +01:00
Joost Lekkerkerker
f3e5cf0e56 Add integration_type device to twinkly (#163735) 2026-02-22 10:47:14 +01:00
Joost Lekkerkerker
d4e40b77cf Add integration_type hub to vegehub (#163744) 2026-02-22 10:46:02 +01:00
Joost Lekkerkerker
953391d9d9 Add integration_type service to uptimerobot (#163741) 2026-02-22 10:45:43 +01:00
Joost Lekkerkerker
4f7edb3c3c Add integration_type service to upcloud (#163740) 2026-02-22 10:45:16 +01:00
Joost Lekkerkerker
6aa4b9cefb Add integration_type service to ukraine_alarm (#163738) 2026-02-22 10:44:52 +01:00
Joost Lekkerkerker
ca01cf1150 Add integration_type service to twilio (#163734) 2026-02-22 10:44:29 +01:00
Joost Lekkerkerker
93ed79008b Add integration_type service to twitch (#163736) 2026-02-22 10:44:08 +01:00
Luke Lashley
429249f3f0 Add support for clean_area to Roborock V1 vacuums (#163760)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-21 19:41:20 -08:00
Joost Lekkerkerker
4fc627a7d8 Add integration_type service to vlc_telnet (#163750) 2026-02-22 00:04:43 +00:00
Joost Lekkerkerker
7c954e9997 Add integration_type device to vivotek (#163749) 2026-02-22 00:00:24 +00:00
Joost Lekkerkerker
95f89df6f4 Add integration_type device to vallox (#163743) 2026-02-21 23:59:53 +00:00
Tim Laing
5bffc14574 Bump pyicloud version to 2.4.1 in manifest and requirements files (#163722) 2026-02-21 23:55:45 +00:00
Ludovic BOUÉ
0e439583a6 Bump python-roborock to 4.15.0 in manifest and requirements files (#163719) 2026-02-21 09:43:06 -08:00
Christopher Fenner
666f6577e6 Bump PyViCare to 2.58.0 (#163686) 2026-02-21 18:09:28 +01:00
Andreas Jakl
ae9f2e6046 NRGkick integration: add reauth config flow (#163619) 2026-02-21 16:43:38 +01:00
Abílio Costa
9b4d209361 Add translated reasons to Govee Light Local setup failures (#163576) 2026-02-21 13:55:34 +01:00
Erwin Douna
99ca425ad0 Bump pyportainer 1.0.28 (#163700) 2026-02-21 11:53:03 +01:00
Josef Zweck
452b0775ee Revert "Replace "add-on" with "app" in zwave_me" (#163701) 2026-02-21 11:13:23 +01:00
Norbert Rittel
dc5caf307b Replace "add-on" with "app" in zwave_me (#163698) 2026-02-21 11:04:45 +01:00
Norbert Rittel
686bcb3199 Replace "add-on" with "app" in homeassistant_hardware (#163696) 2026-02-21 11:04:17 +01:00
hanwg
047d5735d8 Cleanup error handling for Telegram bot (#163689) 2026-02-21 09:19:06 +01:00
Nathan Spencer
c3b0f7ba55 Bump pylitterbot to 2025.1.0 (#163691) 2026-02-21 09:17:59 +01:00
Manu
11f0cd690e Bump aiontfy to 0.8.0 (#163693) 2026-02-21 09:16:14 +01:00
Norbert Rittel
048fbba36d Replace "add-on" with "app" in matter (#163695) 2026-02-21 08:54:49 +01:00
epenet
a791797a6f Mark entity icon type hints as mandatory (#163617) 2026-02-20 22:48:56 +01:00
Franck Nijhof
9c640fe0fa 2026.2.3 (#163683) 2026-02-20 21:43:32 +01:00
Erwin Douna
aa2bb44f0e Bump pyportainer 1.0.27 (#163613)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-02-20 21:33:13 +01:00
Sid
62145e5f9e Bump eheimdigital to 1.6.0 (#161961) 2026-02-20 19:51:10 +00:00
Franck Nijhof
c0fc414bb9 Fix nrgkick tests for rc 2026-02-20 19:49:27 +00:00
Franck Nijhof
69411a05ff Bump version to 2026.2.3 2026-02-20 19:39:05 +00:00
Marc Mueller
06c9ec861d Fix hassfest requirements check (#163681) 2026-02-20 19:38:58 +00:00
Joost Lekkerkerker
946df1755f Bump pySmartThings to 3.5.3 (#163375)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-20 19:38:56 +00:00
Thomas Sejr Madsen
d0678e0641 Fix touchline_sl zone availability when alarm state is set (#163338) 2026-02-20 19:38:55 +00:00
Allen Porter
ec56f183da Bump pyrainbird to 6.0.5 (#163333) 2026-02-20 19:38:53 +00:00
Åke Strandberg
033005e0de Add Miele dishwasher program code (#163308) 2026-02-20 19:38:52 +00:00
Andreas Jakl
91f9f5a826 NRGkick: do not update vehicle connected timestamp when vehicle is not connected (#163292) 2026-02-20 19:38:51 +00:00
David Recordon
ac4fcab827 Fix Control4 HVAC action mapping for multi-stage and idle states (#163222) 2026-02-20 19:38:49 +00:00
Allen Porter
d0eea77178 Fix remote calendar event handling of events within the same update period (#163186)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-20 19:38:48 +00:00
Markus Adrario
fb38fa3844 Add Lux to homee units (#163180) 2026-02-20 19:38:47 +00:00
Allen Porter
440efb953e Bump ical to 13.2.0 (#163123) 2026-02-20 19:38:45 +00:00
Manu
7ce47cca0d Fix blocking call in Xbox config flow (#163122) 2026-02-20 19:38:44 +00:00
Andre Lengwenus
a5f607bb91 Bump pypck to 0.9.11 (#163043) 2026-02-20 19:38:42 +00:00
Andre Lengwenus
b03043aa6f Bump pypck to 0.9.10 (#162333) 2026-02-20 19:38:41 +00:00
Robert Resch
0f3c7ca277 Block redirect to localhost (#162941) 2026-02-20 19:37:03 +00:00
Martin Hjelmare
3abf7c22f3 Fix Z-Wave climate set preset (#162728) 2026-02-20 19:37:01 +00:00
hbludworth
292e1de126 Show progress indicator during backup stage of Core/App update (#162683) 2026-02-20 19:37:00 +00:00
Christian Lackas
2d776a8193 Fix HomematicIP entity recovery after access point cloud reconnect (#162575) 2026-02-20 19:36:58 +00:00
Sid
039bbbb48c Fix dynamic entity creation in eheimdigital (#161155) 2026-02-20 19:36:56 +00:00
Luke Lashley
ad5565df95 Add the ability to select region for Roborock (#160898) 2026-02-20 19:36:55 +00:00
Marc Mueller
6ecbaa979a Fix hassfest requirements check (#163681) 2026-02-20 20:36:07 +01:00
epenet
6115a4c1fb Use shorthand attributes in swiss_hydrological_data (#163607) 2026-02-20 19:56:39 +01:00
Joost Lekkerkerker
f6459453ed Add integration_type hub to surepetcare (#163646) 2026-02-20 19:51:02 +01:00
epenet
eeb7ce3725 Improve type hints in homematic hub (#163614) 2026-02-20 19:49:23 +01:00
Joost Lekkerkerker
f020948e2d Add integration_type hub to tradfri (#163673)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-20 19:48:55 +01:00
Joost Lekkerkerker
0711176f9c Add integration_type device to tilt_ble (#163666) 2026-02-20 19:48:35 +01:00
Joost Lekkerkerker
cd26901386 Add integration_type service to todoist (#163668) 2026-02-20 19:46:51 +01:00
Joost Lekkerkerker
3c1b7ada9a Add integration_type device to tolo (#163670) 2026-02-20 19:46:36 +01:00
Joost Lekkerkerker
debf07e3fc Add integration_type device to toon (#163671) 2026-02-20 19:46:07 +01:00
Joost Lekkerkerker
541cc808b0 Add integration_type hub to totalconnect (#163672) 2026-02-20 19:45:21 +01:00
Joost Lekkerkerker
46b0eaecf6 Add integration_type service to trafikverket_camera (#163674) 2026-02-20 19:43:48 +01:00
Joost Lekkerkerker
35e770b998 Add integration_type service to trafikverket_ferry (#163675) 2026-02-20 19:43:14 +01:00
Joost Lekkerkerker
ed9ad950d9 Add integration_type service to trafikverket_train (#163676) 2026-02-20 19:42:55 +01:00
Joost Lekkerkerker
02058afb10 Add integration_type service to trafikverket_weatherstation (#163677) 2026-02-20 19:42:39 +01:00
Joost Lekkerkerker
3f6bfa96fc Add integration_type hub to tellduslive (#163661) 2026-02-20 19:41:34 +01:00
Joost Lekkerkerker
430f064243 Add integration_type device to tesla_wall_connector (#163662) 2026-02-20 19:40:20 +01:00
Joost Lekkerkerker
08adb88c6b Add integration_type device to thermobeacon (#163663) 2026-02-20 19:39:43 +01:00
Joost Lekkerkerker
14b6269dbf Add integration_type device to thermopro (#163664) 2026-02-20 19:39:05 +01:00
Joost Lekkerkerker
19b1fc6561 Add integration_type hub to tibber (#163665) 2026-02-20 19:37:34 +01:00
Joost Lekkerkerker
b6e83d22e3 Add integration_type device to syncthru (#163658) 2026-02-20 19:36:19 +01:00
Joost Lekkerkerker
7cd48ef079 Add integration_type device to tami4 (#163659) 2026-02-20 19:35:29 +01:00
Joost Lekkerkerker
2a03d95bcd Add integration_type service to telegram_bot (#163660) 2026-02-20 19:34:39 +01:00
Joost Lekkerkerker
e7e8c7a53a Add integration_type device to togrill (#163669) 2026-02-20 19:11:57 +01:00
Joost Lekkerkerker
6ce28987ab Add integration_type service to syncthing (#163651) 2026-02-20 16:27:23 +01:00
Joost Lekkerkerker
da537ddb8b Add integration_type device to steamist (#163640) 2026-02-20 16:26:59 +01:00
Joost Lekkerkerker
03f81e4a09 Add integration_type hub to starline (#163638) 2026-02-20 16:25:58 +01:00
Joost Lekkerkerker
88bc6165b5 Add integration_type device to starlink (#163639) 2026-02-20 16:25:33 +01:00
Joost Lekkerkerker
a1f35ed3c4 Add integration_type hub to switcher_kis (#163650) 2026-02-20 17:23:57 +02:00
Joost Lekkerkerker
c15a804ab4 Add integration_type service to srp_energy (#163636) 2026-02-20 16:23:39 +01:00
Joost Lekkerkerker
34f1c4cbe0 Add integration_type device to soundtouch (#163634) 2026-02-20 16:23:00 +01:00
Joost Lekkerkerker
bf950e4916 Add integration_type service to splunk (#163635) 2026-02-20 16:22:33 +01:00
Joost Lekkerkerker
47eba50b4a Add integration_type service to sonarr (#163632) 2026-02-20 16:22:07 +01:00
Joost Lekkerkerker
8ff06f3c72 Add integration_type hub to soma (#163630) 2026-02-20 16:21:35 +01:00
Joost Lekkerkerker
d2918586f9 Add integration_type device to solax (#163629) 2026-02-20 16:21:09 +01:00
Joost Lekkerkerker
8c3e72b53d Add integration_type device to snooz (#163627) 2026-02-20 16:20:31 +01:00
Joost Lekkerkerker
3143d9c4fd Add integration_type hub to snoo (#163626) 2026-02-20 16:20:01 +01:00
Joost Lekkerkerker
04621a2e58 Add integration_type hub to switchbee (#163648) 2026-02-20 16:19:28 +01:00
Joost Lekkerkerker
9b6e6a688d Add integration_type service to swiss_public_transport (#163647) 2026-02-20 16:18:57 +01:00
Joost Lekkerkerker
2bf5f67ecd Add integration_type service to suez_water (#163644) 2026-02-20 16:18:20 +01:00
Joost Lekkerkerker
522f63cdab Add integration_type hub to sunricher_dali (#163645) 2026-02-20 16:18:03 +01:00
Joost Lekkerkerker
03f5e6d6a3 Add integration_type device to songpal (#163633) 2026-02-20 16:17:47 +01:00
Joost Lekkerkerker
c2ba5d87d5 Add integration_type hub to subaru (#163643) 2026-02-20 16:17:03 +01:00
Joost Lekkerkerker
6a9fd67e05 Add integration_type hub to somfy_mylink (#163631) 2026-02-20 16:16:35 +01:00
Joost Lekkerkerker
69db5787ec Add integration_type device to stiebel_eltron (#163641) 2026-02-20 16:15:39 +01:00
Joost Lekkerkerker
8a38bace90 Add integration_type service to streamlabswater (#163642) 2026-02-20 16:15:05 +01:00
epenet
d6f3079518 Use shorthand attributes in london_air (#163601) 2026-02-20 11:49:48 +01:00
epenet
f80e1dd25b Use shorthand attributes in homematic (#163610) 2026-02-20 11:49:04 +01:00
epenet
4937c6521b Add type hint for icon property (#163609) 2026-02-20 11:43:44 +01:00
epenet
cff5a12d5f Use shorthand attributes in reddit (#163600) 2026-02-20 11:43:23 +01:00
epenet
63e4eaf79e Use shorthand attributes in netdata (#163605) 2026-02-20 11:41:56 +01:00
epenet
eccaac4e94 Use shorthand attributes in rmvtransport (#163599) 2026-02-20 11:38:11 +01:00
epenet
5d818cd2ba Use shorthand attributes in transport_nsw (#163598) 2026-02-20 11:37:40 +01:00
epenet
12591a95c6 Use shorthand attributes in torque (#163597) 2026-02-20 11:33:18 +01:00
epenet
1110ca5dc6 Use shorthand attributes in geonetnz_volcano (#163596) 2026-02-20 11:32:45 +01:00
Brett Adams
2a6f6ef684 Add reconfiguration flow to Splunk integration (#163577)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-20 09:13:32 +01:00
Manu
c173505f76 Add state_class to PlayStation Network sensors (#163591) 2026-02-20 09:10:58 +01:00
Manu
201b31c18a Add state_class to Xbox sensors (#163590) 2026-02-20 08:31:26 +01:00
Manu
cb63c1d435 Impprove oauth2 exception handling in Xbox (#163588) 2026-02-20 08:31:10 +01:00
Brett Adams
6abff84f23 Add exception translations for Splunk setup errors (#163579) 2026-02-20 00:19:03 +01:00
Patrick Vorgers
0996ad4d1d Add pagination support for IDrive e2 (#162960) 2026-02-19 22:42:04 +01:00
wollew
e8885de8c2 add number platform to Velux integration for ExteriorHeating nodes (#162857) 2026-02-19 19:58:13 +01:00
J. Nick Koston
03d9c2cf7b Add Trane Local integration (#163301) 2026-02-19 12:39:58 -06:00
epenet
7f3583587d Combine matter snapshot tests (#162695)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-19 19:38:33 +01:00
Brett Adams
e009440bf9 Mark action-setup quality scale rule as done for Advantage Air (#163208)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:25:41 +01:00
Noah Husby
43dccf15ba Add room correction intensity to Cambridge Audio (#163306) 2026-02-19 19:25:14 +01:00
Josef Zweck
c647ab1877 Add proper ImplementationUnvailable handling to onedrive for business (#163258)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-19 19:24:31 +01:00
JannisPohle
6b395b2703 Add test for device_class inheritance in the min/max integration (#161123) 2026-02-19 19:18:41 +01:00
Thomas Sejr Madsen
882a44a1c2 Fix touchline_sl zone availability when alarm state is set (#163338) 2026-02-19 19:13:44 +01:00
Christopher Fenner
3c9a505fc3 Handle gateway issues during setup in EnOcean integration (#163168)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-19 17:53:29 +00:00
Sab44
b2679ddc42 Update json fixture to reflect response from current LHM versions (#163248) 2026-02-19 18:15:16 +01:00
Andrew Jackson
2055082993 Handle Mastodon auth fail in coordinator (#163234) 2026-02-19 18:14:14 +01:00
Andreas Jakl
6f49f9a12a NRGkick: do not update vehicle connected timestamp when vehicle is not connected (#163292) 2026-02-19 18:08:50 +01:00
Petar Petrov
36c560b7bf Add flow rate (stat_rate) tracking for gas and water (#163274) 2026-02-19 18:08:16 +01:00
hanwg
05abe7efe0 Add callback inline keyboard tests for Telegram bot (#163328) 2026-02-19 17:50:51 +01:00
Manu
865ec96429 Add notify platform to HTML5 integration (#163229) 2026-02-19 17:50:04 +01:00
epenet
e6dbed0a87 Use shorthand attributes in geonetnz_quakes (#163568)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 17:46:37 +01:00
A. Gideonse
a3fd2f692e Add switch platform to Indevolt integration (#163522)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 17:46:13 +01:00
konsulten
eb7e00346d Fixing minor case errors in strings for systemnexa2 (#163567) 2026-02-19 17:39:00 +01:00
Manu
77159e612e Improve error handling in Uptime Kuma (#163477) 2026-02-19 17:23:10 +01:00
mettolen
05f9e25f29 Pump pyliebherrhomeapi to 0.3.0 (#163450) 2026-02-19 17:10:10 +01:00
Denis Shulyaka
7fa51117a9 Update Anthropic repair flow (#163303) 2026-02-19 17:09:09 +01:00
epenet
9e87fa75f8 Mark entity capability/state attribute type hints as mandatory (#163300)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-19 17:02:38 +01:00
epenet
0188f2ffec Mark is_on property as mandatory in binary sensors and toggle entities (#163556) 2026-02-19 17:01:50 +01:00
epenet
c144aec03e Use shorthand attributes in opple light (#163519) 2026-02-19 15:50:15 +01:00
epenet
1cb44aef64 Use shorthand attributes in pilight (#163542) 2026-02-19 15:50:00 +01:00
epenet
900f2300ad Use shorthand attributes in eufy light (#163521) 2026-02-19 15:49:48 +01:00
epenet
b075fba594 Use shorthand attributes in greenwave light (#163526) 2026-02-19 15:49:33 +01:00
epenet
c2ba97fb79 Use shorthand attributes in futurenow light (#163523) 2026-02-19 15:49:16 +01:00
epenet
d0a373aecc Use shorthand attributes in lw12wifi light (#163532) 2026-02-19 15:48:56 +01:00
epenet
758225edad Use shorthand attributes in scsgate light (#163537) 2026-02-19 15:48:43 +01:00
epenet
8ab1a527a4 Use shorthand attributes in rflink (#163555) 2026-02-19 15:48:05 +01:00
epenet
c7582b2f25 Use shorthand attributes in mystrom binary sensor (#163518) 2026-02-19 15:29:39 +01:00
epenet
91b8a67ce2 Use shorthand attributes in scsgate switch (#163510) 2026-02-19 15:23:20 +01:00
epenet
2b13ff98da Use shorthand attributes in itach remote (#163516) 2026-02-19 15:07:50 +01:00
epenet
fd2d9c2ee2 Use shorthand attributes in raincloud (#163515) 2026-02-19 14:56:52 +01:00
Manu
61b5466dcc Add state_class to sensors in Uptime Kuma (#163495) 2026-02-19 14:54:29 +01:00
epenet
bc4af64bea Use shorthand attributes in pencom switch (#163509) 2026-02-19 14:54:00 +01:00
epenet
3323f84c22 Use shorthand attributes in hikvisioncam switch (#163504) 2026-02-19 14:47:10 +01:00
epenet
b1f48a5886 Use shorthand attributes in kankun switch (#163505) 2026-02-19 14:46:55 +01:00
epenet
a14b1db886 Use shorthand attribute in eufy switch (#163503) 2026-02-19 14:46:22 +01:00
epenet
9de89b923e Use shorthand attributes in orvibo switch (#163508) 2026-02-19 14:46:07 +01:00
epenet
21cf5dc321 Use shorthand attribute in elv switch (#163488) 2026-02-19 14:30:27 +01:00
epenet
fe32582233 Use shorthand attribute in edimax switch (#163487) 2026-02-19 14:30:10 +01:00
epenet
6ebf19c4ba Use shorthand attribute in danfoss_air switch (#163486) 2026-02-19 14:29:39 +01:00
Willem-Jan van Rootselaar
5794189f8d Add strict typing for BSB-Lan integration (#163236) 2026-02-19 14:19:10 +01:00
A. Gideonse
c336e58afc Add numbers platform to Indevolt integration (#163298)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-19 13:55:50 +01:00
Manu
cdad602af0 Add new sensor to Uptime Kuma (#163468) 2026-02-19 13:53:04 +01:00
Stefan Agner
520046cd82 Ignore WAKEUP_CHANNEL addition in Thread dataset with same timestamp (#163440)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:42:37 +01:00
Willem-Jan van Rootselaar
e0b2ff0b2a Bump python-bsblan version to 4.2.1 (#163439) 2026-02-19 13:41:31 +01:00
epenet
6164198bde Use shorthand attributes in versasense switch (#163442) 2026-02-19 13:40:41 +01:00
epenet
dd41b4cefd Use shorthand attribute in tellstick toggle entities (#163443) 2026-02-19 13:40:09 +01:00
epenet
ccb8d6af44 Use shorthand attribute in x10 light (#163444) 2026-02-19 13:39:55 +01:00
epenet
6e8c064474 Improve type hints in tesla_wall_connector binary sensor (#163445) 2026-02-19 13:39:35 +01:00
epenet
7079eda8d9 Improve type hints in philips_js light (#163448) 2026-02-19 13:39:20 +01:00
Brett Adams
4e3832758b Add charge cable and charge port latch sensors to Tessie (#163207)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 13:38:55 +01:00
Brett Adams
773c3c4f07 Add diagnostics support to Splunk integration (#163453) 2026-02-19 13:38:17 +01:00
konsulten
b73beba152 System Nexa 2 Core Integration (#159140)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-19 13:31:17 +01:00
epenet
82589b613d Fix pytest warnings in screenlogic (#163455) 2026-02-19 12:57:55 +01:00
J. Diego Rodríguez Royo
c9b5f5f2c1 Use a coordinator per appliance in Home Connect (#152518)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 12:35:19 +01:00
Erwin Douna
725b45db7f Add config URL to Proxmox (#163414)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 12:31:44 +01:00
Pierre PÉRONNET
b194741a13 Add custom headers support to downloader (#160541)
Signed-off-by: Pierre PÉRONNET <pierre.peronnet@gmail.com>
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-02-19 12:29:30 +01:00
epenet
4615b4d104 Add return type hint to is_on property (#163441) 2026-02-19 11:24:38 +01:00
A. Gideonse
2c7d9cb62e Bump indevolt-api requirement to 1.2.3 (#163429) 2026-02-19 11:22:50 +01:00
AlCalzone
e229ba591a Use opening/closing state for Z-Wave covers (#163368)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-19 10:41:52 +01:00
Rob Bierbooms
7914ebe54e Add config flow to InfluxDB integration (#134463)
Co-authored-by: Ariel Ebersberger <ariel@ebersberger.io>
2026-02-19 10:33:32 +01:00
Andreas Jakl
3abaa99706 Add charge control to NRGkick integration (new number platform) (#163273)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-19 10:31:09 +01:00
karwosts
86d7fdfe1e Allow history_stats to configure state_class: total_increasing (#148637) 2026-02-19 10:16:47 +01:00
epenet
676c42d578 Refactor write_ha_state logic in Tuya (#163431) 2026-02-19 10:13:54 +01:00
Manu
39909b7493 Bump pythonkuma to 0.5.0 (#163430) 2026-02-19 09:57:31 +01:00
Manu
6aef9a99e6 Deprecate action call without config entry in DuckDNS integration (#163269) 2026-02-19 08:43:46 +01:00
Joost Lekkerkerker
ff036f38a0 Add integration_type hub to sharkiq (#163392) 2026-02-19 08:31:40 +01:00
On Freund
53e3b4caf0 Bump py-nymta to 0.4.0 (#163418) 2026-02-19 08:30:49 +01:00
Kamil Breguła
dbdc030b74 Enable strict typing for 10 components (#163420)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-19 08:30:24 +01:00
Kamil Breguła
ee0b24f808 Add sensor showing total size of AWS S3 backups (#162513)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-19 08:29:40 +01:00
Joost Lekkerkerker
c0fd8ff342 Add integration_type hub to smappee (#163397) 2026-02-19 08:15:25 +01:00
Joost Lekkerkerker
84d2ec484d Add integration_type device to slimproto (#163396) 2026-02-19 08:14:47 +01:00
Joost Lekkerkerker
844b20e2fc Add integration_type hub to sleepiq (#163395) 2026-02-19 08:14:05 +01:00
Joost Lekkerkerker
2bd07e6626 Add integration_type hub to sensorpush_cloud (#163390) 2026-02-19 08:09:49 +01:00
johanzander
b91c07b2af Fix midnight bounce suppression for Growatt today sensors (#163106)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2026-02-19 09:07:52 +02:00
rhcp011235
37f0f1869f Add sleep health metrics to SleepIQ integration (#163403)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 01:02:43 +01:00
Manu
2fcbd77c95 Don't set last notification timestamp when sending message failed (#163251) 2026-02-19 00:48:01 +01:00
Josef Zweck
b398197c07 Debug logging for config_entries (#163378) 2026-02-19 00:46:06 +01:00
Joost Lekkerkerker
cd5775ca35 Add integration_type service to simplepush (#163394) 2026-02-19 00:37:17 +01:00
Christian Lackas
fafa193549 Add LED light support for WiredPushButton (HmIPW-WRC2/WRC6) (#161841)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-19 00:36:29 +01:00
elgris
ca4d537529 Control datetime on SwitchBot Meter Pro CO2 (#161808) 2026-02-19 00:32:23 +01:00
torben-iometer
e9be363f29 add support for multi tariff meter data in iometer (#161767)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-19 00:23:46 +01:00
Joshua Leaper
0f874f7f03 Add Config Flow for Ness Alarm (#162414)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 00:16:08 +01:00
Brett Adams
14b147b3f7 Mark Splunk dependency-transparency quality scale rule as done (#163355)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-02-19 00:11:10 +01:00
Brett Adams
8a1909e5d8 Bump hass-splunk to 0.1.4 (#163413) 2026-02-18 22:51:31 +00:00
Noah Husby
1fd873869f Bump aiostreammagic to 2.13.0 (#163408) 2026-02-18 22:49:18 +00:00
Robert Resch
3b7b3454d8 Simplify ecovacs unload and register teardown before initialize (#163350) 2026-02-18 23:32:39 +01:00
Josef Zweck
c7276621eb Add metadata validation for missing backup files in OneDrive backup agent (#163072) 2026-02-18 23:32:23 +01:00
Klaas Schoute
6be1e4065f Add Powerfox Local integration (#163302) 2026-02-18 23:27:47 +01:00
Artur Pragacz
ba547c6bdb Add channel muting switches to Onkyo (#162605)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-18 23:26:57 +01:00
mettolen
be25603b76 Refactor optimistic update and delayed refresh for Liebherr integration (#163121) 2026-02-18 23:11:47 +01:00
Joost Lekkerkerker
2e0f727981 Add integration_type hub to senz (#163391) 2026-02-18 23:11:29 +01:00
Joost Lekkerkerker
122bc32f30 Add integration_type device to sensorpush (#163389) 2026-02-18 23:11:01 +01:00
Brett Adams
723825b579 Mark runtime-data quality as exempt in Splunk (#163359)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:06:49 +01:00
rhcp011235
5f6b446195 Migrate SleepIQ sensors to entity descriptions (#163213)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 23:03:53 +01:00
Joost Lekkerkerker
f59f14fe40 Add integration_type device to sensorpro (#163386) 2026-02-18 21:49:12 +01:00
Joost Lekkerkerker
ab9b13302c Add integration_type hub to smarttub (#163399) 2026-02-18 21:47:19 +01:00
Joost Lekkerkerker
f74fdd7605 Add integration_type service to smhi (#163400) 2026-02-18 21:46:18 +01:00
Erwin Douna
f7628b87c8 Add ConfigEntryAuthFailed to Proxmox (#163407) 2026-02-18 21:43:04 +01:00
Karl Beecken
3e31fbfee0 Deduplicate strings in Teltonika integration (#163410) 2026-02-18 21:42:34 +01:00
Norbert Rittel
477797271a Replace "the" with "a" in vacuum action descriptions (#163409) 2026-02-18 21:41:00 +01:00
Andrew Jackson
9f2677ddd8 Add Mastodon mute/unmute actions (#163366) 2026-02-18 19:50:25 +01:00
Manu
558a49cb66 Fix data update in WebhookFlowHandler to preserve existing entry data (#163372) 2026-02-18 19:48:37 +01:00
Stefan Agner
a9b64a15e6 Redact Thread dataset and format them as readable dicts in log messages (#163385)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 19:41:36 +01:00
Andrew Jackson
0a734b7426 Improve Transmission error handling (#163388) 2026-02-18 19:41:28 +01:00
Steve Easley
8df41dc73f Bump Kaleidescape integration dependancy to v1.1.1 (#163384) 2026-02-18 19:41:17 +01:00
Glenn de Haan
e9039cec24 Add HDFury number platform (#163381)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-18 19:22:57 +01:00
Joost Lekkerkerker
15cb102c39 Bump pySmartThings to 3.5.3 (#163375)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-18 18:28:57 +01:00
Anthony Hou
30314ec88e Fix 0°C when the temperature is unavailable in HKO API (#162052) 2026-02-18 17:16:00 +00:00
rhcp011235
428aa31749 Update asyncsleepiq to 1.7.0 (#163214) 2026-02-18 16:44:02 +00:00
Joost Lekkerkerker
0170d56893 Add fixture to SmartThings (#163374) 2026-02-18 17:30:41 +01:00
epenet
eb7d973252 Ignore None keys in meteo_france extra state attributes (#163297) 2026-02-18 17:18:27 +01:00
epenet
e3c98dcd09 Use shorthand attributes in wirelesstag (#161214) 2026-02-18 17:14:06 +01:00
epenet
9c71aea622 Refactor extra_state_attributes in xiaomi_aqara (#163299) 2026-02-18 17:12:06 +01:00
epenet
21978917b9 Mark siren/stt/todo method type hints as mandatory (#163265) 2026-02-18 17:10:11 +01:00
puddly
3b6a5b2c79 Fix uses of reconfigure and re-configure in ZHA (#163377) 2026-02-18 11:05:05 -05:00
Ivan Dlugos
68792f02d4 Fix XMLParsedAsHTMLWarning in scrape integration (#159433)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-02-18 17:00:49 +01:00
Josef Zweck
bfea04b482 Mark onedrive for business as platinum (#163376) 2026-02-18 16:53:07 +01:00
Erwin Douna
dc553f20e6 Ecovacs controller pattern optimization (#160895)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Robert Resch <robert@resch.dev>
2026-02-18 16:49:34 +01:00
Manu
5631170900 Fix spelling of reconfigure in strings (#163370) 2026-02-18 16:36:31 +01:00
David Recordon
60d4b050ac Fix Control4 HVAC action mapping for multi-stage and idle states (#163222) 2026-02-18 16:35:57 +01:00
Josef Zweck
c5e261495f Add diagnostics to onedrive for business (#163336) 2026-02-18 16:35:32 +01:00
Erwin Douna
d1a1183b9a OAuth2.0 token request error handling (#153167)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-02-18 15:36:53 +01:00
Manu
4dcfd5fb91 Reconfiguration support for webhook flow helper (#151729) 2026-02-18 15:31:48 +01:00
Jochen Friedrich
680f7fac1c Fix MySensors battery sensors attachment to correct gateway (#151167)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-02-18 14:29:47 +01:00
Artur Pragacz
7a41ce1fd8 Add clean_area action to vacuum (#149315)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2026-02-18 14:13:08 +01:00
Erwin Douna
937b4866c3 Proxmox polish strings & tests (#163361) 2026-02-18 14:10:16 +01:00
Artur Pragacz
151e075e28 Do not send empty snapshots in analytics (#163351) 2026-02-18 13:45:45 +01:00
Erwin Douna
8094cfc404 Add coordinator to Proxmox (#161146) 2026-02-18 13:37:53 +01:00
Allen Porter
b26483e09e Fix remote calendar event handling of events within the same update period (#163186)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-18 12:52:35 +01:00
Brett Adams
728de32d75 Add missing data_description for reauth_confirm token in Splunk (#163356)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:43:44 +01:00
MoonDevLT
8de1e3d27b Change lunatone config entry title to only include the URL (#162855) 2026-02-18 12:27:25 +01:00
Tom Matheussen
cabf3b7ab9 Set last_reported timestamp for Satel Integra entities (#163352) 2026-02-18 12:04:30 +01:00
theobld-ww
f0e22cca56 Reconfiguration flow Watts Vision + and platinium level (#163346) 2026-02-18 11:55:27 +01:00
Karl Beecken
294a3e5360 add teltonika integration (#157539)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-18 11:18:50 +01:00
Nic Eggert
fdd753e70c Add support for voltage sensors to eGauge integration (#163206) 2026-02-18 08:44:01 +01:00
epenet
392fc7ff91 Use shorthand attributes in osramlightify (#163296) 2026-02-18 08:35:28 +01:00
Allen Porter
d777c1c542 Bump pyrainbird to 6.0.5 (#163333) 2026-02-18 08:19:38 +01:00
dependabot[bot]
fa71fd3992 Bump actions/stale from 10.1.1 to 10.2.0 (#163223) 2026-02-18 07:46:11 +01:00
Jamie Magee
19f6340546 Bump victron-ble-ha-parser to 0.4.10 (#163310) 2026-02-17 15:57:56 -05:00
Allen Porter
479cb7f1e1 Allow Gemini CLI and Anti-gravity SKILL discovery (#163194) 2026-02-17 21:50:38 +01:00
Manu
d50d914928 Update quality scale of Namecheap DynamicDNS integration to platinum 🏆️ (#161682) 2026-02-17 20:02:23 +00:00
Abílio Costa
551a71104e Bump Idasen Desk dependency (#163309) 2026-02-17 19:41:27 +00:00
Åke Strandberg
65cf61571a Add Miele dishwasher program code (#163308) 2026-02-17 19:36:58 +00:00
Simone Chemelli
58ac3d2f45 Type fixture in Fritz tests (#163271) 2026-02-17 18:32:35 +01:00
christian9712
654e132440 ADS Light Color Temperature Support (#153913)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 17:26:19 +01:00
hbludworth
4af60ef3b9 Show progress indicator during backup stage of Core/App update (#162683) 2026-02-17 17:24:06 +01:00
Josef Zweck
2fc9ded6b7 Add sensors to onedrive_for_business (#163135) 2026-02-17 17:15:49 +01:00
karwosts
9f551f3d5b Improve derivative units and auto-device_class (#157369) 2026-02-17 08:08:59 -08:00
epenet
0b8312d942 Use shorthand attributes in serial (#163287) 2026-02-17 17:05:48 +01:00
epenet
413e297022 Use shorthand attributes in tank_utility (#163288)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-17 17:05:04 +01:00
epenet
f7752686df Use shorthand attributes in sony_projector (#163293) 2026-02-17 16:01:42 +00:00
epenet
1313960893 Use shorthand attributes in skybeacon (#163295) 2026-02-17 15:59:53 +00:00
epenet
d298eb033a Use shorthand attributes in vasttrafik (#163285) 2026-02-17 16:58:36 +01:00
epenet
398a6222cd Remove deprecated starline state attribute (#163289) 2026-02-17 16:44:43 +01:00
epenet
7168e2df5a Use shorthand attributes in repetier (#163291) 2026-02-17 16:42:10 +01:00
epenet
3b3c081703 Use shorthand attributes in sigfox (#163286) 2026-02-17 16:41:02 +01:00
epenet
889467e4c2 Use shorthand attributes in openhardwaremonitor (#163284) 2026-02-17 16:35:58 +01:00
epenet
6a3bace824 Use shorthand attributes in hp_ilo (#163282) 2026-02-17 16:35:39 +01:00
epenet
523b527486 Use shorthand attributes in omnilogic (#163283) 2026-02-17 16:19:35 +01:00
epenet
b44900532f Ensure DOMAIN constant is always aliased with _DOMAIN suffix (#163270) 2026-02-17 16:10:11 +01:00
theobld-ww
bd45232972 Translation keys for exceptions Watts Vision + integration (#163231)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-17 16:07:53 +01:00
epenet
e7aa0ae398 Add type hints to extra_state_attributes [m-z] (#163281) 2026-02-17 16:00:42 +01:00
epenet
1d41e24653 Add type hints to extra_state_attributes [a-l] (#163279) 2026-02-17 16:00:18 +01:00
Wendelin
049a910494 Fix frontend development PR download cache (#162928) 2026-02-17 15:55:39 +01:00
A. Gideonse
f6f52005fe Add Indevolt integration (#160595)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-17 15:51:55 +01:00
epenet
b23c402d0a Improve haveibeenpwned type hints (#163280) 2026-02-17 15:48:14 +01:00
Sid
91c36fcdf6 Fix dynamic entity creation in eheimdigital (#161155) 2026-02-17 15:47:56 +01:00
epenet
ff2f0ac320 Mark RestoreEntity/RestoreSensor type hints as mandatory (#163272) 2026-02-17 15:34:16 +01:00
Denis Shulyaka
c205785f4f Add quality scale to Anthropic (#162953) 2026-02-17 15:20:57 +01:00
Joost Lekkerkerker
59dad4c935 Add DHCP Discovery for SmartThings (#160314) 2026-02-17 15:15:42 +01:00
epenet
d61f7d8170 Use shorthand attributes in geo_rss_events (#163268) 2026-02-17 14:39:16 +01:00
epenet
b6e7a55cd1 Rename DOMAIN_xxx aliases in tests (#163261) 2026-02-17 14:37:17 +01:00
epenet
163a6805eb Rename DOMAIN_xxx aliases in components (#163260) 2026-02-17 14:35:25 +01:00
epenet
637accbfff Rename DOMAIN_xxx aliases in template (#163259) 2026-02-17 14:34:48 +01:00
epenet
98b8e152e3 Use shorthand attributes in currencylayer (#163267) 2026-02-17 14:25:00 +01:00
Simone Chemelli
d12816d297 Removed more warnings from Fritz tests (#163262) 2026-02-17 14:08:02 +01:00
Tom Matheussen
58e4a42a1b Add coordinator for Satel Integra (#158533)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-02-17 12:55:00 +00:00
epenet
fdad9873e4 Mark weather method type hints as mandatory (#163247) 2026-02-17 13:23:53 +01:00
epenet
0337988be8 Improve type hints in meteoclimatic weather (#163244) 2026-02-17 13:03:17 +01:00
Brett Adams
ba695b5bd9 Add quality scale to Splunk (#162893) 2026-02-17 12:46:46 +01:00
Simone Chemelli
c114ea2666 Fix warning in Fritz switch tests (#163256) 2026-02-17 12:31:31 +01:00
epenet
82148e46f5 Rename DOMAIN aliases in tests (#163254) 2026-02-17 12:15:36 +01:00
epenet
34a78f9251 Rename DOMAIN aliases (#163253) 2026-02-17 12:15:03 +01:00
Willem-Jan van Rootselaar
f1c142b3d3 Refactor BSB-Lan tests (#163245) 2026-02-17 11:40:25 +01:00
Josef Zweck
68c82c2f90 Debug logging for service calls (#163235) 2026-02-17 11:23:54 +01:00
epenet
487e2f8ccc Improve type hints in tomorrowio weather (#163246) 2026-02-17 11:07:48 +01:00
Josef Zweck
6322185206 Bump onedrive-personal-sdk to 0.1.4 (#163238) 2026-02-17 10:56:32 +01:00
epenet
7f65db260f Improve type hints in meteo_france weather (#163243) 2026-02-17 10:55:51 +01:00
epenet
6c50711e2b Improve type hints in ipma weather (#163242) 2026-02-17 10:55:20 +01:00
epenet
f0e7d099e6 Improve type hints in environment_canada weather (#163241) 2026-02-17 10:55:00 +01:00
epenet
6c0fb12189 Improve type hints in ecobee weather (#163240) 2026-02-17 10:54:33 +01:00
Simone Chemelli
8e14dc7b5a Cleanup for 100% coverage of entity for Fritz (#163237) 2026-02-17 10:46:15 +01:00
epenet
219b982ef5 Improve type hints in aemet weather (#163239) 2026-02-17 10:45:44 +01:00
Zoltán Farkasdi
307c6a4ce2 Netatmo doortag binary sensor addition (#160608) 2026-02-17 10:34:23 +01:00
Erik Montnemery
9b1812858b Remove unnecessary set up of other integration from automation tests (#163230) 2026-02-17 10:11:08 +01:00
Simone Chemelli
9c57be215f Add 100% coverage to helpers for Fritz (#162999) 2026-02-17 10:03:57 +01:00
Josef Zweck
cda6236099 Add full debug logs for coordinator failures (#163228) 2026-02-17 09:52:44 +01:00
epenet
e4c7262260 Use unique node_id in matter fixtures (#162779) 2026-02-17 09:31:42 +01:00
theobld-ww
0f648a7f9d Add diagnostics support for Watts Vision integration (#163177)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-17 09:23:44 +01:00
Erik Montnemery
e6b9c2f737 Raise in EntityComponent.async_prepare_reload on configuration error (#101267)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-02-17 07:42:46 +01:00
Andrej Friesen
e0f39e6392 Add Pressure Stall Information (PSI) to Systemmonitor integration (#151946)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-16 23:48:15 +01:00
Hai-Nam Nguyen
52d645e4bf Hypontech micro invertors support via Hyponcloud (#159442)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-16 23:38:44 +01:00
Brett Adams
e8f2493ed6 Fix common-modules quality scale for advantage_air (#163209)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-16 23:28:25 +01:00
elgris
ba62d95715 Control time display format on SwitchBot Meter Pro CO2 (#163008)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-16 22:58:09 +01:00
MarkGodwin
73fa9925c4 Add test coverage for tplink_omada update entities (#162549) 2026-02-16 22:17:56 +01:00
Jordan Rodgers
9ec456d28e Add port link speed sensor to UniFi integration (#162847) 2026-02-16 22:15:50 +01:00
johanzander
4974439850 Add on-grid discharge stop SOC control for Growatt MIN devices (#160634)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:00:55 +01:00
Thomas Rupprecht
5cf37afbf6 Add quality_scale with strict-typing done for SpaceAPI (#163003) 2026-02-16 21:47:48 +01:00
Andrew Jackson
76ebc134f3 Mealie add get shopping list items action (#163090)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-16 21:43:36 +01:00
Allen Porter
667a77502d Store nest media in a .cache subdirectory (#163200)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-16 21:43:27 +01:00
David Recordon
8c146624f9 Add Celsius Temperature Support for Control4 Integration (#163196) 2026-02-16 21:37:59 +01:00
mettolen
2418036798 Saunum integration fix: close client on unload (#163183) 2026-02-16 21:33:10 +01:00
Simone Chemelli
459996b760 Add 100% coverage of sensors for Fritz (#163005) 2026-02-16 21:30:52 +01:00
wollew
eec854386a bump pyvlx to 0.2.30 (#163203) 2026-02-16 21:06:07 +01:00
Manu
47d6e3e938 Refactor HTML5 integration to use aiohttp instead of requests (#163202) 2026-02-16 20:11:04 +01:00
Norbert Rittel
957c6039e9 Fix reboot_gateway action deprecation message in velux (#163201) 2026-02-16 19:43:28 +01:00
Erik Montnemery
c833cfa395 Don't mock out filesystem operations in backup_restore tests (#163172) 2026-02-16 19:11:36 +01:00
theobld-ww
9dc38eda9f Reauthentication flow for Watts Vision + integration (#163141) 2026-02-16 19:00:49 +01:00
Kamil Breguła
e49767d37a GIOS quality scale fixes to platinum (#162510)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:59:45 +01:00
wollew
e6c5e72470 add upper and lower shutter of Velux dualrollershutters as entities (#162998)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-16 18:57:45 +01:00
James
66dc566d3a Add zone temperature support to Daikin integration (#152642) 2026-02-16 17:44:38 +00:00
epenet
5bb7699df0 Mark water_heater method type hints as mandatory (#163190) 2026-02-16 18:35:09 +01:00
epenet
168dd36d66 Mark vacuum method type hints as mandatory (#163185) 2026-02-16 18:20:38 +01:00
epenet
66d8a5bc51 Improve type hints in econet water_heater (#163193) 2026-02-16 18:08:46 +01:00
Franck Nijhof
3e6bc29a6a 2026.2.2 (#162950) 2026-02-13 21:05:06 +01:00
Franck Nijhof
ec8067a5a8 Bump version to 2026.2.2 2026-02-13 19:25:16 +00:00
Josef Zweck
6f47716d0a Log remaining token duration in onedrive (#162933) 2026-02-13 19:24:25 +00:00
puddly
efba5c6bcc Bump ZHA to 0.0.90 (#162894) 2026-02-13 19:24:24 +00:00
Sammy [Andrei Marinache]
d10e78079f Add Miele TQ1000WP tumble dryer programs and program phases (#162871)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Åke Strandberg <ake@strandberg.eu>
2026-02-13 19:24:23 +00:00
Jon Seager
6d4581580f Bump pytouchlinesl to 0.6.0 (#162856) 2026-02-13 19:24:21 +00:00
Yoshi Walsh
0d9a41a540 Bump pydaikin to 2.17.2 (#162846) 2026-02-13 19:24:20 +00:00
Vicx
cd69e6db73 Bump slixmpp to 1.13.2 (#162837) 2026-02-13 19:24:19 +00:00
Xitee
1320367d0d Filter out transient zero values from qBittorrent alltime stats (#162821)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:24:18 +00:00
Joost Lekkerkerker
dfa4698887 Bump pySmartThings to 3.5.2 (#162809)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2026-02-13 19:24:17 +00:00
Robert Resch
b426115de7 Bump cryptography to 46.0.5 (#162783) 2026-02-13 19:24:15 +00:00
hanwg
fb79fa37f8 Fix bug in edit_message_media action for Telegram bot (#162762) 2026-02-13 19:24:14 +00:00
Simone Chemelli
6a5f7bf424 Fix image platform state for Vodafone Station (#162747)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-02-13 19:24:13 +00:00
Simone Chemelli
142ca6dec1 Fix alarm refresh warning for Comelit SimpleHome (#162710) 2026-02-13 19:24:12 +00:00
epenet
0f986c24d0 Fix unavailable status in Tuya (#162709) 2026-02-13 19:24:11 +00:00
Josef Zweck
01f2b7b6f6 Bump onedrive-personal-sdk to 0.1.2 (#162689) 2026-02-13 19:24:09 +00:00
Michael
b9469027f5 Fix handling when FRITZ!Box reboots in FRITZ!Box Tools (#162679) 2026-02-13 19:24:08 +00:00
Tomás Correia
fbb94af748 fix to cloudflare r2 setup screen info (#162677) 2026-02-13 19:24:07 +00:00
Michael
148bdf6e3a Fix handling when FRITZ!Box reboots in FRITZ!Smarthome (#162676) 2026-02-13 19:24:05 +00:00
starkillerOG
91999f8871 Bump reolink-aio to 0.19.0 (#162672) 2026-02-13 19:24:04 +00:00
Jeef
aecca4eb99 Bump intellifire4py to 4.3.1 (#162659) 2026-02-13 19:24:03 +00:00
Allen Porter
bf8aa49bae Improve MCP SSE fallback error handling (#162655) 2026-02-13 19:24:02 +00:00
Joost Lekkerkerker
4423425683 Pin setuptools to 81.0.0 (#162589)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 19:24:01 +00:00
Aaron Godfrey
44202da53d Increase max tasks retrieved per page to prevent timeout (#162587) 2026-02-13 19:23:59 +00:00
Thomas55555
9f7dfb72c4 Bump aioautomower to 2.7.3 (#162583)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-13 19:23:58 +00:00
Michael
de07a69e4f Bump aioimmich to 0.12.0 (#162573)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-02-13 19:23:57 +00:00
Maikel Punie
bbf4c38115 migrate velbus config entries (#162565) 2026-02-13 19:23:56 +00:00
ElCruncharino
e1bb5d52ef Add timeout to B2 metadata downloads to prevent backup hang (#162562) 2026-02-13 19:23:54 +00:00
hanwg
eb64b6bdee Fix config flow bug for Telegram bot (#162555) 2026-02-13 19:23:53 +00:00
Andrea Turri
ecb288b735 Add new Miele mappings (#162544) 2026-02-13 19:23:52 +00:00
Norbert Rittel
a419c9c420 Sentence-case "speech-to-text" in google_cloud (#162534) 2026-02-13 19:23:51 +00:00
Brett Adams
dd29133324 Fix Tesla Fleet partner registration to use all regions (#162525)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:23:50 +00:00
Allen Porter
90f22ea516 Bump grpc to 1.78.0 (#162520) 2026-02-13 19:23:48 +00:00
Peter Grauvogel
9db1428265 Fix Green Planet Energy price unit conversion (#162511) 2026-02-13 19:23:47 +00:00
Denis Shulyaka
a696b05b0d Fix JSON serialization of time objects in Cloud conversation tool results (#162506) 2026-02-13 19:23:46 +00:00
Denis Shulyaka
77ddb63b73 Fix JSON serialization of time objects in Open Router tool results (#162505) 2026-02-13 19:23:44 +00:00
Denis Shulyaka
4180a6e176 Fix JSON serialization of time objects in Ollama tool results (#162502)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-13 19:23:43 +00:00
Denis Shulyaka
6d74c912d2 Fix JSON serialization of datetime objects in Google Generative AI tool results (#162495) 2026-02-13 19:23:42 +00:00
Denis Shulyaka
8a01dfcc00 Fix JSON serialization of time objects in OpenAI tool results (#162490) 2026-02-13 19:23:40 +00:00
Brett Adams
9722898dc6 Fix device_class of backup reserve sensor in Tessie (#162459) 2026-02-13 19:23:39 +00:00
Brett Adams
7438c71fcb Fix device_class of backup reserve sensor in teslemetry (#162458) 2026-02-13 19:23:38 +00:00
Christian Lackas
0b5e55b923 Fix absolute humidity sensor on HmIP-WGT glass thermostats (#162455) 2026-02-13 19:23:37 +00:00
ElCruncharino
61ed959e8e Fix AsyncIteratorReader blocking after stream exhaustion (#161731) 2026-02-13 19:17:20 +00:00
Jaap Pieroen
3989532465 Bump essent-dynamic-pricing to 0.3.1 (#160958)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-02-13 19:17:18 +00:00
Franck Nijhof
28027ddca4 2026.2.1 (#162450) 2026-02-06 22:44:07 +01:00
Franck Nijhof
fe0d7b3cca Bump version to 2026.2.1 2026-02-06 20:49:26 +00:00
jameson_uk
0dcc4e9527 dep: bump aioamazondevices to 11.1.3 (#162437) 2026-02-06 20:47:38 +00:00
Artur Pragacz
b13b189703 Make bad entity ID detection more lenient (#162425) 2026-02-06 20:47:37 +00:00
epenet
150829f599 Fix invalid yardian snaphots (#162422) 2026-02-06 20:47:36 +00:00
Joost Lekkerkerker
57dd9d9c23 Remove double unit of measurement for yardian (#162412) 2026-02-06 20:47:34 +00:00
Sab44
e2056cb12c Bump librehardwaremonitor-api to version 1.9.1 (#162409) 2026-02-06 20:47:33 +00:00
Joost Lekkerkerker
fa2c8992cf Remove entity id overwrite for ambient station (#162403) 2026-02-06 20:47:32 +00:00
Matt Zimmerman
ddf5c7fe3a Add missing config flow strings to SmartTub (#162375)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-06 20:47:31 +00:00
Matt Zimmerman
7034ed6d3f Bump python-smarttub to 0.0.47 (#162367)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-06 20:47:29 +00:00
Aaron Godfrey
9015b53c1b Fix conversion of data for todo.* actions (#162366) 2026-02-06 20:47:28 +00:00
Jordan Harvey
1cfa6561f7 Update pynintendoparental requirement to version 2.3.2.1 (#162362) 2026-02-06 20:47:27 +00:00
Shay Levy
eead02dcca Fix Shelly Linkedgo Thermostat status update (#162339) 2026-02-06 20:47:26 +00:00
Arie Catsman
456e51a221 Bump pyenphase to 2.4.5 (#162324) 2026-02-06 20:47:25 +00:00
Luo Chen
5d984ce186 Fix unicode escaping in MCP server tool response (#162319)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-02-06 20:47:24 +00:00
Oliver
61f45489ac Add mapping for stopped state to denonavr media player (#162283) 2026-02-06 20:47:23 +00:00
Tomás Correia
f72c643b38 Fix multipart upload to use consistent part sizes for R2/S3 (#162278) 2026-02-06 20:47:22 +00:00
Oliver
27bc26e886 Bump denonavr to 1.3.2 (#162271) 2026-02-06 20:47:20 +00:00
Thomas55555
0e9f03cbc1 Bump google_air_quality_api to 3.0.1 (#162233) 2026-02-06 20:47:19 +00:00
David Bonnes
9480c33fb0 Bump evohome-async to 1.1.3 (#162232) 2026-02-06 20:47:18 +00:00
Jonathan
3e6b8663e8 Fix device_class of backup reserve sensor (#161178) 2026-02-06 20:47:17 +00:00
epenet
1c69a83793 Fix redundant off preset in Tuya climate (#161040) 2026-02-06 20:47:16 +00:00
2991 changed files with 175256 additions and 54753 deletions

1
.agent/skills Symbolic link
View File

@@ -0,0 +1 @@
../.claude/skills/

View File

@@ -0,0 +1,46 @@
---
name: github-pr-reviewer
description: Review a GitHub pull request and provide feedback comments. Use when the user says "review the current PR" or asks to review a specific PR.
---
# Review GitHub Pull Request
## Preparation:
- Check if the local commit matches the last one in the PR. If not, checkout the PR locally using 'gh pr checkout'.
- CRITICAL: If 'gh pr checkout' fails for ANY reason, you MUST immediately STOP.
- Do NOT attempt any workarounds.
- Do NOT proceed with the review.
- ALERT about the failure and WAIT for instructions.
- This is a hard requirement - no exceptions.
## Follow these steps:
1. Use 'gh pr view' to get the PR details and description.
2. Use 'gh pr diff' to see all the changes in the PR.
3. Analyze the code changes for:
- Code quality and style consistency
- Potential bugs or issues
- Performance implications
- Security concerns
- Test coverage
- Documentation updates if needed
4. Ensure any existing review comments have been addressed.
5. Generate constructive review comments in the CONSOLE. DO NOT POST TO GITHUB YOURSELF.
## IMPORTANT:
- Just review. DO NOT make any changes
- Be constructive and specific in your comments
- Suggest improvements where appropriate
- Only provide review feedback in the CONSOLE. DO NOT ACT ON GITHUB.
- No need to run tests or linters, just review the code changes.
- No need to highlight things that are already good.
## Output format:
- List specific comments for each file/line that needs attention
- In the end, summarize with an overall assessment (approve, request changes, or comment) and bullet point list of changes suggested, if any.
- Example output:
```
Overall assessment: request changes.
- [CRITICAL] Memory leak in homeassistant/components/sensor/my_sensor.py:143
- [PROBLEM] Inefficient algorithm in homeassistant/helpers/data_processing.py:87
- [SUGGESTION] Improve variable naming in homeassistant/helpers/config_validation.py:45
```

View File

@@ -34,6 +34,7 @@ base_platforms: &base_platforms
- homeassistant/components/humidifier/**
- homeassistant/components/image/**
- homeassistant/components/image_processing/**
- homeassistant/components/infrared/**
- homeassistant/components/lawn_mower/**
- homeassistant/components/light/**
- homeassistant/components/lock/**

1
.gemini/skills Symbolic link
View File

@@ -0,0 +1 @@
../.claude/skills

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.14.2"
PIP_TIMEOUT: 60
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
@@ -42,10 +41,10 @@ jobs:
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
- name: Get information
id: info
@@ -80,7 +79,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: translations
path: translations.tar.gz
@@ -112,7 +111,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@5c98f0b039f36ef966fdb7dfa9779262785ecb05 # v14
uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -123,7 +122,7 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@5c98f0b039f36ef966fdb7dfa9779262785ecb05 # v14
uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 # v16
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: OHF-Voice/intents-package
@@ -132,11 +131,11 @@ jobs:
workflow_conclusion: success
name: package
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
if: needs.init.outputs.channel == 'dev'
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
- name: Adjust nightly version
if: needs.init.outputs.channel == 'dev'
@@ -182,7 +181,7 @@ jobs:
fi
- name: Download translations
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: translations
@@ -197,7 +196,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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -272,7 +271,7 @@ jobs:
name: Build ${{ matrix.machine }} machine core image
if: github.repository_owner == 'home-assistant'
needs: ["init", "build_base"]
runs-on: ubuntu-latest
runs-on: ${{ matrix.runs-on }}
permissions:
contents: read # To check out the repository
packages: write # To push to GHCR
@@ -294,6 +293,21 @@ jobs:
- raspberrypi5-64
- yellow
- green
include:
# Default: aarch64 on native ARM runner
- arch: aarch64
runs-on: ubuntu-24.04-arm
# Overrides for amd64 machines
- machine: generic-x86-64
arch: amd64
runs-on: ubuntu-24.04
- machine: qemux86-64
arch: amd64
runs-on: ubuntu-24.04
# TODO: remove, intel-nuc is a legacy name for x86-64, renamed in 2021
- machine: intel-nuc
arch: amd64
runs-on: ubuntu-24.04
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -314,15 +328,16 @@ jobs:
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@21bc64d76dad7a5184c67826aab41c6b6f89023a # 2025.11.0
uses: home-assistant/builder@6cb4fd3d1338b6e22d0958a4bcb53e0965ea63b4 # 2026.02.1
with:
image: ${{ matrix.arch }}
args: |
$BUILD_ARGS \
--target /data/machine \
@@ -391,13 +406,13 @@ jobs:
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -522,13 +537,13 @@ jobs:
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
- name: Download translations
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: translations
@@ -570,7 +585,7 @@ jobs:
persist-credentials: false
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -599,7 +614,7 @@ jobs:
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}

View File

@@ -37,12 +37,11 @@ on:
type: boolean
env:
CACHE_VERSION: 2
CACHE_VERSION: 3
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.3"
DEFAULT_PYTHON: "3.14.2"
ALL_PYTHON_VERSIONS: "['3.14.2']"
HA_SHORT_VERSION: "2026.4"
ADDITIONAL_PYTHON_VERSIONS: "[]"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support
@@ -166,6 +165,11 @@ jobs:
tests_glob=""
lint_only=""
skip_coverage=""
default_python=$(cat .python-version)
all_python_versions=$(jq -cn \
--arg default_python "${default_python}" \
--argjson additional_python_versions "${ADDITIONAL_PYTHON_VERSIONS}" \
'[$default_python] + $additional_python_versions')
if [[ "${INTEGRATION_CHANGES}" != "[]" ]];
then
@@ -235,8 +239,8 @@ jobs:
echo "mariadb_groups=${mariadb_groups}" >> $GITHUB_OUTPUT
echo "postgresql_groups: ${postgresql_groups}"
echo "postgresql_groups=${postgresql_groups}" >> $GITHUB_OUTPUT
echo "python_versions: ${ALL_PYTHON_VERSIONS}"
echo "python_versions=${ALL_PYTHON_VERSIONS}" >> $GITHUB_OUTPUT
echo "python_versions: ${all_python_versions}"
echo "python_versions=${all_python_versions}" >> $GITHUB_OUTPUT
echo "test_full_suite: ${test_full_suite}"
echo "test_full_suite=${test_full_suite}" >> $GITHUB_OUTPUT
echo "integrations_glob: ${integrations_glob}"
@@ -452,7 +456,7 @@ jobs:
python --version
uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt
@@ -503,13 +507,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -540,13 +544,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -576,11 +580,11 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Run gen_copilot_instructions.py
run: |
@@ -605,7 +609,7 @@ jobs:
with:
persist-credentials: false
- name: Dependency review
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
with:
license-check: false # We use our own license audit checks
@@ -653,7 +657,7 @@ jobs:
. venv/bin/activate
python -m script.licenses extract --output-file=licenses-${PYTHON_VERSION}.json
- name: Upload licenses
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
path: licenses-${{ matrix.python-version }}.json
@@ -682,13 +686,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -735,13 +739,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -786,11 +790,11 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Generate partial mypy restore key
id: generate-mypy-key
@@ -798,7 +802,7 @@ jobs:
mypy_version=$(cat requirements_test.txt | grep 'mypy.*=' | cut -d '=' -f 3)
echo "version=${mypy_version}" >> $GITHUB_OUTPUT
echo "key=mypy-${MYPY_CACHE_VERSION}-${mypy_version}-${HA_SHORT_VERSION}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -879,13 +883,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
- name: Restore full Python virtual environment
id: cache-venv
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
@@ -901,7 +905,7 @@ jobs:
. venv/bin/activate
python -m script.split_tests ${TEST_GROUP_COUNT} tests
- name: Upload pytest_buckets
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pytest_buckets
path: pytest_buckets.txt
@@ -978,7 +982,7 @@ jobs:
run: |
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
- name: Download pytest_buckets
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: pytest_buckets
- name: Compile English translations
@@ -1020,14 +1024,14 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${TEST_GROUP}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1040,7 +1044,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: test-results-full-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml
@@ -1177,7 +1181,7 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${mariadb}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1185,7 +1189,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1199,7 +1203,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: test-results-mariadb-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.mariadb }}
@@ -1338,7 +1342,7 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${postgresql}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1346,7 +1350,7 @@ jobs:
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1360,7 +1364,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: test-results-postgres-${{ matrix.python-version }}-${{
steps.pytest-partial.outputs.postgresql }}
@@ -1387,7 +1391,7 @@ jobs:
with:
persist-credentials: false
- name: Download all coverage artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
@@ -1514,14 +1518,14 @@ jobs:
2>&1 | tee pytest-${PYTHON_VERSION}-${TEST_GROUP}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
path: pytest-*.txt
overwrite: true
- name: Upload coverage artifact
if: needs.info.outputs.skip_coverage != 'true'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@@ -1534,7 +1538,7 @@ jobs:
mv "junit.xml-tmp" "junit.xml"
- name: Upload test results artifact
if: needs.info.outputs.skip_coverage != 'true' && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: test-results-partial-${{ matrix.python-version }}-${{ matrix.group }}
path: junit.xml
@@ -1558,7 +1562,7 @@ jobs:
with:
persist-credentials: false
- name: Download all coverage artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: coverage-*
- name: Upload coverage to Codecov
@@ -1587,7 +1591,7 @@ jobs:
&& needs.info.outputs.skip_coverage != 'true' && !cancelled()
steps:
- name: Download all coverage artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: test-results-*
- name: Upload test results to Codecov

View File

@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
category: "/language:python"

View File

@@ -236,7 +236,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@a380166897b5408b8fb7dddd148142794cb5624a # v2.0.6
uses: actions/ai-inference@e09e65981758de8b2fdab13c2bfb7c7d5493b0b6 # v2.0.7
with:
model: openai/gpt-4o
system-prompt: |

View File

@@ -62,7 +62,7 @@ jobs:
- name: Detect language using AI
id: ai_language_detection
if: steps.detect_language.outputs.should_continue == 'true'
uses: actions/ai-inference@a380166897b5408b8fb7dddd148142794cb5624a # v2.0.6
uses: actions/ai-inference@e09e65981758de8b2fdab13c2bfb7c7d5493b0b6 # v2.0.7
with:
model: openai/gpt-4o-mini
system-prompt: |

View File

@@ -27,7 +27,7 @@ jobs:
# - No PRs marked as no-stale
# - No issues (-1)
- name: 60 days stale PRs policy
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60
@@ -67,7 +67,7 @@ jobs:
# - No issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: 90 days stale issues
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
repo-token: ${{ steps.token.outputs.token }}
days-before-stale: 90
@@ -97,7 +97,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted
# - No PRs (-1)
- name: Needs more information stale issues policy
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
repo-token: ${{ steps.token.outputs.token }}
only-labels: "needs-more-information"

View File

@@ -15,9 +15,6 @@ concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
env:
DEFAULT_PYTHON: "3.14.2"
jobs:
upload:
name: Upload
@@ -29,10 +26,10 @@ jobs:
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
- name: Upload Translations
env:

View File

@@ -16,9 +16,6 @@ on:
- "requirements.txt"
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.14.2"
permissions: {}
concurrency:
@@ -36,11 +33,11 @@ jobs:
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version-file: ".python-version"
check-latest: true
- name: Create Python virtual environment
@@ -77,7 +74,7 @@ jobs:
) > .env_file
- name: Upload env_file
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: env_file
path: ./.env_file
@@ -85,7 +82,7 @@ jobs:
overwrite: true
- name: Upload requirements_diff
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: requirements_diff
path: ./requirements_diff.txt
@@ -97,7 +94,7 @@ jobs:
python -m script.gen_requirements_all ci
- name: Upload requirements_all_wheels
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: requirements_all_wheels
path: ./requirements_all_wheels_*.txt
@@ -110,7 +107,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp313", "cp314"]
abi: ["cp314"]
arch: ["amd64", "aarch64"]
include:
- arch: amd64
@@ -124,12 +121,12 @@ jobs:
persist-credentials: false
- name: Download env_file
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: env_file
- name: Download requirements_diff
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: requirements_diff
@@ -161,7 +158,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp313", "cp314"]
abi: ["cp314"]
arch: ["amd64", "aarch64"]
include:
- arch: amd64
@@ -175,17 +172,17 @@ jobs:
persist-credentials: false
- name: Download env_file
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: env_file
- name: Download requirements_diff
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: requirements_diff
- name: Download requirements_all_wheels
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: requirements_all_wheels
@@ -209,4 +206,4 @@ jobs:
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
constraints: "homeassistant/package_constraints.txt"
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txt"
requirements: "requirements_all_wheels_${{ matrix.arch }}.txt"

View File

@@ -1 +1 @@
3.14
3.14.2

View File

@@ -49,6 +49,7 @@ homeassistant.components.actiontec.*
homeassistant.components.adax.*
homeassistant.components.adguard.*
homeassistant.components.aftership.*
homeassistant.components.ai_task.*
homeassistant.components.air_quality.*
homeassistant.components.airgradient.*
homeassistant.components.airly.*
@@ -122,7 +123,6 @@ homeassistant.components.blueprint.*
homeassistant.components.bluesound.*
homeassistant.components.bluetooth.*
homeassistant.components.bluetooth_adapters.*
homeassistant.components.bmw_connected_drive.*
homeassistant.components.bond.*
homeassistant.components.bosch_alarm.*
homeassistant.components.braviatv.*
@@ -130,6 +130,7 @@ homeassistant.components.bring.*
homeassistant.components.brother.*
homeassistant.components.browser.*
homeassistant.components.bryant_evolution.*
homeassistant.components.bsblan.*
homeassistant.components.bthome.*
homeassistant.components.button.*
homeassistant.components.calendar.*
@@ -209,7 +210,9 @@ homeassistant.components.firefly_iii.*
homeassistant.components.fitbit.*
homeassistant.components.flexit_bacnet.*
homeassistant.components.flux_led.*
homeassistant.components.folder_watcher.*
homeassistant.components.forecast_solar.*
homeassistant.components.freshr.*
homeassistant.components.fritz.*
homeassistant.components.fritzbox.*
homeassistant.components.fritzbox_callmonitor.*
@@ -275,6 +278,7 @@ homeassistant.components.humidifier.*
homeassistant.components.husqvarna_automower.*
homeassistant.components.hydrawise.*
homeassistant.components.hyperion.*
homeassistant.components.hypontech.*
homeassistant.components.ibeacon.*
homeassistant.components.idasen_desk.*
homeassistant.components.image.*
@@ -285,6 +289,7 @@ homeassistant.components.imgw_pib.*
homeassistant.components.immich.*
homeassistant.components.incomfort.*
homeassistant.components.inels.*
homeassistant.components.infrared.*
homeassistant.components.input_button.*
homeassistant.components.input_select.*
homeassistant.components.input_text.*
@@ -297,6 +302,7 @@ homeassistant.components.iotty.*
homeassistant.components.ipp.*
homeassistant.components.iqvia.*
homeassistant.components.iron_os.*
homeassistant.components.isal.*
homeassistant.components.islamic_prayer_times.*
homeassistant.components.isy994.*
homeassistant.components.jellyfin.*
@@ -307,6 +313,7 @@ homeassistant.components.knocki.*
homeassistant.components.knx.*
homeassistant.components.kraken.*
homeassistant.components.kulersky.*
homeassistant.components.labs.*
homeassistant.components.lacrosse.*
homeassistant.components.lacrosse_view.*
homeassistant.components.lamarzocco.*
@@ -335,6 +342,7 @@ homeassistant.components.lookin.*
homeassistant.components.lovelace.*
homeassistant.components.luftdaten.*
homeassistant.components.lunatone.*
homeassistant.components.lutron.*
homeassistant.components.madvr.*
homeassistant.components.manual.*
homeassistant.components.mastodon.*
@@ -366,6 +374,7 @@ homeassistant.components.my.*
homeassistant.components.mysensors.*
homeassistant.components.myuplink.*
homeassistant.components.nam.*
homeassistant.components.namecheapdns.*
homeassistant.components.nasweb.*
homeassistant.components.neato.*
homeassistant.components.nest.*
@@ -401,6 +410,7 @@ homeassistant.components.opnsense.*
homeassistant.components.opower.*
homeassistant.components.oralb.*
homeassistant.components.otbr.*
homeassistant.components.otp.*
homeassistant.components.overkiz.*
homeassistant.components.overseerr.*
homeassistant.components.p1_monitor.*
@@ -417,6 +427,7 @@ homeassistant.components.plugwise.*
homeassistant.components.pooldose.*
homeassistant.components.portainer.*
homeassistant.components.powerfox.*
homeassistant.components.powerfox_local.*
homeassistant.components.powerwall.*
homeassistant.components.private_ble_device.*
homeassistant.components.prometheus.*
@@ -435,10 +446,12 @@ homeassistant.components.radarr.*
homeassistant.components.radio_browser.*
homeassistant.components.rainforest_raven.*
homeassistant.components.rainmachine.*
homeassistant.components.random.*
homeassistant.components.raspberry_pi.*
homeassistant.components.rdw.*
homeassistant.components.recollect_waste.*
homeassistant.components.recorder.*
homeassistant.components.recovery_mode.*
homeassistant.components.redgtech.*
homeassistant.components.remember_the_milk.*
homeassistant.components.remote.*
@@ -470,6 +483,7 @@ homeassistant.components.schlage.*
homeassistant.components.scrape.*
homeassistant.components.script.*
homeassistant.components.search.*
homeassistant.components.season.*
homeassistant.components.select.*
homeassistant.components.sensibo.*
homeassistant.components.sensirion_ble.*
@@ -496,6 +510,7 @@ homeassistant.components.smtp.*
homeassistant.components.snooz.*
homeassistant.components.solarlog.*
homeassistant.components.sonarr.*
homeassistant.components.spaceapi.*
homeassistant.components.speedtestdotnet.*
homeassistant.components.spotify.*
homeassistant.components.sql.*
@@ -520,6 +535,7 @@ homeassistant.components.synology_dsm.*
homeassistant.components.system_health.*
homeassistant.components.system_log.*
homeassistant.components.systemmonitor.*
homeassistant.components.systemnexa2.*
homeassistant.components.tag.*
homeassistant.components.tailscale.*
homeassistant.components.tailwind.*
@@ -530,6 +546,7 @@ homeassistant.components.tcp.*
homeassistant.components.technove.*
homeassistant.components.tedee.*
homeassistant.components.telegram_bot.*
homeassistant.components.teslemetry.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
homeassistant.components.threshold.*
@@ -562,12 +579,14 @@ homeassistant.components.update.*
homeassistant.components.uptime.*
homeassistant.components.uptime_kuma.*
homeassistant.components.uptimerobot.*
homeassistant.components.usage_prediction.*
homeassistant.components.usb.*
homeassistant.components.uvc.*
homeassistant.components.vacuum.*
homeassistant.components.vallox.*
homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.velux.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
@@ -580,6 +599,7 @@ homeassistant.components.water_heater.*
homeassistant.components.watts.*
homeassistant.components.watttime.*
homeassistant.components.weather.*
homeassistant.components.web_rtc.*
homeassistant.components.webhook.*
homeassistant.components.webostv.*
homeassistant.components.websocket_api.*
@@ -596,6 +616,7 @@ homeassistant.components.yale_smart_alarm.*
homeassistant.components.yalexs_ble.*
homeassistant.components.youtube.*
homeassistant.components.zeroconf.*
homeassistant.components.zinvolt.*
homeassistant.components.zodiac.*
homeassistant.components.zone.*
homeassistant.components.zwave_js.*

318
AGENTS.md
View File

@@ -4,325 +4,17 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Code Review Guidelines
**When reviewing code, do NOT comment on:**
- **Missing imports** - We use static analysis tooling to catch that
- **Code formatting** - We have ruff as a formatting tool that will catch those if needed (unless specifically instructed otherwise in these instructions)
**Git commit practices during review:**
- **Do NOT amend, squash, or rebase commits after review has started** - Reviewers need to see what changed since their last review
## Python Requirements
- **Compatibility**: Python 3.13+
- **Language Features**: Use the newest features when possible:
- Pattern matching
- Type hints
- f-strings (preferred over `%` or `.format()`)
- Dataclasses
- Walrus operator
### Strict Typing (Platinum)
- **Comprehensive Type Hints**: Add type hints to all functions, methods, and variables
- **Custom Config Entry Types**: When using runtime_data:
```python
type MyIntegrationConfigEntry = ConfigEntry[MyClient]
```
- **Library Requirements**: Include `py.typed` file for PEP-561 compliance
## Code Quality Standards
- **Formatting**: Ruff
- **Linting**: PyLint and Ruff
- **Type Checking**: MyPy
- **Lint/Type/Format Fixes**: Always prefer addressing the underlying issue (e.g., import the typed source, update shared stubs, align with Ruff expectations, or correct formatting at the source) before disabling a rule, adding `# type: ignore`, or skipping a formatter. Treat suppressions and `noqa` comments as a last resort once no compliant fix exists
- **Testing**: pytest with plain functions and fixtures
- **Language**: American English for all code, comments, and documentation (use sentence case, including titles)
### Writing Style Guidelines
- **Tone**: Friendly and informative
- **Perspective**: Use second-person ("you" and "your") for user-facing messages
- **Inclusivity**: Use objective, non-discriminatory language
- **Clarity**: Write for non-native English speakers
- **Formatting in Messages**:
- Use backticks for: file paths, filenames, variable names, field entries
- Use sentence case for titles and messages (capitalize only the first word and proper nouns)
- Avoid abbreviations when possible
### Documentation Standards
- **File Headers**: Short and concise
```python
"""Integration for Peblar EV chargers."""
```
- **Method/Function Docstrings**: Required for all
```python
async def async_setup_entry(hass: HomeAssistant, entry: PeblarConfigEntry) -> bool:
"""Set up Peblar from a config entry."""
```
- **Comment Style**:
- Use clear, descriptive comments
- Explain the "why" not just the "what"
- Keep code block lines under 80 characters when possible
- Use progressive disclosure (simple explanation first, complex details later)
## Async Programming
- All external I/O operations must be async
- **Best Practices**:
- Avoid sleeping in loops
- Avoid awaiting in loops - use `gather` instead
- No blocking calls
- Group executor jobs when possible - switching between event loop and executor is expensive
### Blocking Operations
- **Use Executor**: For blocking I/O operations
```python
result = await hass.async_add_executor_job(blocking_function, args)
```
- **Never Block Event Loop**: Avoid file operations, `time.sleep()`, blocking HTTP calls
- **Replace with Async**: Use `asyncio.sleep()` instead of `time.sleep()`
### Thread Safety
- **@callback Decorator**: For event loop safe functions
```python
@callback
def async_update_callback(self, event):
"""Safe to run in event loop."""
self.async_write_ha_state()
```
- **Sync APIs from Threads**: Use sync versions when calling from non-event loop threads
- **Registry Changes**: Must be done in event loop thread
### Error Handling
- **Exception Types**: Choose most specific exception available
- `ServiceValidationError`: User input errors (preferred over `ValueError`)
- `HomeAssistantError`: Device communication failures
- `ConfigEntryNotReady`: Temporary setup issues (device offline)
- `ConfigEntryAuthFailed`: Authentication problems
- `ConfigEntryError`: Permanent setup issues
- **Try/Catch Best Practices**:
- Only wrap code that can throw exceptions
- Keep try blocks minimal - process data after the try/catch
- **Avoid bare exceptions** except in specific cases:
- ❌ Generally not allowed: `except:` or `except Exception:`
- ✅ Allowed in config flows to ensure robustness
- ✅ Allowed in functions/methods that run in background tasks
- Bad pattern:
```python
try:
data = await device.get_data() # Can throw
# ❌ Don't process data inside try block
processed = data.get("value", 0) * 100
self._attr_native_value = processed
except DeviceError:
_LOGGER.error("Failed to get data")
```
- Good pattern:
```python
try:
data = await device.get_data() # Can throw
except DeviceError:
_LOGGER.error("Failed to get data")
return
# ✅ Process data outside try block
processed = data.get("value", 0) * 100
self._attr_native_value = processed
```
- **Bare Exception Usage**:
```python
# ❌ Not allowed in regular code
try:
data = await device.get_data()
except Exception: # Too broad
_LOGGER.error("Failed")
# ✅ Allowed in config flow for robustness
async def async_step_user(self, user_input=None):
try:
await self._test_connection(user_input)
except Exception: # Allowed here
errors["base"] = "unknown"
# ✅ Allowed in background tasks
async def _background_refresh():
try:
await coordinator.async_refresh()
except Exception: # Allowed in task
_LOGGER.exception("Unexpected error in background task")
```
- **Setup Failure Patterns**:
```python
try:
await device.async_setup()
except (asyncio.TimeoutError, TimeoutException) as ex:
raise ConfigEntryNotReady(f"Timeout connecting to {device.host}") from ex
except AuthFailed as ex:
raise ConfigEntryAuthFailed(f"Credentials expired for {device.name}") from ex
```
### Logging
- **Format Guidelines**:
- No periods at end of messages
- No integration names/domains (added automatically)
- No sensitive data (keys, tokens, passwords)
- Use debug level for non-user-facing messages
- **Use Lazy Logging**:
```python
_LOGGER.debug("This is a log message with %s", variable)
```
### Unavailability Logging
- **Log Once**: When device/service becomes unavailable (info level)
- **Log Recovery**: When device/service comes back online
- **Implementation Pattern**:
```python
_unavailable_logged: bool = False
if not self._unavailable_logged:
_LOGGER.info("The sensor is unavailable: %s", ex)
self._unavailable_logged = True
# On recovery:
if self._unavailable_logged:
_LOGGER.info("The sensor is back online")
self._unavailable_logged = False
```
## Development Commands
### Environment
- **Local development (non-container)**: Activate the project venv before running commands: `source .venv/bin/activate`
- **Dev container**: No activation needed, the environment is pre-configured
.vscode/tasks.json contains useful commands used for development.
### Code Quality & Linting
- **Run all linters on all files**: `prek run --all-files`
- **Run linters on staged files only**: `prek run`
- **PyLint on everything** (slow): `pylint homeassistant`
- **PyLint on specific folder**: `pylint homeassistant/components/my_integration`
- **MyPy type checking (whole project)**: `mypy homeassistant/`
- **MyPy on specific integration**: `mypy homeassistant/components/my_integration`
## Python Syntax Notes
### Testing
- **Quick test of changed files**: `pytest --timeout=10 --picked`
- **Update test snapshots**: Add `--snapshot-update` to pytest command
- ⚠️ Omit test results after using `--snapshot-update`
- Always run tests again without the flag to verify snapshots
- **Full test suite** (AVOID - very slow): `pytest ./tests`
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses.
### Dependencies & Requirements
- **Update generated files after dependency changes**: `python -m script.gen_requirements_all`
- **Install all Python requirements**:
```bash
uv pip install -r requirements_all.txt -r requirements.txt -r requirements_test.txt
```
- **Install test requirements only**:
```bash
uv pip install -r requirements_test_all.txt -r requirements.txt
```
## Good practices
### Translations
- **Update translations after strings.json changes**:
```bash
python -m script.translations develop --all
```
### Project Validation
- **Run hassfest** (checks project structure and updates generated files):
```bash
python -m script.hassfest
```
## Common Anti-Patterns & Best Practices
### ❌ **Avoid These Patterns**
```python
# Blocking operations in event loop
data = requests.get(url) # ❌ Blocks event loop
time.sleep(5) # ❌ Blocks event loop
# Reusing BleakClient instances
self.client = BleakClient(address)
await self.client.connect()
# Later...
await self.client.connect() # ❌ Don't reuse
# Hardcoded strings in code
self._attr_name = "Temperature Sensor" # ❌ Not translatable
# Missing error handling
data = await self.api.get_data() # ❌ No exception handling
# Storing sensitive data in diagnostics
return {"api_key": entry.data[CONF_API_KEY]} # ❌ Exposes secrets
# Accessing hass.data directly in tests
coordinator = hass.data[DOMAIN][entry.entry_id] # ❌ Don't access hass.data
# User-configurable polling intervals
# In config flow
vol.Optional("scan_interval", default=60): cv.positive_int # ❌ Not allowed
# In coordinator
update_interval = timedelta(minutes=entry.data.get("scan_interval", 1)) # ❌ Not allowed
# User-configurable config entry names (non-helper integrations)
vol.Optional("name", default="My Device"): cv.string # ❌ Not allowed in regular integrations
# Too much code in try block
try:
response = await client.get_data() # Can throw
# ❌ Data processing should be outside try block
temperature = response["temperature"] / 10
humidity = response["humidity"]
self._attr_native_value = temperature
except ClientError:
_LOGGER.error("Failed to fetch data")
# Bare exceptions in regular code
try:
value = await sensor.read_value()
except Exception: # ❌ Too broad - catch specific exceptions
_LOGGER.error("Failed to read sensor")
```
### ✅ **Use These Patterns Instead**
```python
# Async operations with executor
data = await hass.async_add_executor_job(requests.get, url)
await asyncio.sleep(5) # ✅ Non-blocking
# Fresh BleakClient instances
client = BleakClient(address) # ✅ New instance each time
await client.connect()
# Translatable entity names
_attr_translation_key = "temperature_sensor" # ✅ Translatable
# Proper error handling
try:
data = await self.api.get_data()
except ApiException as err:
raise UpdateFailed(f"API error: {err}") from err
# Redacted diagnostics data
return async_redact_data(data, {"api_key", "password"}) # ✅ Safe
# Test through proper integration setup and fixtures
@pytest.fixture
async def init_integration(hass, mock_config_entry, mock_api):
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id) # ✅ Proper setup
# Integration-determined polling intervals (not user-configurable)
SCAN_INTERVAL = timedelta(minutes=5) # ✅ Common pattern: constant in const.py
class MyCoordinator(DataUpdateCoordinator[MyData]):
def __init__(self, hass: HomeAssistant, client: MyClient, config_entry: ConfigEntry) -> None:
# ✅ Integration determines interval based on device capabilities, connection type, etc.
interval = timedelta(minutes=1) if client.is_local else SCAN_INTERVAL
super().__init__(
hass,
logger=LOGGER,
name=DOMAIN,
update_interval=interval,
config_entry=config_entry, # ✅ Pass config_entry - it's accepted and recommended
)
```
Integrations with Platinum or Gold level in the Integration Quality Scale reflect a high standard of code quality and maintainability. When looking for examples of something, these are good places to start. The level is indicated in the manifest.json of the integration.

70
CODEOWNERS generated
View File

@@ -234,14 +234,14 @@ build.json @home-assistant/supervisor
/tests/components/bluetooth/ @bdraco
/homeassistant/components/bluetooth_adapters/ @bdraco
/tests/components/bluetooth_adapters/ @bdraco
/homeassistant/components/bmw_connected_drive/ @gerard33 @rikroe
/tests/components/bmw_connected_drive/ @gerard33 @rikroe
/homeassistant/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/homeassistant/components/bosch_alarm/ @mag1024 @sanjay900
/tests/components/bosch_alarm/ @mag1024 @sanjay900
/homeassistant/components/bosch_shc/ @tschamm
/tests/components/bosch_shc/ @tschamm
/homeassistant/components/brands/ @home-assistant/core
/tests/components/brands/ @home-assistant/core
/homeassistant/components/braviatv/ @bieniu @Drafteed
/tests/components/braviatv/ @bieniu @Drafteed
/homeassistant/components/bring/ @miaucl @tr4nt0r
@@ -279,6 +279,8 @@ build.json @home-assistant/supervisor
/tests/components/cert_expiry/ @jjlawren
/homeassistant/components/chacon_dio/ @cnico
/tests/components/chacon_dio/ @cnico
/homeassistant/components/chess_com/ @joostlek
/tests/components/chess_com/ @joostlek
/homeassistant/components/cisco_ios/ @fbradyirl
/homeassistant/components/cisco_mobility_express/ @fbradyirl
/homeassistant/components/cisco_webex_teams/ @fbradyirl
@@ -381,6 +383,8 @@ build.json @home-assistant/supervisor
/tests/components/dlna_dms/ @chishm
/homeassistant/components/dnsip/ @gjohansson-ST
/tests/components/dnsip/ @gjohansson-ST
/homeassistant/components/door/ @home-assistant/core
/tests/components/door/ @home-assistant/core
/homeassistant/components/doorbird/ @oblogic7 @bdraco @flacjacket
/tests/components/doorbird/ @oblogic7 @bdraco @flacjacket
/homeassistant/components/dormakaba_dkey/ @emontnemery
@@ -399,12 +403,10 @@ build.json @home-assistant/supervisor
/tests/components/dsmr_reader/ @sorted-bits @glodenox @erwindouna
/homeassistant/components/duckdns/ @tr4nt0r
/tests/components/duckdns/ @tr4nt0r
/homeassistant/components/duke_energy/ @hunterjm
/tests/components/duke_energy/ @hunterjm
/homeassistant/components/duotecno/ @cereal2nd
/tests/components/duotecno/ @cereal2nd
/homeassistant/components/dwd_weather_warnings/ @runningman84 @stephan192 @andarotajo
/tests/components/dwd_weather_warnings/ @runningman84 @stephan192 @andarotajo
/homeassistant/components/dwd_weather_warnings/ @runningman84 @stephan192
/tests/components/dwd_weather_warnings/ @runningman84 @stephan192
/homeassistant/components/dynalite/ @ziv1234
/tests/components/dynalite/ @ziv1234
/homeassistant/components/eafm/ @Jc2k
@@ -549,14 +551,14 @@ build.json @home-assistant/supervisor
/tests/components/freebox/ @hacf-fr @Quentame
/homeassistant/components/freedompro/ @stefano055415
/tests/components/freedompro/ @stefano055415
/homeassistant/components/freshr/ @SierraNL
/tests/components/freshr/ @SierraNL
/homeassistant/components/fressnapf_tracker/ @eifinger
/tests/components/fressnapf_tracker/ @eifinger
/homeassistant/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/tests/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/homeassistant/components/fritzbox/ @mib1185 @flabbamann
/tests/components/fritzbox/ @mib1185 @flabbamann
/homeassistant/components/fritzbox_callmonitor/ @cdce8p
/tests/components/fritzbox_callmonitor/ @cdce8p
/homeassistant/components/fronius/ @farmio
/tests/components/fronius/ @farmio
/homeassistant/components/frontend/ @home-assistant/frontend
@@ -569,10 +571,14 @@ build.json @home-assistant/supervisor
/tests/components/fully_kiosk/ @cgarwood
/homeassistant/components/fyta/ @dontinelli
/tests/components/fyta/ @dontinelli
/homeassistant/components/garage_door/ @home-assistant/core
/tests/components/garage_door/ @home-assistant/core
/homeassistant/components/garages_amsterdam/ @klaasnicolaas
/tests/components/garages_amsterdam/ @klaasnicolaas
/homeassistant/components/gardena_bluetooth/ @elupus
/tests/components/gardena_bluetooth/ @elupus
/homeassistant/components/gate/ @home-assistant/core
/tests/components/gate/ @home-assistant/core
/homeassistant/components/gdacs/ @exxamalte
/tests/components/gdacs/ @exxamalte
/homeassistant/components/generic/ @davet2001
@@ -719,8 +725,8 @@ build.json @home-assistant/supervisor
/tests/components/homematic/ @pvizeli
/homeassistant/components/homematicip_cloud/ @hahn-th @lackas
/tests/components/homematicip_cloud/ @hahn-th @lackas
/homeassistant/components/homevolt/ @danielhiversen
/tests/components/homevolt/ @danielhiversen
/homeassistant/components/homevolt/ @danielhiversen @liudger
/tests/components/homevolt/ @danielhiversen @liudger
/homeassistant/components/homewizard/ @DCSBL
/tests/components/homewizard/ @DCSBL
/homeassistant/components/honeywell/ @rdfurman @mkmer
@@ -739,6 +745,8 @@ build.json @home-assistant/supervisor
/tests/components/huisbaasje/ @dennisschroer
/homeassistant/components/humidifier/ @home-assistant/core @Shulyaka
/tests/components/humidifier/ @home-assistant/core @Shulyaka
/homeassistant/components/humidity/ @home-assistant/core
/tests/components/humidity/ @home-assistant/core
/homeassistant/components/hunterdouglas_powerview/ @bdraco @kingy444 @trullock
/tests/components/hunterdouglas_powerview/ @bdraco @kingy444 @trullock
/homeassistant/components/husqvarna_automower/ @Thomas55555
@@ -753,6 +761,8 @@ build.json @home-assistant/supervisor
/tests/components/hydrawise/ @dknowles2 @thomaskistler @ptcryan
/homeassistant/components/hyperion/ @dermotduffy
/tests/components/hyperion/ @dermotduffy
/homeassistant/components/hypontech/ @jcisio
/tests/components/hypontech/ @jcisio
/homeassistant/components/ialarm/ @RyuzakiKK
/tests/components/ialarm/ @RyuzakiKK
/homeassistant/components/iammeter/ @lewei50
@@ -786,10 +796,14 @@ build.json @home-assistant/supervisor
/tests/components/improv_ble/ @emontnemery
/homeassistant/components/incomfort/ @jbouwh
/tests/components/incomfort/ @jbouwh
/homeassistant/components/indevolt/ @xirt
/tests/components/indevolt/ @xirt
/homeassistant/components/inels/ @epdevlab
/tests/components/inels/ @epdevlab
/homeassistant/components/influxdb/ @mdegat01
/tests/components/influxdb/ @mdegat01
/homeassistant/components/influxdb/ @mdegat01 @Robbie1221
/tests/components/influxdb/ @mdegat01 @Robbie1221
/homeassistant/components/infrared/ @home-assistant/core
/tests/components/infrared/ @home-assistant/core
/homeassistant/components/inkbird/ @bdraco
/tests/components/inkbird/ @bdraco
/homeassistant/components/input_boolean/ @home-assistant/core
@@ -1078,6 +1092,8 @@ build.json @home-assistant/supervisor
/tests/components/mutesync/ @currentoor
/homeassistant/components/my/ @home-assistant/core
/tests/components/my/ @home-assistant/core
/homeassistant/components/myneomitis/ @l-pr
/tests/components/myneomitis/ @l-pr
/homeassistant/components/mysensors/ @MartinHjelmare @functionpointer
/tests/components/mysensors/ @MartinHjelmare @functionpointer
/homeassistant/components/mystrom/ @fabaff
@@ -1094,8 +1110,8 @@ build.json @home-assistant/supervisor
/tests/components/nasweb/ @nasWebio
/homeassistant/components/nederlandse_spoorwegen/ @YarmoM @heindrichpaul
/tests/components/nederlandse_spoorwegen/ @YarmoM @heindrichpaul
/homeassistant/components/ness_alarm/ @nickw444
/tests/components/ness_alarm/ @nickw444
/homeassistant/components/ness_alarm/ @nickw444 @poshy163
/tests/components/ness_alarm/ @nickw444 @poshy163
/homeassistant/components/nest/ @allenporter
/tests/components/nest/ @allenporter
/homeassistant/components/netatmo/ @cgtobi
@@ -1194,6 +1210,8 @@ build.json @home-assistant/supervisor
/tests/components/open_meteo/ @frenck
/homeassistant/components/open_router/ @joostlek
/tests/components/open_router/ @joostlek
/homeassistant/components/opendisplay/ @g4bri3lDev
/tests/components/opendisplay/ @g4bri3lDev
/homeassistant/components/openerz/ @misialq
/tests/components/openerz/ @misialq
/homeassistant/components/openevse/ @c00w @firstof9
@@ -1279,6 +1297,8 @@ build.json @home-assistant/supervisor
/tests/components/portainer/ @erwindouna
/homeassistant/components/powerfox/ @klaasnicolaas
/tests/components/powerfox/ @klaasnicolaas
/homeassistant/components/powerfox_local/ @klaasnicolaas
/tests/components/powerfox_local/ @klaasnicolaas
/homeassistant/components/powerwall/ @bdraco @jrester @daniel-simpson
/tests/components/powerwall/ @bdraco @jrester @daniel-simpson
/homeassistant/components/prana/ @prana-dev-official
@@ -1297,8 +1317,8 @@ build.json @home-assistant/supervisor
/tests/components/prosegur/ @dgomes
/homeassistant/components/proximity/ @mib1185
/tests/components/proximity/ @mib1185
/homeassistant/components/proxmoxve/ @jhollowe @Corbeno @erwindouna
/tests/components/proxmoxve/ @jhollowe @Corbeno @erwindouna
/homeassistant/components/proxmoxve/ @Corbeno @erwindouna @CoMPaTech
/tests/components/proxmoxve/ @Corbeno @erwindouna @CoMPaTech
/homeassistant/components/ps4/ @ktnrg45
/tests/components/ps4/ @ktnrg45
/homeassistant/components/pterodactyl/ @elmurato
@@ -1642,6 +1662,8 @@ build.json @home-assistant/supervisor
/tests/components/system_bridge/ @timmo001
/homeassistant/components/systemmonitor/ @gjohansson-ST
/tests/components/systemmonitor/ @gjohansson-ST
/homeassistant/components/systemnexa2/ @konsulten
/tests/components/systemnexa2/ @konsulten
/homeassistant/components/tado/ @erwindouna
/tests/components/tado/ @erwindouna
/homeassistant/components/tag/ @home-assistant/core
@@ -1667,6 +1689,8 @@ build.json @home-assistant/supervisor
/tests/components/telegram_bot/ @hanwg
/homeassistant/components/tellduslive/ @fredrike
/tests/components/tellduslive/ @fredrike
/homeassistant/components/teltonika/ @karlbeecken
/tests/components/teltonika/ @karlbeecken
/homeassistant/components/template/ @Petro31 @home-assistant/core
/tests/components/template/ @Petro31 @home-assistant/core
/homeassistant/components/tesla_fleet/ @Bre77
@@ -1679,7 +1703,6 @@ build.json @home-assistant/supervisor
/tests/components/tessie/ @Bre77
/homeassistant/components/text/ @home-assistant/core
/tests/components/text/ @home-assistant/core
/homeassistant/components/tfiac/ @fredrike @mellado
/homeassistant/components/thermobeacon/ @bdraco
/tests/components/thermobeacon/ @bdraco
/homeassistant/components/thermopro/ @bdraco @h3ss
@@ -1733,6 +1756,8 @@ build.json @home-assistant/supervisor
/tests/components/trafikverket_train/ @gjohansson-ST
/homeassistant/components/trafikverket_weatherstation/ @gjohansson-ST
/tests/components/trafikverket_weatherstation/ @gjohansson-ST
/homeassistant/components/trane/ @bdraco
/tests/components/trane/ @bdraco
/homeassistant/components/transmission/ @engrbm87 @JPHutchins @andrew-codechimp
/tests/components/transmission/ @engrbm87 @JPHutchins @andrew-codechimp
/homeassistant/components/trend/ @jpbede
@@ -1868,8 +1893,8 @@ build.json @home-assistant/supervisor
/tests/components/webostv/ @thecode
/homeassistant/components/websocket_api/ @home-assistant/core
/tests/components/websocket_api/ @home-assistant/core
/homeassistant/components/weheat/ @jesperraemaekers
/tests/components/weheat/ @jesperraemaekers
/homeassistant/components/weheat/ @barryvdh
/tests/components/weheat/ @barryvdh
/homeassistant/components/wemo/ @esev
/tests/components/wemo/ @esev
/homeassistant/components/whirlpool/ @abmantis @mkmer
@@ -1885,8 +1910,8 @@ build.json @home-assistant/supervisor
/tests/components/withings/ @joostlek
/homeassistant/components/wiz/ @sbidy @arturpragacz
/tests/components/wiz/ @sbidy @arturpragacz
/homeassistant/components/wled/ @frenck
/tests/components/wled/ @frenck
/homeassistant/components/wled/ @frenck @mik-laj
/tests/components/wled/ @frenck @mik-laj
/homeassistant/components/wmspro/ @mback2k
/tests/components/wmspro/ @mback2k
/homeassistant/components/wolflink/ @adamkrol93 @mtielen
@@ -1947,11 +1972,14 @@ build.json @home-assistant/supervisor
/tests/components/zha/ @dmulcahey @adminiuga @puddly @TheJulianJES
/homeassistant/components/zimi/ @markhannon
/tests/components/zimi/ @markhannon
/homeassistant/components/zinvolt/ @joostlek
/tests/components/zinvolt/ @joostlek
/homeassistant/components/zodiac/ @JulienTant
/tests/components/zodiac/ @JulienTant
/homeassistant/components/zone/ @home-assistant/core
/tests/components/zone/ @home-assistant/core
/homeassistant/components/zoneminder/ @rohankapoorcom @nabbi
/tests/components/zoneminder/ @rohankapoorcom @nabbi
/homeassistant/components/zwave_js/ @home-assistant/z-wave
/tests/components/zwave_js/ @home-assistant/z-wave
/homeassistant/components/zwave_me/ @lawfulchaos @Z-Wave-Me @PoltoS

2
Dockerfile generated
View File

@@ -30,7 +30,7 @@ RUN \
# Verify go2rtc can be executed
go2rtc --version \
# Install uv
&& pip3 install uv==0.9.26
&& pip3 install uv==0.10.6
WORKDIR /usr/src

View File

@@ -10,6 +10,7 @@ coverage:
target: auto
threshold: 1
paths:
- homeassistant/components/*/backup.py
- homeassistant/components/*/config_flow.py
- homeassistant/components/*/device_action.py
- homeassistant/components/*/device_condition.py
@@ -28,6 +29,7 @@ coverage:
target: 100
threshold: 0
paths:
- homeassistant/components/*/backup.py
- homeassistant/components/*/config_flow.py
- homeassistant/components/*/device_action.py
- homeassistant/components/*/device_condition.py

View File

@@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import hashlib
import json
import logging
from pathlib import Path
@@ -40,17 +39,6 @@ class RestoreBackupFileContent:
restore_homeassistant: bool
def password_to_key(password: str) -> bytes:
"""Generate a AES Key from password.
Matches the implementation in supervisor.backups.utils.password_to_key.
"""
key: bytes = password.encode()
for _ in range(100):
key = hashlib.sha256(key).digest()
return key[:16]
def restore_backup_file_content(config_dir: Path) -> RestoreBackupFileContent | None:
"""Return the contents of the restore backup file."""
instruction_path = config_dir.joinpath(RESTORE_BACKUP_FILE)
@@ -96,15 +84,14 @@ def _extract_backup(
"""Extract the backup file to the config directory."""
with (
TemporaryDirectory() as tempdir,
securetar.SecureTarFile(
securetar.SecureTarArchive(
restore_content.backup_file_path,
gzip=False,
mode="r",
) as ostf,
):
ostf.extractall(
ostf.tar.extractall(
path=Path(tempdir, "extracted"),
members=securetar.secure_path(ostf),
members=securetar.secure_path(ostf.tar),
filter="fully_trusted",
)
backup_meta_file = Path(tempdir, "extracted", "backup.json")
@@ -126,10 +113,7 @@ def _extract_backup(
f"homeassistant.tar{'.gz' if backup_meta['compressed'] else ''}",
),
gzip=backup_meta["compressed"],
key=password_to_key(restore_content.password)
if restore_content.password is not None
else None,
mode="r",
password=restore_content.password,
) as istf:
istf.extractall(
path=Path(tempdir, "homeassistant"),

View File

@@ -70,7 +70,7 @@ from .const import (
SIGNAL_BOOTSTRAP_INTEGRATIONS,
)
from .core_config import async_process_ha_core_config
from .exceptions import HomeAssistantError
from .exceptions import HomeAssistantError, UnsupportedStorageVersionError
from .helpers import (
area_registry,
category_registry,
@@ -210,6 +210,7 @@ DEFAULT_INTEGRATIONS = {
"analytics", # Needed for onboarding
"application_credentials",
"backup",
"brands",
"frontend",
"hardware",
"labs",
@@ -235,9 +236,20 @@ DEFAULT_INTEGRATIONS = {
"input_text",
"schedule",
"timer",
#
# Base platforms:
*BASE_PLATFORMS,
#
# Integrations providing triggers and conditions for base platforms:
"door",
"garage_door",
"gate",
"humidity",
}
DEFAULT_INTEGRATIONS_RECOVERY_MODE = {
# These integrations are set up if recovery mode is activated.
"backup",
"cloud",
"frontend",
}
DEFAULT_INTEGRATIONS_SUPERVISOR = {
@@ -432,32 +444,56 @@ def _init_blocking_io_modules_in_executor() -> None:
is_docker_env()
async def async_load_base_functionality(hass: core.HomeAssistant) -> None:
"""Load the registries and modules that will do blocking I/O."""
async def async_load_base_functionality(hass: core.HomeAssistant) -> bool:
"""Load the registries and modules that will do blocking I/O.
Return whether loading succeeded.
"""
if DATA_REGISTRIES_LOADED in hass.data:
return
return True
hass.data[DATA_REGISTRIES_LOADED] = None
entity.async_setup(hass)
frame.async_setup(hass)
template.async_setup(hass)
translation.async_setup(hass)
await asyncio.gather(
create_eager_task(get_internal_store_manager(hass).async_initialize()),
create_eager_task(area_registry.async_load(hass)),
create_eager_task(category_registry.async_load(hass)),
create_eager_task(device_registry.async_load(hass)),
create_eager_task(entity_registry.async_load(hass)),
create_eager_task(floor_registry.async_load(hass)),
create_eager_task(issue_registry.async_load(hass)),
create_eager_task(label_registry.async_load(hass)),
hass.async_add_executor_job(_init_blocking_io_modules_in_executor),
create_eager_task(template.async_load_custom_templates(hass)),
create_eager_task(restore_state.async_load(hass)),
create_eager_task(hass.config_entries.async_initialize()),
create_eager_task(async_get_system_info(hass)),
create_eager_task(condition.async_setup(hass)),
create_eager_task(trigger.async_setup(hass)),
)
recovery = hass.config.recovery_mode
try:
await asyncio.gather(
create_eager_task(get_internal_store_manager(hass).async_initialize()),
create_eager_task(area_registry.async_load(hass, load_empty=recovery)),
create_eager_task(category_registry.async_load(hass, load_empty=recovery)),
create_eager_task(device_registry.async_load(hass, load_empty=recovery)),
create_eager_task(entity_registry.async_load(hass, load_empty=recovery)),
create_eager_task(floor_registry.async_load(hass, load_empty=recovery)),
create_eager_task(issue_registry.async_load(hass, load_empty=recovery)),
create_eager_task(label_registry.async_load(hass, load_empty=recovery)),
hass.async_add_executor_job(_init_blocking_io_modules_in_executor),
create_eager_task(template.async_load_custom_templates(hass)),
create_eager_task(restore_state.async_load(hass, load_empty=recovery)),
create_eager_task(hass.config_entries.async_initialize()),
create_eager_task(async_get_system_info(hass)),
create_eager_task(condition.async_setup(hass)),
create_eager_task(trigger.async_setup(hass)),
)
except UnsupportedStorageVersionError as err:
# If we're already in recovery mode, we don't want to handle the exception
# and activate recovery mode again, as that would lead to an infinite loop.
if recovery:
raise
_LOGGER.error(
"Storage file %s was created by a newer version of Home Assistant"
" (storage version %s > %s); activating recovery mode; on-disk data"
" is preserved; upgrade Home Assistant or restore from a backup",
err.storage_key,
err.found_version,
err.max_supported_version,
)
return False
return True
async def async_from_config_dict(
@@ -474,7 +510,9 @@ async def async_from_config_dict(
# Prime custom component cache early so we know if registry entries are tied
# to a custom integration
await loader.async_get_custom_components(hass)
await async_load_base_functionality(hass)
if not await async_load_base_functionality(hass):
return None
# Set up core.
_LOGGER.debug("Setting up %s", CORE_INTEGRATIONS)

View File

@@ -0,0 +1,5 @@
{
"domain": "american_standard",
"name": "American Standard",
"integrations": ["nexia", "trane"]
}

View File

@@ -0,0 +1,5 @@
{
"domain": "powerfox",
"name": "Powerfox",
"integrations": ["powerfox", "powerfox_local"]
}

View File

@@ -0,0 +1,5 @@
{
"domain": "trane",
"name": "Trane",
"integrations": ["nexia", "trane"]
}

View File

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

View File

@@ -12,10 +12,6 @@ from homeassistant.helpers.dispatcher import dispatcher_send
from .const import DOMAIN, DOMAIN_DATA, LOGGER
SERVICE_SETTINGS = "change_setting"
SERVICE_CAPTURE_IMAGE = "capture_image"
SERVICE_TRIGGER_AUTOMATION = "trigger_automation"
ATTR_SETTING = "setting"
ATTR_VALUE = "value"
@@ -75,16 +71,13 @@ def async_setup_services(hass: HomeAssistant) -> None:
"""Home Assistant services."""
hass.services.async_register(
DOMAIN, SERVICE_SETTINGS, _change_setting, schema=CHANGE_SETTING_SCHEMA
DOMAIN, "change_setting", _change_setting, schema=CHANGE_SETTING_SCHEMA
)
hass.services.async_register(
DOMAIN, SERVICE_CAPTURE_IMAGE, _capture_image, schema=CAPTURE_IMAGE_SCHEMA
DOMAIN, "capture_image", _capture_image, schema=CAPTURE_IMAGE_SCHEMA
)
hass.services.async_register(
DOMAIN,
SERVICE_TRIGGER_AUTOMATION,
_trigger_automation,
schema=AUTOMATION_SCHEMA,
DOMAIN, "trigger_automation", _trigger_automation, schema=AUTOMATION_SCHEMA
)

View File

@@ -7,7 +7,7 @@ import logging
from accuweather import AccuWeather
from homeassistant.components.sensor import DOMAIN as SENSOR_PLATFORM
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import CONF_API_KEY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@@ -72,7 +72,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AccuWeatherConfigEntry)
ent_reg = er.async_get(hass)
for day in range(5):
unique_id = f"{location_key}-ozone-{day}"
if entity_id := ent_reg.async_get_entity_id(SENSOR_PLATFORM, DOMAIN, unique_id):
if entity_id := ent_reg.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, unique_id):
_LOGGER.debug("Removing ozone sensor entity %s", entity_id)
ent_reg.async_remove(entity_id)

View File

@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["accuweather"],
"requirements": ["accuweather==5.0.0"]
"requirements": ["accuweather==5.1.0"]
}

View File

@@ -30,6 +30,8 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
)
return {
"can_reach_server": system_health.async_check_can_reach_url(hass, ENDPOINT),
"can_reach_server": system_health.async_check_can_reach_url(
hass, str(ENDPOINT)
),
"remaining_requests": remaining_requests,
}

View File

@@ -191,7 +191,7 @@ class AccuWeatherEntity(
{
ATTR_FORECAST_TIME: utc_from_timestamp(item["EpochDate"]).isoformat(),
ATTR_FORECAST_CLOUD_COVERAGE: item["CloudCoverDay"],
ATTR_FORECAST_HUMIDITY: item["RelativeHumidityDay"]["Average"],
ATTR_FORECAST_HUMIDITY: item["RelativeHumidityDay"].get("Average"),
ATTR_FORECAST_NATIVE_TEMP: item["TemperatureMax"][ATTR_VALUE],
ATTR_FORECAST_NATIVE_TEMP_LOW: item["TemperatureMin"][ATTR_VALUE],
ATTR_FORECAST_NATIVE_APPARENT_TEMP: item["RealFeelTemperatureMax"][

40
homeassistant/components/adax/climate.py Normal file → Executable file
View File

@@ -168,29 +168,57 @@ class LocalAdaxDevice(CoordinatorEntity[AdaxLocalCoordinator], ClimateEntity):
if hvac_mode == HVACMode.HEAT:
temperature = self._attr_target_temperature or self._attr_min_temp
await self._adax_data_handler.set_target_temperature(temperature)
self._attr_target_temperature = temperature
self._attr_icon = "mdi:radiator"
elif hvac_mode == HVACMode.OFF:
await self._adax_data_handler.set_target_temperature(0)
self._attr_icon = "mdi:radiator-off"
else:
# Ignore unsupported HVAC modes to avoid desynchronizing entity state
# from the physical device.
return
self._attr_hvac_mode = hvac_mode
self.async_write_ha_state()
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return
await self._adax_data_handler.set_target_temperature(temperature)
if self._attr_hvac_mode == HVACMode.HEAT:
await self._adax_data_handler.set_target_temperature(temperature)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._attr_target_temperature = temperature
self.async_write_ha_state()
def _update_hvac_attributes(self) -> None:
"""Update hvac mode and temperatures from coordinator data.
The coordinator reports a target temperature of 0 when the heater is
turned off. In that case, only the hvac mode and icon are updated and
the previous non-zero target temperature is preserved. When the
reported target temperature is non-zero, the stored target temperature
is updated to match the coordinator value.
"""
if data := self.coordinator.data:
self._attr_current_temperature = data["current_temperature"]
self._attr_available = self._attr_current_temperature is not None
if (target_temp := data["target_temperature"]) == 0:
self._attr_hvac_mode = HVACMode.OFF
self._attr_icon = "mdi:radiator-off"
if target_temp == 0:
if self._attr_target_temperature is None:
self._attr_target_temperature = self._attr_min_temp
else:
self._attr_hvac_mode = HVACMode.HEAT
self._attr_icon = "mdi:radiator"
self._attr_target_temperature = target_temp
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_hvac_attributes()
super()._handle_coordinator_update()
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self._update_hvac_attributes()

View File

@@ -9,9 +9,13 @@ import voluptuous as vol
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
DEFAULT_MAX_KELVIN,
DEFAULT_MIN_KELVIN,
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
ColorMode,
LightEntity,
filter_supported_color_modes,
)
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
@@ -24,13 +28,20 @@ from .entity import AdsEntity
from .hub import AdsHub
CONF_ADS_VAR_BRIGHTNESS = "adsvar_brightness"
CONF_ADS_VAR_COLOR_TEMP_KELVIN = "adsvar_color_temp_kelvin"
CONF_MIN_COLOR_TEMP_KELVIN = "min_color_temp_kelvin"
CONF_MAX_COLOR_TEMP_KELVIN = "max_color_temp_kelvin"
STATE_KEY_BRIGHTNESS = "brightness"
STATE_KEY_COLOR_TEMP_KELVIN = "color_temp_kelvin"
DEFAULT_NAME = "ADS Light"
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ADS_VAR): cv.string,
vol.Optional(CONF_ADS_VAR_BRIGHTNESS): cv.string,
vol.Optional(CONF_ADS_VAR_COLOR_TEMP_KELVIN): cv.string,
vol.Optional(CONF_MIN_COLOR_TEMP_KELVIN): cv.positive_int,
vol.Optional(CONF_MAX_COLOR_TEMP_KELVIN): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
@@ -47,9 +58,24 @@ def setup_platform(
ads_var_enable: str = config[CONF_ADS_VAR]
ads_var_brightness: str | None = config.get(CONF_ADS_VAR_BRIGHTNESS)
ads_var_color_temp_kelvin: str | None = config.get(CONF_ADS_VAR_COLOR_TEMP_KELVIN)
min_color_temp_kelvin: int | None = config.get(CONF_MIN_COLOR_TEMP_KELVIN)
max_color_temp_kelvin: int | None = config.get(CONF_MAX_COLOR_TEMP_KELVIN)
name: str = config[CONF_NAME]
add_entities([AdsLight(ads_hub, ads_var_enable, ads_var_brightness, name)])
add_entities(
[
AdsLight(
ads_hub,
ads_var_enable,
ads_var_brightness,
ads_var_color_temp_kelvin,
min_color_temp_kelvin,
max_color_temp_kelvin,
name,
)
]
)
class AdsLight(AdsEntity, LightEntity):
@@ -60,18 +86,40 @@ class AdsLight(AdsEntity, LightEntity):
ads_hub: AdsHub,
ads_var_enable: str,
ads_var_brightness: str | None,
ads_var_color_temp_kelvin: str | None,
min_color_temp_kelvin: int | None,
max_color_temp_kelvin: int | None,
name: str,
) -> None:
"""Initialize AdsLight entity."""
super().__init__(ads_hub, name, ads_var_enable)
self._state_dict[STATE_KEY_BRIGHTNESS] = None
self._state_dict[STATE_KEY_COLOR_TEMP_KELVIN] = None
self._ads_var_brightness = ads_var_brightness
self._ads_var_color_temp_kelvin = ads_var_color_temp_kelvin
# Determine supported color modes
color_modes = {ColorMode.ONOFF}
if ads_var_brightness is not None:
self._attr_color_mode = ColorMode.BRIGHTNESS
self._attr_supported_color_modes = {ColorMode.BRIGHTNESS}
else:
self._attr_color_mode = ColorMode.ONOFF
self._attr_supported_color_modes = {ColorMode.ONOFF}
color_modes.add(ColorMode.BRIGHTNESS)
if ads_var_color_temp_kelvin is not None:
color_modes.add(ColorMode.COLOR_TEMP)
self._attr_supported_color_modes = filter_supported_color_modes(color_modes)
self._attr_color_mode = next(iter(self._attr_supported_color_modes))
# Set color temperature range (static config values take precedence over defaults)
if ads_var_color_temp_kelvin is not None:
self._attr_min_color_temp_kelvin = (
min_color_temp_kelvin
if min_color_temp_kelvin is not None
else DEFAULT_MIN_KELVIN
)
self._attr_max_color_temp_kelvin = (
max_color_temp_kelvin
if max_color_temp_kelvin is not None
else DEFAULT_MAX_KELVIN
)
async def async_added_to_hass(self) -> None:
"""Register device notification."""
@@ -84,11 +132,23 @@ class AdsLight(AdsEntity, LightEntity):
STATE_KEY_BRIGHTNESS,
)
if self._ads_var_color_temp_kelvin is not None:
await self.async_initialize_device(
self._ads_var_color_temp_kelvin,
pyads.PLCTYPE_UINT,
STATE_KEY_COLOR_TEMP_KELVIN,
)
@property
def brightness(self) -> int | None:
"""Return the brightness of the light (0..255)."""
return self._state_dict[STATE_KEY_BRIGHTNESS]
@property
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in Kelvin."""
return self._state_dict[STATE_KEY_COLOR_TEMP_KELVIN]
@property
def is_on(self) -> bool:
"""Return True if the entity is on."""
@@ -97,6 +157,8 @@ class AdsLight(AdsEntity, LightEntity):
def turn_on(self, **kwargs: Any) -> None:
"""Turn the light on or set a specific dimmer value."""
brightness = kwargs.get(ATTR_BRIGHTNESS)
color_temp = kwargs.get(ATTR_COLOR_TEMP_KELVIN)
self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL)
if self._ads_var_brightness is not None and brightness is not None:
@@ -104,6 +166,11 @@ class AdsLight(AdsEntity, LightEntity):
self._ads_var_brightness, brightness, pyads.PLCTYPE_UINT
)
if self._ads_var_color_temp_kelvin is not None and color_temp is not None:
self._ads_hub.write_by_name(
self._ads_var_color_temp_kelvin, color_temp, pyads.PLCTYPE_UINT
)
def turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)

View File

@@ -1,26 +1,17 @@
"""Advantage Air climate integration."""
from datetime import timedelta
import logging
from advantage_air import advantage_air
from advantage_air import ApiError, advantage_air
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
from .models import AdvantageAirData
from .coordinator import AdvantageAirCoordinator, AdvantageAirDataConfigEntry
from .services import async_setup_services
type AdvantageAirDataConfigEntry = ConfigEntry[AdvantageAirData]
ADVANTAGE_AIR_SYNC_INTERVAL = 15
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
@@ -32,9 +23,6 @@ PLATFORMS = [
Platform.UPDATE,
]
_LOGGER = logging.getLogger(__name__)
REQUEST_REFRESH_DELAY = 0.5
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
@@ -57,27 +45,10 @@ async def async_setup_entry(
retry=ADVANTAGE_AIR_RETRY,
)
async def async_get():
try:
return await api.async_get()
except ApiError as err:
raise UpdateFailed(err) from err
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
config_entry=entry,
name="Advantage Air",
update_method=async_get,
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
),
)
coordinator = AdvantageAirCoordinator(hass, entry, api)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = AdvantageAirData(coordinator, api)
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@@ -11,8 +11,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData
PARALLEL_UPDATES = 0
@@ -24,19 +24,23 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir Binary Sensor platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[BinarySensorEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirFilter(instance, ac_key))
entities.append(AdvantageAirFilter(coordinator, ac_key))
for zone_key, zone in ac_device["zones"].items():
# Only add motion sensor when motion is enabled
if zone["motionConfig"] >= 2:
entities.append(AdvantageAirZoneMotion(instance, ac_key, zone_key))
entities.append(
AdvantageAirZoneMotion(coordinator, ac_key, zone_key)
)
# Only add MyZone if it is available
if zone["type"] != 0:
entities.append(AdvantageAirZoneMyZone(instance, ac_key, zone_key))
entities.append(
AdvantageAirZoneMyZone(coordinator, ac_key, zone_key)
)
async_add_entities(entities)
@@ -47,9 +51,9 @@ class AdvantageAirFilter(AdvantageAirAcEntity, BinarySensorEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_name = "Filter"
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air Filter sensor."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-filter"
@property
@@ -63,9 +67,11 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
_attr_device_class = BinarySensorDeviceClass.MOTION
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Motion sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} motion"
self._attr_unique_id += "-motion"
@@ -81,9 +87,11 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
_attr_entity_registry_enabled_default = False
_attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone MyZone sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} myZone"
self._attr_unique_id += "-myzone"

View File

@@ -31,8 +31,8 @@ from .const import (
ADVANTAGE_AIR_STATE_ON,
ADVANTAGE_AIR_STATE_OPEN,
)
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData
ADVANTAGE_AIR_HVAC_MODES = {
"heat": HVACMode.HEAT,
@@ -90,16 +90,16 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir climate platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[ClimateEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirAC(instance, ac_key))
entities.append(AdvantageAirAC(coordinator, ac_key))
for zone_key, zone in ac_device["zones"].items():
# Only add zone climate control when zone is in temperature control
if zone["type"] > 0:
entities.append(AdvantageAirZone(instance, ac_key, zone_key))
entities.append(AdvantageAirZone(coordinator, ac_key, zone_key))
async_add_entities(entities)
@@ -114,9 +114,9 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
_attr_name = None
_support_preset = ClimateEntityFeature(0)
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_preset_modes = [ADVANTAGE_AIR_MYZONE]
@@ -282,9 +282,11 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
_attr_max_temp = 32
_attr_min_temp = 16
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an AdvantageAir Zone control."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = self._zone["name"]
@property

View File

@@ -0,0 +1,59 @@
"""Coordinator for the Advantage Air integration."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from advantage_air import ApiError, advantage_air
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
ADVANTAGE_AIR_SYNC_INTERVAL = 15
REQUEST_REFRESH_DELAY = 0.5
_LOGGER = logging.getLogger(__name__)
type AdvantageAirDataConfigEntry = ConfigEntry[AdvantageAirCoordinator]
class AdvantageAirCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Advantage Air coordinator."""
config_entry: AdvantageAirDataConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: AdvantageAirDataConfigEntry,
api: advantage_air,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name="Advantage Air",
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
),
)
self.api = api
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from the API."""
try:
return await self.api.async_get()
except ApiError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_failed",
translation_placeholders={"error": str(err)},
) from err

View File

@@ -13,8 +13,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .const import ADVANTAGE_AIR_STATE_CLOSE, ADVANTAGE_AIR_STATE_OPEN
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirThingEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData
PARALLEL_UPDATES = 0
@@ -26,24 +26,24 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir cover platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[CoverEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
for zone_key, zone in ac_device["zones"].items():
# Only add zone vent controls when zone in vent control mode.
if zone["type"] == 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
if things := instance.coordinator.data.get("myThings"):
entities.append(AdvantageAirZoneVent(coordinator, ac_key, zone_key))
if things := coordinator.data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] in [1, 2]: # 1 = "Blind", 2 = "Blind 2"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.BLIND)
AdvantageAirThingCover(coordinator, thing, CoverDeviceClass.BLIND)
)
elif thing["channelDipState"] in [3, 10]: # 3 & 10 = "Garage door"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.GARAGE)
AdvantageAirThingCover(coordinator, thing, CoverDeviceClass.GARAGE)
)
async_add_entities(entities)
@@ -58,9 +58,11 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
| CoverEntityFeature.SET_POSITION
)
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Vent."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = self._zone["name"]
@property
@@ -106,12 +108,12 @@ class AdvantageAirThingCover(AdvantageAirThingEntity, CoverEntity):
def __init__(
self,
instance: AdvantageAirData,
coordinator: AdvantageAirCoordinator,
thing: dict[str, Any],
device_class: CoverDeviceClass,
) -> None:
"""Initialize an Advantage Air Things Cover."""
super().__init__(instance, thing)
super().__init__(coordinator, thing)
self._attr_device_class = device_class
@property

View File

@@ -27,7 +27,7 @@ async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: AdvantageAirDataConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
data = config_entry.runtime_data.coordinator.data
data = config_entry.runtime_data.data
# Return only the relevant children
return {

View File

@@ -9,17 +9,17 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .models import AdvantageAirData
from .coordinator import AdvantageAirCoordinator
class AdvantageAirEntity(CoordinatorEntity):
class AdvantageAirEntity(CoordinatorEntity[AdvantageAirCoordinator]):
"""Parent class for Advantage Air Entities."""
_attr_has_entity_name = True
def __init__(self, instance: AdvantageAirData) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator) -> None:
"""Initialize common aspects of an Advantage Air entity."""
super().__init__(instance.coordinator)
super().__init__(coordinator)
self._attr_unique_id: str = self.coordinator.data["system"]["rid"]
def update_handle_factory(self, func, *keys):
@@ -41,9 +41,9 @@ class AdvantageAirEntity(CoordinatorEntity):
class AdvantageAirAcEntity(AdvantageAirEntity):
"""Parent class for Advantage Air AC Entities."""
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize common aspects of an Advantage Air ac entity."""
super().__init__(instance)
super().__init__(coordinator)
self.ac_key: str = ac_key
self._attr_unique_id += f"-{ac_key}"
@@ -56,7 +56,7 @@ class AdvantageAirAcEntity(AdvantageAirEntity):
name=self.coordinator.data["aircons"][self.ac_key]["info"]["name"],
)
self.async_update_ac = self.update_handle_factory(
instance.api.aircon.async_update_ac, self.ac_key
coordinator.api.aircon.async_update_ac, self.ac_key
)
@property
@@ -73,14 +73,16 @@ class AdvantageAirAcEntity(AdvantageAirEntity):
class AdvantageAirZoneEntity(AdvantageAirAcEntity):
"""Parent class for Advantage Air Zone Entities."""
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize common aspects of an Advantage Air zone entity."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self.zone_key: str = zone_key
self._attr_unique_id += f"-{zone_key}"
self.async_update_zone = self.update_handle_factory(
instance.api.aircon.async_update_zone, self.ac_key, self.zone_key
coordinator.api.aircon.async_update_zone, self.ac_key, self.zone_key
)
@property
@@ -93,9 +95,11 @@ class AdvantageAirThingEntity(AdvantageAirEntity):
_attr_name = None
def __init__(self, instance: AdvantageAirData, thing: dict[str, Any]) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, thing: dict[str, Any]
) -> None:
"""Initialize common aspects of an Advantage Air Things entity."""
super().__init__(instance)
super().__init__(coordinator)
self._id = thing["id"]
self._attr_unique_id += f"-{self._id}"
@@ -108,7 +112,7 @@ class AdvantageAirThingEntity(AdvantageAirEntity):
name=thing["name"],
)
self.async_update_value = self.update_handle_factory(
instance.api.things.async_update_value, self._id
coordinator.api.things.async_update_value, self._id
)
@property
@@ -117,7 +121,7 @@ class AdvantageAirThingEntity(AdvantageAirEntity):
return self.coordinator.data["myThings"]["things"][self._id]
@property
def is_on(self):
def is_on(self) -> bool:
"""Return if the thing is considered on."""
return self._data["value"] > 0

View File

@@ -9,8 +9,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .const import ADVANTAGE_AIR_STATE_ON, DOMAIN
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirEntity, AdvantageAirThingEntity
from .models import AdvantageAirData
async def async_setup_entry(
@@ -20,21 +20,21 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir light platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[LightEntity] = []
if my_lights := instance.coordinator.data.get("myLights"):
if my_lights := coordinator.data.get("myLights"):
for light in my_lights["lights"].values():
if light.get("relay"):
entities.append(AdvantageAirLight(instance, light))
entities.append(AdvantageAirLight(coordinator, light))
else:
entities.append(AdvantageAirLightDimmable(instance, light))
if things := instance.coordinator.data.get("myThings"):
entities.append(AdvantageAirLightDimmable(coordinator, light))
if things := coordinator.data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] == 4: # 4 = "Light (on/off)""
entities.append(AdvantageAirThingLight(instance, thing))
entities.append(AdvantageAirThingLight(coordinator, thing))
elif thing["channelDipState"] == 5: # 5 = "Light (Dimmable)""
entities.append(AdvantageAirThingLightDimmable(instance, thing))
entities.append(AdvantageAirThingLightDimmable(coordinator, thing))
async_add_entities(entities)
@@ -45,9 +45,11 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
_attr_supported_color_modes = {ColorMode.ONOFF}
_attr_name = None
def __init__(self, instance: AdvantageAirData, light: dict[str, Any]) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, light: dict[str, Any]
) -> None:
"""Initialize an Advantage Air Light."""
super().__init__(instance)
super().__init__(coordinator)
self._id: str = light["id"]
self._attr_unique_id += f"-{self._id}"
@@ -59,7 +61,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
name=light["name"],
)
self.async_update_state = self.update_handle_factory(
instance.api.lights.async_update_state, self._id
coordinator.api.lights.async_update_state, self._id
)
@property
@@ -87,11 +89,13 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
_attr_color_mode = ColorMode.BRIGHTNESS
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
def __init__(self, instance: AdvantageAirData, light: dict[str, Any]) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, light: dict[str, Any]
) -> None:
"""Initialize an Advantage Air Dimmable Light."""
super().__init__(instance, light)
super().__init__(coordinator, light)
self.async_update_value = self.update_handle_factory(
instance.api.lights.async_update_value, self._id
coordinator.api.lights.async_update_value, self._id
)
@property

View File

@@ -1,17 +0,0 @@
"""The Advantage Air integration models."""
from __future__ import annotations
from dataclasses import dataclass
from advantage_air import advantage_air
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@dataclass
class AdvantageAirData:
"""Data for the Advantage Air integration."""
coordinator: DataUpdateCoordinator
api: advantage_air

View File

@@ -1,16 +1,9 @@
rules:
# Bronze
action-setup:
status: todo
comment: https://developers.home-assistant.io/blog/2025/09/25/entity-services-api-changes/
action-setup: done
appropriate-polling: done
brands: done
common-modules:
status: todo
comment: |
Move coordinator from __init__.py to coordinator.py.
Consider using entity descriptions for binary_sensor and switch.
Consider simplifying climate supported features flow.
common-modules: done
config-flow-test-coverage:
status: todo
comment: |
@@ -33,9 +26,7 @@ rules:
comment: Entities do not explicitly subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data:
status: done
comment: Consider extending coordinator to access API via coordinator and remove extra dataclass.
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
@@ -92,7 +83,7 @@ rules:
entity-translations: todo
exception-translations:
status: todo
comment: UpdateFailed in the coordinator
comment: HomeAssistantError in entity.py and ServiceValidationError in climate.py
icon-translations: todo
reconfiguration-flow: todo
repair-issues:

View File

@@ -5,8 +5,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity
from .models import AdvantageAirData
ADVANTAGE_AIR_INACTIVE = "Inactive"
@@ -18,10 +18,12 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir select platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
if aircons := instance.coordinator.data.get("aircons"):
async_add_entities(AdvantageAirMyZone(instance, ac_key) for ac_key in aircons)
if aircons := coordinator.data.get("aircons"):
async_add_entities(
AdvantageAirMyZone(coordinator, ac_key) for ac_key in aircons
)
class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
@@ -30,16 +32,16 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
_attr_icon = "mdi:home-thermometer"
_attr_name = "MyZone"
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air MyZone control."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-myzone"
self._attr_options = [ADVANTAGE_AIR_INACTIVE]
self._number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
self._name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}
if "aircons" in instance.coordinator.data:
for zone in instance.coordinator.data["aircons"][ac_key]["zones"].values():
if "aircons" in coordinator.data:
for zone in coordinator.data["aircons"][ac_key]["zones"].values():
if zone["type"] > 0:
self._name_to_number[zone["name"]] = zone["number"]
self._number_to_name[zone["number"]] = zone["name"]

View File

@@ -16,8 +16,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .const import ADVANTAGE_AIR_STATE_OPEN
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirZoneEntity
from .models import AdvantageAirData
ADVANTAGE_AIR_SET_COUNTDOWN_VALUE = "minutes"
ADVANTAGE_AIR_SET_COUNTDOWN_UNIT = "min"
@@ -32,21 +32,23 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir sensor platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[SensorEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirTimeTo(instance, ac_key, "On"))
entities.append(AdvantageAirTimeTo(instance, ac_key, "Off"))
entities.append(AdvantageAirTimeTo(coordinator, ac_key, "On"))
entities.append(AdvantageAirTimeTo(coordinator, ac_key, "Off"))
for zone_key, zone in ac_device["zones"].items():
# Only show damper and temp sensors when zone is in temperature control
if zone["type"] != 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
entities.append(AdvantageAirZoneTemp(instance, ac_key, zone_key))
entities.append(AdvantageAirZoneVent(coordinator, ac_key, zone_key))
entities.append(AdvantageAirZoneTemp(coordinator, ac_key, zone_key))
# Only show wireless signal strength sensors when using wireless sensors
if zone["rssi"] > 0:
entities.append(AdvantageAirZoneSignal(instance, ac_key, zone_key))
entities.append(
AdvantageAirZoneSignal(coordinator, ac_key, zone_key)
)
async_add_entities(entities)
@@ -56,9 +58,11 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
_attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
_attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance: AdvantageAirData, ac_key: str, action: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, action: str
) -> None:
"""Initialize the Advantage Air timer control."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self.action = action
self._time_key = f"countDownTo{action}"
self._attr_name = f"Time to {action}"
@@ -89,9 +93,11 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Vent Sensor."""
super().__init__(instance, ac_key, zone_key=zone_key)
super().__init__(coordinator, ac_key, zone_key=zone_key)
self._attr_name = f"{self._zone['name']} vent"
self._attr_unique_id += "-vent"
@@ -117,9 +123,11 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone wireless signal sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} signal"
self._attr_unique_id += "-signal"
@@ -151,9 +159,11 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
_attr_entity_registry_enabled_default = False
_attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
def __init__(
self, coordinator: AdvantageAirCoordinator, ac_key: str, zone_key: str
) -> None:
"""Initialize an Advantage Air Zone Temp Sensor."""
super().__init__(instance, ac_key, zone_key)
super().__init__(coordinator, ac_key, zone_key)
self._attr_name = f"{self._zone['name']} temperature"
self._attr_unique_id += "-temp"

View File

@@ -10,8 +10,6 @@ from homeassistant.helpers import config_validation as cv, service
from .const import DOMAIN
ADVANTAGE_AIR_SERVICE_SET_TIME_TO = "set_time_to"
@callback
def async_setup_services(hass: HomeAssistant) -> None:
@@ -20,7 +18,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
"set_time_to",
entity_domain=SENSOR_DOMAIN,
schema={vol.Required("minutes"): cv.positive_int},
func="set_time_to",

View File

@@ -17,6 +17,11 @@
}
}
},
"exceptions": {
"update_failed": {
"message": "An error occurred while updating from the Advantage Air API: {error}"
}
},
"services": {
"set_time_to": {
"description": "Controls timers to turn the system on or off after a set number of minutes.",

View File

@@ -13,8 +13,8 @@ from .const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
)
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirAcEntity, AdvantageAirThingEntity
from .models import AdvantageAirData
async def async_setup_entry(
@@ -24,20 +24,20 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir switch platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
entities: list[SwitchEntity] = []
if aircons := instance.coordinator.data.get("aircons"):
if aircons := coordinator.data.get("aircons"):
for ac_key, ac_device in aircons.items():
if ac_device["info"]["freshAirStatus"] != "none":
entities.append(AdvantageAirFreshAir(instance, ac_key))
entities.append(AdvantageAirFreshAir(coordinator, ac_key))
if ADVANTAGE_AIR_AUTOFAN_ENABLED in ac_device["info"]:
entities.append(AdvantageAirMyFan(instance, ac_key))
entities.append(AdvantageAirMyFan(coordinator, ac_key))
if ADVANTAGE_AIR_NIGHT_MODE_ENABLED in ac_device["info"]:
entities.append(AdvantageAirNightMode(instance, ac_key))
if things := instance.coordinator.data.get("myThings"):
entities.append(AdvantageAirNightMode(coordinator, ac_key))
if things := coordinator.data.get("myThings"):
entities.extend(
AdvantageAirRelay(instance, thing)
AdvantageAirRelay(coordinator, thing)
for thing in things["things"].values()
if thing["channelDipState"] == 8 # 8 = Other relay
)
@@ -51,9 +51,9 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
_attr_name = "Fresh air"
_attr_device_class = SwitchDeviceClass.SWITCH
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air fresh air control."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-freshair"
@property
@@ -77,9 +77,9 @@ class AdvantageAirMyFan(AdvantageAirAcEntity, SwitchEntity):
_attr_name = "MyFan"
_attr_device_class = SwitchDeviceClass.SWITCH
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air MyFan control."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-myfan"
@property
@@ -103,9 +103,9 @@ class AdvantageAirNightMode(AdvantageAirAcEntity, SwitchEntity):
_attr_name = "MySleep$aver"
_attr_device_class = SwitchDeviceClass.SWITCH
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
"""Initialize an Advantage Air Night Mode control."""
super().__init__(instance, ac_key)
super().__init__(coordinator, ac_key)
self._attr_unique_id += "-nightmode"
@property

View File

@@ -7,8 +7,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AdvantageAirDataConfigEntry
from .const import DOMAIN
from .coordinator import AdvantageAirCoordinator
from .entity import AdvantageAirEntity
from .models import AdvantageAirData
async def async_setup_entry(
@@ -18,9 +18,9 @@ async def async_setup_entry(
) -> None:
"""Set up AdvantageAir update platform."""
instance = config_entry.runtime_data
coordinator = config_entry.runtime_data
async_add_entities([AdvantageAirApp(instance)])
async_add_entities([AdvantageAirApp(coordinator)])
class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
@@ -28,9 +28,9 @@ class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
_attr_name = "App"
def __init__(self, instance: AdvantageAirData) -> None:
def __init__(self, coordinator: AdvantageAirCoordinator) -> None:
"""Initialize the Advantage Air App."""
super().__init__(instance)
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self.coordinator.data["system"]["rid"])},
manufacturer="Advantage Air",

View File

@@ -74,7 +74,7 @@ class AemetWeather(
self._attr_unique_id = unique_id
@property
def condition(self):
def condition(self) -> str | None:
"""Return the current condition."""
cond = self.get_aemet_value([AOD_WEATHER, AOD_CONDITION])
return CONDITIONS_MAP.get(cond)
@@ -90,31 +90,31 @@ class AemetWeather(
return self.get_aemet_forecast(AOD_FORECAST_HOURLY)
@property
def humidity(self):
def humidity(self) -> float | None:
"""Return the humidity."""
return self.get_aemet_value([AOD_WEATHER, AOD_HUMIDITY])
@property
def native_pressure(self):
def native_pressure(self) -> float | None:
"""Return the pressure."""
return self.get_aemet_value([AOD_WEATHER, AOD_PRESSURE])
@property
def native_temperature(self):
def native_temperature(self) -> float | None:
"""Return the temperature."""
return self.get_aemet_value([AOD_WEATHER, AOD_TEMP])
@property
def wind_bearing(self):
def wind_bearing(self) -> float | None:
"""Return the wind bearing."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_DIRECTION])
@property
def native_wind_gust_speed(self):
def native_wind_gust_speed(self) -> float | None:
"""Return the wind gust speed in native units."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_SPEED_MAX])
@property
def native_wind_speed(self):
def native_wind_speed(self) -> float | None:
"""Return the wind speed."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_SPEED])

View File

@@ -8,18 +8,12 @@ from homeassistant.helpers import service
from .const import DOMAIN
_DEV_EN_ALT = "enable_alerts"
_DEV_DS_ALT = "disable_alerts"
_DEV_EN_REC = "start_recording"
_DEV_DS_REC = "stop_recording"
_DEV_SNAP = "snapshot"
CAMERA_SERVICES = {
_DEV_EN_ALT: "async_enable_alerts",
_DEV_DS_ALT: "async_disable_alerts",
_DEV_EN_REC: "async_start_recording",
_DEV_DS_REC: "async_stop_recording",
_DEV_SNAP: "async_snapshot",
"enable_alerts": "async_enable_alerts",
"disable_alerts": "async_disable_alerts",
"start_recording": "async_start_recording",
"stop_recording": "async_stop_recording",
"snapshot": "async_snapshot",
}

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_DOMAIN
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -75,9 +75,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirlyConfigEntry) -> boo
# Remove air_quality entities from registry if they exist
ent_reg = er.async_get(hass)
unique_id = f"{coordinator.latitude}-{coordinator.longitude}"
if entity_id := ent_reg.async_get_entity_id(
AIR_QUALITY_PLATFORM, DOMAIN, unique_id
):
if entity_id := ent_reg.async_get_entity_id(AIR_QUALITY_DOMAIN, DOMAIN, unique_id):
_LOGGER.debug("Removing deprecated air_quality entity %s", entity_id)
ent_reg.async_remove(entity_id)

View File

@@ -93,7 +93,6 @@ class AirobotNumber(AirobotEntity, NumberEntity):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="set_value_failed",
translation_placeholders={"error": str(err)},
) from err
else:
await self.coordinator.async_request_refresh()

View File

@@ -112,7 +112,7 @@
"message": "Failed to set temperature to {temperature}."
},
"set_value_failed": {
"message": "Failed to set value: {error}"
"message": "Failed to set value."
},
"switch_turn_off_failed": {
"message": "Failed to turn off {switch}."

View File

@@ -4,7 +4,16 @@ from __future__ import annotations
import logging
from airos.airos6 import AirOS6
from airos.airos8 import AirOS8
from airos.exceptions import (
AirOSConnectionAuthenticationError,
AirOSConnectionSetupError,
AirOSDataMissingError,
AirOSDeviceConnectionError,
AirOSKeyDataMissingError,
)
from airos.helpers import DetectDeviceData, async_get_firmware_data
from homeassistant.const import (
CONF_HOST,
@@ -15,6 +24,11 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
ConfigEntryError,
ConfigEntryNotReady,
)
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -23,6 +37,7 @@ from .coordinator import AirOSConfigEntry, AirOSDataUpdateCoordinator
_PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.SENSOR,
]
@@ -38,15 +53,40 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> boo
hass, verify_ssl=entry.data[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL]
)
airos_device = AirOS8(
host=entry.data[CONF_HOST],
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
session=session,
use_ssl=entry.data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
conn_data = {
CONF_HOST: entry.data[CONF_HOST],
CONF_USERNAME: entry.data[CONF_USERNAME],
CONF_PASSWORD: entry.data[CONF_PASSWORD],
"use_ssl": entry.data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
"session": session,
}
# Determine firmware version before creating the device instance
try:
device_data: DetectDeviceData = await async_get_firmware_data(**conn_data)
except (
AirOSConnectionSetupError,
AirOSDeviceConnectionError,
TimeoutError,
) as err:
raise ConfigEntryNotReady from err
except (
AirOSConnectionAuthenticationError,
AirOSDataMissingError,
) as err:
raise ConfigEntryAuthFailed from err
except AirOSKeyDataMissingError as err:
raise ConfigEntryError("key_data_missing") from err
except Exception as err:
raise ConfigEntryError("unknown") from err
airos_class: type[AirOS8 | AirOS6] = (
AirOS8 if device_data["fw_major"] == 8 else AirOS6
)
coordinator = AirOSDataUpdateCoordinator(hass, entry, airos_device)
airos_device = airos_class(**conn_data)
coordinator = AirOSDataUpdateCoordinator(hass, entry, device_data, airos_device)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator

View File

@@ -4,7 +4,9 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Generic, TypeVar
from airos.data import AirOSDataBaseClass
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@@ -18,25 +20,24 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AirOS8Data, AirOSConfigEntry, AirOSDataUpdateCoordinator
from .entity import AirOSEntity
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
AirOSDataModel = TypeVar("AirOSDataModel", bound=AirOSDataBaseClass)
@dataclass(frozen=True, kw_only=True)
class AirOSBinarySensorEntityDescription(BinarySensorEntityDescription):
class AirOSBinarySensorEntityDescription(
BinarySensorEntityDescription,
Generic[AirOSDataModel],
):
"""Describe an AirOS binary sensor."""
value_fn: Callable[[AirOS8Data], bool]
value_fn: Callable[[AirOSDataModel], bool]
BINARY_SENSORS: tuple[AirOSBinarySensorEntityDescription, ...] = (
AirOSBinarySensorEntityDescription(
key="portfw",
translation_key="port_forwarding",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.portfw,
),
AirOS8BinarySensorEntityDescription = AirOSBinarySensorEntityDescription[AirOS8Data]
COMMON_BINARY_SENSORS: tuple[AirOSBinarySensorEntityDescription, ...] = (
AirOSBinarySensorEntityDescription(
key="dhcp_client",
translation_key="dhcp_client",
@@ -52,14 +53,6 @@ BINARY_SENSORS: tuple[AirOSBinarySensorEntityDescription, ...] = (
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",
@@ -70,6 +63,23 @@ BINARY_SENSORS: tuple[AirOSBinarySensorEntityDescription, ...] = (
),
)
AIROS8_BINARY_SENSORS: tuple[AirOS8BinarySensorEntityDescription, ...] = (
AirOS8BinarySensorEntityDescription(
key="portfw",
translation_key="port_forwarding",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.portfw,
),
AirOS8BinarySensorEntityDescription(
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,
),
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -79,9 +89,18 @@ async def async_setup_entry(
"""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
)
entities = [
AirOSBinarySensor(coordinator, description)
for description in COMMON_BINARY_SENSORS
]
if coordinator.device_data["fw_major"] == 8:
entities.extend(
AirOSBinarySensor(coordinator, description)
for description in AIROS8_BINARY_SENSORS
)
async_add_entities(entities)
class AirOSBinarySensor(AirOSEntity, BinarySensorEntity):

View File

@@ -0,0 +1,69 @@
"""AirOS button component for Home Assistant."""
from __future__ import annotations
from airos.exceptions import AirOSException
from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import DOMAIN, AirOSConfigEntry, AirOSDataUpdateCoordinator
from .entity import AirOSEntity
PARALLEL_UPDATES = 0
REBOOT_BUTTON = ButtonEntityDescription(
key="reboot",
device_class=ButtonDeviceClass.RESTART,
entity_registry_enabled_default=False,
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AirOSConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the AirOS button from a config entry."""
async_add_entities([AirOSRebootButton(config_entry.runtime_data, REBOOT_BUTTON)])
class AirOSRebootButton(AirOSEntity, ButtonEntity):
"""Button to reboot device."""
entity_description: ButtonEntityDescription
def __init__(
self,
coordinator: AirOSDataUpdateCoordinator,
description: ButtonEntityDescription,
) -> None:
"""Initialize the AirOS client button."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.data.derived.mac}_{description.key}"
async def async_press(self) -> None:
"""Handle the button press to reboot the device."""
try:
await self.coordinator.airos_device.login()
result = await self.coordinator.airos_device.reboot()
except AirOSException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from err
if not result:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="reboot_failed",
) from None

View File

@@ -2,17 +2,24 @@
from __future__ import annotations
import asyncio
from collections.abc import Mapping
import logging
from typing import Any
from airos.airos6 import AirOS6
from airos.airos8 import AirOS8
from airos.discovery import airos_discover_devices
from airos.exceptions import (
AirOSConnectionAuthenticationError,
AirOSConnectionSetupError,
AirOSDataMissingError,
AirOSDeviceConnectionError,
AirOSEndpointError,
AirOSKeyDataMissingError,
AirOSListenerError,
)
from airos.helpers import DetectDeviceData, async_get_firmware_data
import voluptuous as vol
from homeassistant.config_entries import (
@@ -30,21 +37,36 @@ from homeassistant.const import (
)
from homeassistant.data_entry_flow import section
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN, SECTION_ADVANCED_SETTINGS
from .coordinator import AirOS8
from .const import (
DEFAULT_SSL,
DEFAULT_USERNAME,
DEFAULT_VERIFY_SSL,
DEVICE_NAME,
DOMAIN,
HOSTNAME,
IP_ADDRESS,
MAC_ADDRESS,
SECTION_ADVANCED_SETTINGS,
)
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
AirOSDeviceDetect = AirOS8 | AirOS6
# Discovery duration in seconds, airOS announces every 20 seconds
DISCOVER_INTERVAL: int = 30
STEP_DISCOVERY_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_USERNAME, default="ubnt"): str,
vol.Required(CONF_USERNAME, default=DEFAULT_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(SECTION_ADVANCED_SETTINGS): section(
vol.Schema(
@@ -58,6 +80,10 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
}
)
STEP_MANUAL_DATA_SCHEMA = STEP_DISCOVERY_DATA_SCHEMA.extend(
{vol.Required(CONF_HOST): str}
)
class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Ubiquiti airOS."""
@@ -65,14 +91,29 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 2
MINOR_VERSION = 1
_discovery_task: asyncio.Task | None = None
def __init__(self) -> None:
"""Initialize the config flow."""
super().__init__()
self.airos_device: AirOS8
self.airos_device: AirOSDeviceDetect
self.errors: dict[str, str] = {}
self.discovered_devices: dict[str, dict[str, Any]] = {}
self.discovery_abort_reason: str | None = None
self.selected_device_info: dict[str, Any] = {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
self.errors = {}
return self.async_show_menu(
step_id="user", menu_options=["discovery", "manual"]
)
async def async_step_manual(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the manual input of host and credentials."""
self.errors = {}
@@ -84,7 +125,7 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
data=validated_info["data"],
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=self.errors
step_id="manual", data_schema=STEP_MANUAL_DATA_SCHEMA, errors=self.errors
)
async def _validate_and_get_device_info(
@@ -98,16 +139,14 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
verify_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL],
)
airos_device = AirOS8(
host=config_data[CONF_HOST],
username=config_data[CONF_USERNAME],
password=config_data[CONF_PASSWORD],
session=session,
use_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
)
try:
await airos_device.login()
airos_data = await airos_device.status()
device_data: DetectDeviceData = await async_get_firmware_data(
host=config_data[CONF_HOST],
username=config_data[CONF_USERNAME],
password=config_data[CONF_PASSWORD],
session=session,
use_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
)
except (
AirOSConnectionSetupError,
@@ -122,14 +161,14 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception during credential validation")
self.errors["base"] = "unknown"
else:
await self.async_set_unique_id(airos_data.derived.mac)
await self.async_set_unique_id(device_data["mac"])
if self.source in [SOURCE_REAUTH, SOURCE_RECONFIGURE]:
self._abort_if_unique_id_mismatch()
else:
self._abort_if_unique_id_configured()
return {"title": airos_data.host.hostname, "data": config_data}
return {"title": device_data["hostname"], "data": config_data}
return None
@@ -220,3 +259,175 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
),
errors=self.errors,
)
async def async_step_discovery(
self,
discovery_info: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Start the discovery process."""
if self._discovery_task and self._discovery_task.done():
self._discovery_task = None
# Handle appropriate 'errors' as abort through progress_done
if self.discovery_abort_reason:
return self.async_show_progress_done(
next_step_id=self.discovery_abort_reason
)
# Abort through progress_done if no devices were found
if not self.discovered_devices:
_LOGGER.debug(
"No (new or unconfigured) airOS devices found during discovery"
)
return self.async_show_progress_done(
next_step_id="discovery_no_devices"
)
# Skip selecting a device if only one new/unconfigured device was found
if len(self.discovered_devices) == 1:
self.selected_device_info = list(self.discovered_devices.values())[0]
return self.async_show_progress_done(next_step_id="configure_device")
return self.async_show_progress_done(next_step_id="select_device")
if not self._discovery_task:
self.discovered_devices = {}
self._discovery_task = self.hass.async_create_task(
self._async_run_discovery_with_progress()
)
# Show the progress bar and wait for discovery to complete
return self.async_show_progress(
step_id="discovery",
progress_action="discovering",
progress_task=self._discovery_task,
description_placeholders={"seconds": str(DISCOVER_INTERVAL)},
)
async def async_step_select_device(
self,
discovery_info: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Select a discovered device."""
if discovery_info is not None:
selected_mac = discovery_info[MAC_ADDRESS]
self.selected_device_info = self.discovered_devices[selected_mac]
return await self.async_step_configure_device()
list_options = {
mac: f"{device.get(HOSTNAME, mac)} ({device.get(IP_ADDRESS, DEVICE_NAME)})"
for mac, device in self.discovered_devices.items()
}
return self.async_show_form(
step_id="select_device",
data_schema=vol.Schema({vol.Required(MAC_ADDRESS): vol.In(list_options)}),
)
async def async_step_configure_device(
self,
user_input: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""Configure the selected device."""
self.errors = {}
if user_input is not None:
config_data = {
**user_input,
CONF_HOST: self.selected_device_info[IP_ADDRESS],
}
validated_info = await self._validate_and_get_device_info(config_data)
if validated_info:
return self.async_create_entry(
title=validated_info["title"],
data=validated_info["data"],
)
device_name = self.selected_device_info.get(
HOSTNAME, self.selected_device_info.get(IP_ADDRESS, DEVICE_NAME)
)
return self.async_show_form(
step_id="configure_device",
data_schema=STEP_DISCOVERY_DATA_SCHEMA,
errors=self.errors,
description_placeholders={"device_name": device_name},
)
async def _async_run_discovery_with_progress(self) -> None:
"""Run discovery with an embedded progress update loop."""
progress_bar = self.hass.async_create_task(self._async_update_progress_bar())
known_mac_addresses = {
entry.unique_id.lower()
for entry in self.hass.config_entries.async_entries(DOMAIN)
if entry.unique_id
}
try:
devices = await airos_discover_devices(DISCOVER_INTERVAL)
except AirOSEndpointError:
self.discovery_abort_reason = "discovery_detect_error"
except AirOSListenerError:
self.discovery_abort_reason = "discovery_listen_error"
except Exception:
self.discovery_abort_reason = "discovery_failed"
_LOGGER.exception("An error occurred during discovery")
else:
self.discovered_devices = {
mac_addr: info
for mac_addr, info in devices.items()
if mac_addr.lower() not in known_mac_addresses
}
_LOGGER.debug(
"Discovery task finished. Found %s new devices",
len(self.discovered_devices),
)
finally:
progress_bar.cancel()
async def _async_update_progress_bar(self) -> None:
"""Update progress bar every second."""
try:
for i in range(DISCOVER_INTERVAL):
progress = (i + 1) / DISCOVER_INTERVAL
self.async_update_progress(progress)
await asyncio.sleep(1)
except asyncio.CancelledError:
pass
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Automatically handle a DHCP discovered IP change."""
ip_address = discovery_info.ip
# python-airos defaults to upper for derived mac_address
normalized_mac = format_mac(discovery_info.macaddress).upper()
await self.async_set_unique_id(normalized_mac)
self._abort_if_unique_id_configured(updates={CONF_HOST: ip_address})
return self.async_abort(reason="unreachable")
async def async_step_discovery_no_devices(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Abort if discovery finds no (unconfigured) devices."""
return self.async_abort(reason="no_devices_found")
async def async_step_discovery_listen_error(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Abort if discovery is unable to listen on the port."""
return self.async_abort(reason="listen_error")
async def async_step_discovery_detect_error(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Abort if discovery receives incorrect broadcasts."""
return self.async_abort(reason="detect_error")
async def async_step_discovery_failed(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Abort if discovery fails for other reasons."""
return self.async_abort(reason="discovery_failed")

View File

@@ -12,3 +12,10 @@ DEFAULT_VERIFY_SSL = False
DEFAULT_SSL = True
SECTION_ADVANCED_SETTINGS = "advanced_settings"
# Discovery related
DEFAULT_USERNAME = "ubnt"
HOSTNAME = "hostname"
IP_ADDRESS = "ip_address"
MAC_ADDRESS = "mac_address"
DEVICE_NAME = "airOS device"

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import logging
from airos.airos6 import AirOS6, AirOS6Data
from airos.airos8 import AirOS8, AirOS8Data
from airos.exceptions import (
AirOSConnectionAuthenticationError,
@@ -11,6 +12,7 @@ from airos.exceptions import (
AirOSDataMissingError,
AirOSDeviceConnectionError,
)
from airos.helpers import DetectDeviceData
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -21,19 +23,28 @@ from .const import DOMAIN, SCAN_INTERVAL
_LOGGER = logging.getLogger(__name__)
AirOSDeviceDetect = AirOS8 | AirOS6
AirOSDataDetect = AirOS8Data | AirOS6Data
type AirOSConfigEntry = ConfigEntry[AirOSDataUpdateCoordinator]
class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOS8Data]):
class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOSDataDetect]):
"""Class to manage fetching AirOS data from single endpoint."""
airos_device: AirOSDeviceDetect
config_entry: AirOSConfigEntry
def __init__(
self, hass: HomeAssistant, config_entry: AirOSConfigEntry, airos_device: AirOS8
self,
hass: HomeAssistant,
config_entry: AirOSConfigEntry,
device_data: DetectDeviceData,
airos_device: AirOSDeviceDetect,
) -> None:
"""Initialize the coordinator."""
self.airos_device = airos_device
self.device_data = device_data
super().__init__(
hass,
_LOGGER,
@@ -42,7 +53,7 @@ class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOS8Data]):
update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self) -> AirOS8Data:
async def _async_update_data(self) -> AirOSDataDetect:
"""Fetch data from AirOS."""
try:
await self.airos_device.login()
@@ -62,7 +73,7 @@ class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOS8Data]):
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from err
except (AirOSDataMissingError,) as err:
except AirOSDataMissingError as err:
_LOGGER.error("Expected data not returned by airOS device: %s", err)
raise UpdateFailed(
translation_domain=DOMAIN,

View File

@@ -3,9 +3,10 @@
"name": "Ubiquiti airOS",
"codeowners": ["@CoMPaTech"],
"config_flow": true,
"dhcp": [{ "registered_devices": true }],
"documentation": "https://www.home-assistant.io/integrations/airos",
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "silver",
"requirements": ["airos==0.6.3"]
"quality_scale": "platinum",
"requirements": ["airos==0.6.4"]
}

View File

@@ -42,16 +42,20 @@ rules:
# Gold
devices: done
diagnostics: done
discovery-update-info: todo
discovery: todo
discovery-update-info: done
discovery:
status: exempt
comment: No way to detect device on the network
docs-data-update: done
docs-examples: todo
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: todo
dynamic-devices:
status: exempt
comment: single airOS device per config entry; peer/remote endpoints are not modeled as child devices/entities at this time
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
@@ -61,8 +65,10 @@ rules:
status: exempt
comment: no (custom) icons used or envisioned
reconfiguration-flow: done
repair-issues: todo
stale-devices: todo
repair-issues: done
stale-devices:
status: exempt
comment: single airOS device per config entry; peer/remote endpoints are not modeled as child devices/entities at this time
# Platinum
async-dependency: done

View File

@@ -5,8 +5,14 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Generic, TypeVar
from airos.data import DerivedWirelessMode, DerivedWirelessRole, NetRole
from airos.data import (
AirOSDataBaseClass,
DerivedWirelessMode,
DerivedWirelessRole,
NetRole,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -37,15 +43,19 @@ WIRELESS_ROLE_OPTIONS = [mode.value for mode in DerivedWirelessRole]
PARALLEL_UPDATES = 0
AirOSDataModel = TypeVar("AirOSDataModel", bound=AirOSDataBaseClass)
@dataclass(frozen=True, kw_only=True)
class AirOSSensorEntityDescription(SensorEntityDescription):
class AirOSSensorEntityDescription(SensorEntityDescription, Generic[AirOSDataModel]):
"""Describe an AirOS sensor."""
value_fn: Callable[[AirOS8Data], StateType]
value_fn: Callable[[AirOSDataModel], StateType]
SENSORS: tuple[AirOSSensorEntityDescription, ...] = (
AirOS8SensorEntityDescription = AirOSSensorEntityDescription[AirOS8Data]
COMMON_SENSORS: tuple[AirOSSensorEntityDescription, ...] = (
AirOSSensorEntityDescription(
key="host_cpuload",
translation_key="host_cpuload",
@@ -75,54 +85,6 @@ SENSORS: tuple[AirOSSensorEntityDescription, ...] = (
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",
@@ -158,6 +120,57 @@ SENSORS: tuple[AirOSSensorEntityDescription, ...] = (
options=WIRELESS_ROLE_OPTIONS,
entity_registry_enabled_default=False,
),
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_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,
),
)
AIROS8_SENSORS: tuple[AirOS8SensorEntityDescription, ...] = (
AirOS8SensorEntityDescription(
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,
),
AirOS8SensorEntityDescription(
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,
),
)
@@ -169,7 +182,14 @@ async def async_setup_entry(
"""Set up the AirOS sensors from a config entry."""
coordinator = config_entry.runtime_data
async_add_entities(AirOSSensor(coordinator, description) for description in SENSORS)
entities = [AirOSSensor(coordinator, description) for description in COMMON_SENSORS]
if coordinator.device_data["fw_major"] == 8:
entities.extend(
AirOSSensor(coordinator, description) for description in AIROS8_SENSORS
)
async_add_entities(entities)
class AirOSSensor(AirOSEntity, SensorEntity):

View File

@@ -2,6 +2,10 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"detect_error": "Unable to process discovered devices data, check the documentation for supported devices",
"discovery_failed": "Unable to start discovery, check logs for details",
"listen_error": "Unable to start listening for devices",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unique_id_mismatch": "Re-authentication should be used for the same device not a new one"
@@ -13,37 +17,36 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"flow_title": "Ubiquiti airOS device",
"progress": {
"connecting": "Connecting to the airOS device",
"discovering": "Listening for any airOS devices for {seconds} seconds"
},
"step": {
"reauth_confirm": {
"configure_device": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::user::data_description::password%]"
}
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::user::data_description::password%]"
"password": "[%key:component::airos::config::step::manual::data_description::password%]",
"username": "[%key:component::airos::config::step::manual::data_description::username%]"
},
"description": "Enter the username and password for {device_name}",
"sections": {
"advanced_settings": {
"data": {
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data::ssl%]",
"ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::ssl%]",
"verify_ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::verify_ssl%]"
"ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data_description::ssl%]",
"verify_ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data_description::verify_ssl%]"
},
"name": "[%key:component::airos::config::step::user::sections::advanced_settings::name%]"
"name": "[%key:component::airos::config::step::manual::sections::advanced_settings::name%]"
}
}
},
"user": {
"manual": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]",
@@ -67,6 +70,49 @@
"name": "Advanced settings"
}
}
},
"reauth_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::manual::data_description::password%]"
}
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airos::config::step::manual::data_description::password%]"
},
"sections": {
"advanced_settings": {
"data": {
"ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data_description::ssl%]",
"verify_ssl": "[%key:component::airos::config::step::manual::sections::advanced_settings::data_description::verify_ssl%]"
},
"name": "[%key:component::airos::config::step::manual::sections::advanced_settings::name%]"
}
}
},
"select_device": {
"data": {
"mac_address": "Select the device to configure"
},
"data_description": {
"mac_address": "Select the device MAC address"
}
},
"user": {
"menu_options": {
"discovery": "Listen for airOS devices on the network",
"manual": "Manually configure airOS device"
}
}
}
},
@@ -157,6 +203,9 @@
},
"key_data_missing": {
"message": "Key data not returned from device"
},
"reboot_failed": {
"message": "The device did not accept the reboot request. Try again, or check your device web interface for errors."
}
}
}

View File

@@ -18,6 +18,10 @@ from homeassistant.helpers.schema_config_entry_flow import (
SchemaOptionsFlowHandler,
)
from homeassistant.helpers.selector import BooleanSelector
from homeassistant.helpers.service_info.zeroconf import (
ATTR_PROPERTIES_ID,
ZeroconfServiceInfo,
)
from .const import CONF_CLIP_NEGATIVE, CONF_RETURN_AVERAGE, DOMAIN
@@ -46,6 +50,9 @@ class AirQConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
_discovered_host: str
_discovered_name: str
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -90,6 +97,58 @@ class AirQConfigFlow(ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
async def async_step_zeroconf(
self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle zeroconf discovery of an air-Q device."""
self._discovered_host = discovery_info.host
self._discovered_name = discovery_info.properties.get("devicename", "air-Q")
device_id = discovery_info.properties.get(ATTR_PROPERTIES_ID)
if not device_id:
return self.async_abort(reason="incomplete_discovery")
await self.async_set_unique_id(device_id)
self._abort_if_unique_id_configured(
updates={CONF_IP_ADDRESS: self._discovered_host},
reload_on_update=True,
)
self.context["title_placeholders"] = {"name": self._discovered_name}
return await self.async_step_discovery_confirm()
async def async_step_discovery_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle user confirmation of a discovered air-Q device."""
errors: dict[str, str] = {}
if user_input is not None:
session = async_get_clientsession(self.hass)
airq = AirQ(self._discovered_host, user_input[CONF_PASSWORD], session)
try:
await airq.validate()
except ClientConnectionError:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
else:
return self.async_create_entry(
title=self._discovered_name,
data={
CONF_IP_ADDRESS: self._discovered_host,
CONF_PASSWORD: user_input[CONF_PASSWORD],
},
)
return self.async_show_form(
step_id="discovery_confirm",
data_schema=vol.Schema({vol.Required(CONF_PASSWORD): str}),
description_placeholders={"name": self._discovered_name},
errors=errors,
)
@staticmethod
@callback
def async_get_options_flow(

View File

@@ -7,5 +7,13 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioairq"],
"requirements": ["aioairq==0.4.7"]
"requirements": ["aioairq==0.4.7"],
"zeroconf": [
{
"properties": {
"device": "air-q"
},
"type": "_http._tcp.local."
}
]
}

View File

@@ -1,14 +1,23 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"incomplete_discovery": "The discovered air-Q device did not provide a device ID. Ensure the firmware is up to date."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_input": "[%key:common::config_flow::error::invalid_host%]"
},
"flow_title": "{name}",
"step": {
"discovery_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"description": "Do you want to set up **{name}**?",
"title": "Set up air-Q"
},
"user": {
"data": {
"ip_address": "[%key:common::config_flow::data::ip%]",

View File

@@ -117,23 +117,23 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
return super()._handle_coordinator_update()
@property
def current_temperature(self):
def current_temperature(self) -> int:
"""Return the current temperature."""
return self._unit.Temperature
@property
def fan_mode(self):
def fan_mode(self) -> str:
"""Return fan mode of the AC this group belongs to."""
return AT_TO_HA_FAN_SPEED[self._airtouch.acs[self._ac_number].AcFanSpeed]
@property
def fan_modes(self):
def fan_modes(self) -> list[str]:
"""Return the list of available fan modes."""
airtouch_fan_speeds = self._airtouch.GetSupportedFanSpeedsForAc(self._ac_number)
return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
@property
def hvac_mode(self):
def hvac_mode(self) -> HVACMode:
"""Return hvac target hvac state."""
is_off = self._unit.PowerState == "Off"
if is_off:
@@ -236,17 +236,17 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
return self._airtouch.acs[self._unit.BelongsToAc].MaxSetpoint
@property
def current_temperature(self):
def current_temperature(self) -> int:
"""Return the current temperature."""
return self._unit.Temperature
@property
def target_temperature(self):
def target_temperature(self) -> int:
"""Return the temperature we are trying to reach."""
return self._unit.TargetSetpoint
@property
def hvac_mode(self):
def hvac_mode(self) -> HVACMode:
"""Return hvac target hvac state."""
# there are other power states that aren't 'on' but still count as on (eg. 'Turbo')
is_off = self._unit.PowerState == "Off"
@@ -272,12 +272,12 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
self.async_write_ha_state()
@property
def fan_mode(self):
def fan_mode(self) -> str:
"""Return fan mode of the AC this group belongs to."""
return AT_TO_HA_FAN_SPEED[self._airtouch.acs[self._unit.BelongsToAc].AcFanSpeed]
@property
def fan_modes(self):
def fan_modes(self) -> list[str]:
"""Return the list of available fan modes."""
airtouch_fan_speeds = self._airtouch.GetSupportedFanSpeedsByGroup(
self._group_number

View File

@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["airtouch5py"],
"requirements": ["airtouch5py==0.3.0"]
"requirements": ["airtouch5py==0.4.0"]
}

View File

@@ -7,13 +7,7 @@ from datetime import timedelta
from math import ceil
from typing import Any
from pyairvisual.cloud_api import (
CloudAPI,
InvalidKeyError,
KeyExpiredError,
UnauthorizedError,
)
from pyairvisual.errors import AirVisualError
from pyairvisual.cloud_api import CloudAPI
from homeassistant.components import automation
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
@@ -28,14 +22,12 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import (
aiohttp_client,
device_registry as dr,
entity_registry as er,
)
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import (
CONF_CITY,
@@ -47,8 +39,7 @@ from .const import (
INTEGRATION_TYPE_NODE_PRO,
LOGGER,
)
type AirVisualConfigEntry = ConfigEntry[DataUpdateCoordinator]
from .coordinator import AirVisualConfigEntry, AirVisualDataUpdateCoordinator
# We use a raw string for the airvisual_pro domain (instead of importing the actual
# constant) so that we can avoid listing it as a dependency:
@@ -85,8 +76,8 @@ def async_get_cloud_api_update_interval(
@callback
def async_get_cloud_coordinators_by_api_key(
hass: HomeAssistant, api_key: str
) -> list[DataUpdateCoordinator]:
"""Get all DataUpdateCoordinator objects related to a particular API key."""
) -> list[AirVisualDataUpdateCoordinator]:
"""Get all AirVisualDataUpdateCoordinator objects related to a particular API key."""
return [
entry.runtime_data
for entry in hass.config_entries.async_entries(DOMAIN)
@@ -180,38 +171,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirVisualConfigEntry) ->
websession = aiohttp_client.async_get_clientsession(hass)
cloud_api = CloudAPI(entry.data[CONF_API_KEY], session=websession)
async def async_update_data() -> dict[str, Any]:
"""Get new data from the API."""
if CONF_CITY in entry.data:
api_coro = cloud_api.air_quality.city(
entry.data[CONF_CITY],
entry.data[CONF_STATE],
entry.data[CONF_COUNTRY],
)
else:
api_coro = cloud_api.air_quality.nearest_city(
entry.data[CONF_LATITUDE],
entry.data[CONF_LONGITUDE],
)
try:
return await api_coro
except (InvalidKeyError, KeyExpiredError, UnauthorizedError) as ex:
raise ConfigEntryAuthFailed from ex
except AirVisualError as err:
raise UpdateFailed(f"Error while retrieving data: {err}") from err
coordinator = DataUpdateCoordinator(
coordinator = AirVisualDataUpdateCoordinator(
hass,
LOGGER,
config_entry=entry,
entry,
cloud_api,
name=async_get_geography_id(entry.data),
# We give a placeholder update interval in order to create the coordinator;
# then, below, we use the coordinator's presence (along with any other
# coordinators using the same API key) to calculate an actual, leveled
# update interval:
update_interval=timedelta(minutes=5),
update_method=async_update_data,
)
entry.async_on_unload(entry.add_update_listener(async_reload_entry))

View File

@@ -0,0 +1,72 @@
"""Define an AirVisual data coordinator."""
from __future__ import annotations
from datetime import timedelta
from typing import Any
from pyairvisual.cloud_api import (
CloudAPI,
InvalidKeyError,
KeyExpiredError,
UnauthorizedError,
)
from pyairvisual.errors import AirVisualError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_LATITUDE, CONF_LONGITUDE, CONF_STATE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_CITY, LOGGER
type AirVisualConfigEntry = ConfigEntry[AirVisualDataUpdateCoordinator]
class AirVisualDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching AirVisual data."""
config_entry: AirVisualConfigEntry
def __init__(
self,
hass: HomeAssistant,
entry: AirVisualConfigEntry,
cloud_api: CloudAPI,
name: str,
) -> None:
"""Initialize the coordinator."""
self._cloud_api = cloud_api
super().__init__(
hass,
LOGGER,
config_entry=entry,
name=name,
# We give a placeholder update interval in order to create the coordinator;
# then, in async_setup_entry, we use the coordinator's presence (along with
# any other coordinators using the same API key) to calculate an actual,
# leveled update interval:
update_interval=timedelta(minutes=5),
)
async def _async_update_data(self) -> dict[str, Any]:
"""Get new data from the API."""
if CONF_CITY in self.config_entry.data:
api_coro = self._cloud_api.air_quality.city(
self.config_entry.data[CONF_CITY],
self.config_entry.data[CONF_STATE],
self.config_entry.data[CONF_COUNTRY],
)
else:
api_coro = self._cloud_api.air_quality.nearest_city(
self.config_entry.data[CONF_LATITUDE],
self.config_entry.data[CONF_LONGITUDE],
)
try:
return await api_coro
except (InvalidKeyError, KeyExpiredError, UnauthorizedError) as ex:
raise ConfigEntryAuthFailed from ex
except AirVisualError as err:
raise UpdateFailed(f"Error while retrieving data: {err}") from err

View File

@@ -15,8 +15,8 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from . import AirVisualConfigEntry
from .const import CONF_CITY
from .coordinator import AirVisualConfigEntry
CONF_COORDINATES = "coordinates"
CONF_TITLE = "title"

View File

@@ -2,29 +2,25 @@
from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import callback
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .coordinator import AirVisualDataUpdateCoordinator
class AirVisualEntity(CoordinatorEntity):
class AirVisualEntity(CoordinatorEntity[AirVisualDataUpdateCoordinator]):
"""Define a generic AirVisual entity."""
def __init__(
self,
coordinator: DataUpdateCoordinator,
entry: ConfigEntry,
coordinator: AirVisualDataUpdateCoordinator,
description: EntityDescription,
) -> None:
"""Initialize."""
super().__init__(coordinator)
self._attr_extra_state_attributes = {}
self._entry = entry
self.entity_description = description
async def async_added_to_hass(self) -> None:

View File

@@ -8,7 +8,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
@@ -24,10 +23,9 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import AirVisualConfigEntry
from .const import CONF_CITY
from .coordinator import AirVisualConfigEntry, AirVisualDataUpdateCoordinator
from .entity import AirVisualEntity
ATTR_CITY = "city"
@@ -113,7 +111,7 @@ async def async_setup_entry(
"""Set up AirVisual sensors based on a config entry."""
coordinator = entry.runtime_data
async_add_entities(
AirVisualGeographySensor(coordinator, entry, description, locale)
AirVisualGeographySensor(coordinator, description, locale)
for locale in GEOGRAPHY_SENSOR_LOCALES
for description in GEOGRAPHY_SENSOR_DESCRIPTIONS
)
@@ -124,14 +122,14 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
def __init__(
self,
coordinator: DataUpdateCoordinator,
entry: ConfigEntry,
coordinator: AirVisualDataUpdateCoordinator,
description: SensorEntityDescription,
locale: str,
) -> None:
"""Initialize."""
super().__init__(coordinator, entry, description)
super().__init__(coordinator, description)
entry = coordinator.config_entry
self._attr_extra_state_attributes.update(
{
ATTR_CITY: entry.data.get(CONF_CITY),
@@ -182,16 +180,16 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
#
# We use any coordinates in the config entry and, in the case of a geography by
# name, we fall back to the latitude longitude provided in the coordinator data:
latitude = self._entry.data.get(
latitude = self.coordinator.config_entry.data.get(
CONF_LATITUDE,
self.coordinator.data["location"]["coordinates"][1],
)
longitude = self._entry.data.get(
longitude = self.coordinator.config_entry.data.get(
CONF_LONGITUDE,
self.coordinator.data["location"]["coordinates"][0],
)
if self._entry.options[CONF_SHOW_ON_MAP]:
if self.coordinator.config_entry.options[CONF_SHOW_ON_MAP]:
self._attr_extra_state_attributes[ATTR_LATITUDE] = latitude
self._attr_extra_state_attributes[ATTR_LONGITUDE] = longitude
self._attr_extra_state_attributes.pop("lati", None)

View File

@@ -4,18 +4,9 @@ from __future__ import annotations
import asyncio
from contextlib import suppress
from dataclasses import dataclass
from datetime import timedelta
from typing import Any
from pyairvisual.node import (
InvalidAuthenticationError,
NodeConnectionError,
NodeProError,
NodeSamba,
)
from pyairvisual.node import NodeProError, NodeSamba
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_IP_ADDRESS,
CONF_PASSWORD,
@@ -23,25 +14,16 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.exceptions import ConfigEntryNotReady
from .const import LOGGER
from .coordinator import (
AirVisualProConfigEntry,
AirVisualProCoordinator,
AirVisualProData,
)
PLATFORMS = [Platform.SENSOR]
UPDATE_INTERVAL = timedelta(minutes=1)
type AirVisualProConfigEntry = ConfigEntry[AirVisualProData]
@dataclass
class AirVisualProData:
"""Define a data class."""
coordinator: DataUpdateCoordinator
node: NodeSamba
async def async_setup_entry(
hass: HomeAssistant, entry: AirVisualProConfigEntry
@@ -54,48 +36,15 @@ async def async_setup_entry(
except NodeProError as err:
raise ConfigEntryNotReady from err
reload_task: asyncio.Task | None = None
async def async_get_data() -> dict[str, Any]:
"""Get data from the device."""
try:
data = await node.async_get_latest_measurements()
data["history"] = {}
if data["settings"].get("follow_mode") == "device":
history = await node.async_get_history(include_trends=False)
data["history"] = history.get("measurements", [])[-1]
except InvalidAuthenticationError as err:
raise ConfigEntryAuthFailed("Invalid Samba password") from err
except NodeConnectionError as err:
nonlocal reload_task
if not reload_task:
reload_task = hass.async_create_task(
hass.config_entries.async_reload(entry.entry_id)
)
raise UpdateFailed(f"Connection to Pro unit lost: {err}") from err
except NodeProError as err:
raise UpdateFailed(f"Error while retrieving data: {err}") from err
return data
coordinator = DataUpdateCoordinator(
hass,
LOGGER,
config_entry=entry,
name="Node/Pro data",
update_interval=UPDATE_INTERVAL,
update_method=async_get_data,
)
coordinator = AirVisualProCoordinator(hass, entry, node)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = AirVisualProData(coordinator=coordinator, node=node)
async def async_shutdown(_: Event) -> None:
"""Define an event handler to disconnect from the websocket."""
nonlocal reload_task
if reload_task:
if coordinator.reload_task:
with suppress(asyncio.CancelledError):
reload_task.cancel()
coordinator.reload_task.cancel()
await node.async_disconnect()
entry.async_on_unload(

View File

@@ -0,0 +1,79 @@
"""DataUpdateCoordinator for the AirVisual Pro integration."""
from __future__ import annotations
import asyncio
from dataclasses import dataclass
from datetime import timedelta
from typing import Any
from pyairvisual.node import (
InvalidAuthenticationError,
NodeConnectionError,
NodeProError,
NodeSamba,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import LOGGER
UPDATE_INTERVAL = timedelta(minutes=1)
@dataclass
class AirVisualProData:
"""Define a data class."""
coordinator: AirVisualProCoordinator
node: NodeSamba
type AirVisualProConfigEntry = ConfigEntry[AirVisualProData]
class AirVisualProCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Coordinator for AirVisual Pro data."""
config_entry: AirVisualProConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: AirVisualProConfigEntry,
node: NodeSamba,
) -> None:
"""Initialize."""
super().__init__(
hass,
LOGGER,
config_entry=config_entry,
name="Node/Pro data",
update_interval=UPDATE_INTERVAL,
)
self._node = node
self.reload_task: asyncio.Task[bool] | None = None
async def _async_update_data(self) -> dict[str, Any]:
"""Get data from the device."""
try:
data = await self._node.async_get_latest_measurements()
data["history"] = {}
if data["settings"].get("follow_mode") == "device":
history = await self._node.async_get_history(include_trends=False)
data["history"] = history.get("measurements", [])[-1]
except InvalidAuthenticationError as err:
raise ConfigEntryAuthFailed("Invalid Samba password") from err
except NodeConnectionError as err:
if self.reload_task is None:
self.reload_task = self.hass.async_create_task(
self.hass.config_entries.async_reload(self.config_entry.entry_id)
)
raise UpdateFailed(f"Connection to Pro unit lost: {err}") from err
except NodeProError as err:
raise UpdateFailed(f"Error while retrieving data: {err}") from err
return data

View File

@@ -8,7 +8,7 @@ from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_PASSWORD
from homeassistant.core import HomeAssistant
from . import AirVisualProConfigEntry
from .coordinator import AirVisualProConfigEntry
CONF_MAC_ADDRESS = "mac_address"
CONF_SERIAL_NUMBER = "serial_number"

View File

@@ -4,19 +4,17 @@ from __future__ import annotations
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AirVisualProCoordinator
class AirVisualProEntity(CoordinatorEntity):
class AirVisualProEntity(CoordinatorEntity[AirVisualProCoordinator]):
"""Define a generic AirVisual Pro entity."""
def __init__(
self, coordinator: DataUpdateCoordinator, description: EntityDescription
self, coordinator: AirVisualProCoordinator, description: EntityDescription
) -> None:
"""Initialize."""
super().__init__(coordinator)

View File

@@ -22,7 +22,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AirVisualProConfigEntry
from .coordinator import AirVisualProConfigEntry
from .entity import AirVisualProEntity

View File

@@ -2,10 +2,12 @@
from __future__ import annotations
import aiohttp
from genie_partner_sdk.client import AladdinConnectClient
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import (
aiohttp_client,
config_entry_oauth2_flow,
@@ -31,11 +33,27 @@ async def async_setup_entry(
session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
try:
await session.async_ensure_token_valid()
except aiohttp.ClientResponseError as err:
if 400 <= err.status < 500:
raise ConfigEntryAuthFailed(err) from err
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err
client = AladdinConnectClient(
api.AsyncConfigEntryAuth(aiohttp_client.async_get_clientsession(hass), session)
)
doors = await client.get_doors()
try:
doors = await client.get_doors()
except aiohttp.ClientResponseError as err:
if 400 <= err.status < 500:
raise ConfigEntryAuthFailed(err) from err
raise ConfigEntryNotReady from err
except aiohttp.ClientError as err:
raise ConfigEntryNotReady from err
entry.runtime_data = {
door.unique_id: AladdinConnectCoordinator(hass, entry, client, door)

View File

@@ -11,6 +11,18 @@ API_URL = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1"
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
class AsyncConfigFlowAuth(Auth):
"""Provide Aladdin Connect Genie authentication for config flow validation."""
def __init__(self, websession: ClientSession, access_token: str) -> None:
"""Initialize Aladdin Connect Genie auth."""
super().__init__(websession, API_URL, access_token, API_KEY)
async def async_get_access_token(self) -> str:
"""Return the access token."""
return self.access_token
class AsyncConfigEntryAuth(Auth):
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""

View File

@@ -4,12 +4,14 @@ from collections.abc import Mapping
import logging
from typing import Any
from genie_partner_sdk.client import AladdinConnectClient
import jwt
import voluptuous as vol
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow
from .api import AsyncConfigFlowAuth
from .const import CONFIG_FLOW_MINOR_VERSION, CONFIG_FLOW_VERSION, DOMAIN
@@ -52,11 +54,25 @@ class OAuth2FlowHandler(
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
"""Create an oauth config entry or update existing entry for reauth."""
# Extract the user ID from the JWT token's 'sub' field
token = jwt.decode(
data["token"]["access_token"], options={"verify_signature": False}
try:
token = jwt.decode(
data["token"]["access_token"], options={"verify_signature": False}
)
user_id = token["sub"]
except jwt.DecodeError, KeyError:
return self.async_abort(reason="oauth_error")
client = AladdinConnectClient(
AsyncConfigFlowAuth(
aiohttp_client.async_get_clientsession(self.hass),
data["token"]["access_token"],
)
)
user_id = token["sub"]
try:
await client.get_doors()
except Exception: # noqa: BLE001
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(user_id)
if self.source == SOURCE_REAUTH:

View File

@@ -5,12 +5,13 @@ from __future__ import annotations
from datetime import timedelta
import logging
import aiohttp
from genie_partner_sdk.client import AladdinConnectClient
from genie_partner_sdk.model import GarageDoor
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
_LOGGER = logging.getLogger(__name__)
type AladdinConnectConfigEntry = ConfigEntry[dict[str, AladdinConnectCoordinator]]
@@ -40,7 +41,10 @@ class AladdinConnectCoordinator(DataUpdateCoordinator[GarageDoor]):
async def _async_update_data(self) -> GarageDoor:
"""Fetch data from the Aladdin Connect API."""
await self.client.update_door(self.data.device_id, self.data.door_number)
try:
await self.client.update_door(self.data.device_id, self.data.door_number)
except aiohttp.ClientError as err:
raise UpdateFailed(f"Error communicating with API: {err}") from err
self.data.status = self.client.get_door_status(
self.data.device_id, self.data.door_number
)

View File

@@ -4,14 +4,19 @@ from __future__ import annotations
from typing import Any
import aiohttp
from homeassistant.components.cover import CoverDeviceClass, CoverEntity
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import SUPPORTED_FEATURES
from .const import DOMAIN, SUPPORTED_FEATURES
from .coordinator import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
PARALLEL_UPDATES = 1
async def async_setup_entry(
hass: HomeAssistant,
@@ -40,11 +45,23 @@ class AladdinCoverEntity(AladdinConnectEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None:
"""Issue open command to cover."""
await self.client.open_door(self._device_id, self._number)
try:
await self.client.open_door(self._device_id, self._number)
except aiohttp.ClientError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="open_door_failed",
) from err
async def async_close_cover(self, **kwargs: Any) -> None:
"""Issue close command to cover."""
await self.client.close_door(self._device_id, self._number)
try:
await self.client.close_door(self._device_id, self._number)
except aiohttp.ClientError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="close_door_failed",
) from err
@property
def is_closed(self) -> bool | None:

View File

@@ -0,0 +1,32 @@
"""Diagnostics support for Aladdin Connect."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.core import HomeAssistant
from .coordinator import AladdinConnectConfigEntry
TO_REDACT = {"access_token", "refresh_token"}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return {
"config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT),
"doors": {
uid: {
"device_id": coordinator.data.device_id,
"door_number": coordinator.data.door_number,
"name": coordinator.data.name,
"status": coordinator.data.status,
"link_status": coordinator.data.link_status,
"battery_level": coordinator.data.battery_level,
}
for uid, coordinator in config_entry.runtime_data.items()
},
}

View File

@@ -7,74 +7,56 @@ rules:
brands: done
common-modules: done
config-flow: done
config-flow-test-coverage: todo
config-flow-test-coverage: done
dependency-transparency: done
docs-actions:
status: exempt
comment: Integration does not register any service actions.
docs-high-level-description: done
docs-installation-instructions:
status: todo
comment: Documentation needs to be created.
docs-removal-instructions:
status: todo
comment: Documentation needs to be created.
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: Integration does not subscribe to external events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure:
status: todo
comment: Config flow does not currently test connection during setup.
test-before-setup: todo
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions: todo
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: todo
comment: Documentation needs to be created.
docs-installation-parameters:
status: todo
comment: Documentation needs to be created.
entity-unavailable: todo
status: exempt
comment: Integration does not have an options flow.
docs-installation-parameters: done
entity-unavailable:
status: done
comment: Handled by the coordinator.
integration-owner: done
log-when-unavailable: todo
parallel-updates: todo
log-when-unavailable:
status: done
comment: Handled by the coordinator.
parallel-updates: done
reauthentication-flow: done
test-coverage:
status: todo
comment: Platform tests for cover and sensor need to be implemented to reach 95% coverage.
test-coverage: done
# Gold
devices: done
diagnostics: todo
discovery: todo
discovery-update-info: todo
docs-data-update:
status: todo
comment: Documentation needs to be created.
docs-examples:
status: todo
comment: Documentation needs to be created.
docs-known-limitations:
status: todo
comment: Documentation needs to be created.
docs-supported-devices:
status: todo
comment: Documentation needs to be created.
docs-supported-functions:
status: todo
comment: Documentation needs to be created.
docs-troubleshooting:
status: todo
comment: Documentation needs to be created.
docs-use-cases:
status: todo
comment: Documentation needs to be created.
diagnostics: done
discovery: done
discovery-update-info:
status: exempt
comment: Integration connects via the cloud and not locally.
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: todo
entity-category: done
entity-device-class: done
@@ -84,9 +66,7 @@ rules:
icon-translations: todo
reconfiguration-flow: todo
repair-issues: todo
stale-devices:
status: todo
comment: Stale devices can be done dynamically
stale-devices: done
# Platinum
async-dependency: todo

View File

@@ -20,6 +20,8 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AladdinConnectSensorEntityDescription(SensorEntityDescription):

View File

@@ -4,6 +4,7 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml.",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
@@ -31,5 +32,13 @@
"title": "[%key:common::config_flow::title::reauth%]"
}
}
},
"exceptions": {
"close_door_failed": {
"message": "Failed to close the garage door"
},
"open_door_failed": {
"message": "Failed to open the garage door"
}
}
}

View File

@@ -44,7 +44,7 @@ def make_entity_state_trigger_required_features(
class CustomTrigger(EntityStateTriggerRequiredFeatures):
"""Trigger for entity state changes."""
_domain = domain
_domains = {domain}
_to_states = {to_state}
_required_features = required_features

View File

@@ -13,9 +13,6 @@ from homeassistant.helpers import config_validation as cv, service
from .const import DOMAIN
SERVICE_ALARM_TOGGLE_CHIME = "alarm_toggle_chime"
SERVICE_ALARM_KEYPRESS = "alarm_keypress"
ATTR_KEYPRESS = "keypress"
@@ -26,7 +23,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_ALARM_TOGGLE_CHIME,
"alarm_toggle_chime",
entity_domain=ALARM_CONTROL_PANEL_DOMAIN,
schema={
vol.Required(ATTR_CODE): cv.string,
@@ -37,7 +34,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_ALARM_KEYPRESS,
"alarm_keypress",
entity_domain=ALARM_CONTROL_PANEL_DOMAIN,
schema={
vol.Required(ATTR_KEYPRESS): cv.string,

View File

@@ -13,7 +13,7 @@ from homeassistant.components.notify import (
ATTR_DATA,
ATTR_MESSAGE,
ATTR_TITLE,
DOMAIN as DOMAIN_NOTIFY,
DOMAIN as NOTIFY_DOMAIN,
)
from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON
from homeassistant.core import Event, EventStateChangedData, HassJob, HomeAssistant
@@ -185,7 +185,7 @@ class AlertEntity(Entity):
for target in self._notifiers:
try:
await self.hass.services.async_call(
DOMAIN_NOTIFY, target, msg_payload, context=self._context
NOTIFY_DOMAIN, target, msg_payload, context=self._context
)
except ServiceNotFound:
LOGGER.error(

View File

@@ -1,6 +1,6 @@
"""Defines a base Alexa Devices entity."""
from aioamazondevices.const.devices import SPEAKER_GROUP_MODEL
from aioamazondevices.const.devices import SPEAKER_GROUP_DEVICE_TYPE
from aioamazondevices.structures import AmazonDevice
from homeassistant.helpers.device_registry import DeviceInfo
@@ -25,19 +25,20 @@ class AmazonEntity(CoordinatorEntity[AmazonDevicesCoordinator]):
"""Initialize the entity."""
super().__init__(coordinator)
self._serial_num = serial_num
model_details = coordinator.api.get_model_details(self.device) or {}
model = model_details.get("model")
model = self.device.model
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, serial_num)},
name=self.device.account_name,
model=model,
model_id=self.device.device_type,
manufacturer=model_details.get("manufacturer", "Amazon"),
hw_version=model_details.get("hw_version"),
manufacturer=self.device.manufacturer or "Amazon",
hw_version=self.device.hardware_version,
sw_version=(
self.device.software_version if model != SPEAKER_GROUP_MODEL else None
self.device.software_version
if model != SPEAKER_GROUP_DEVICE_TYPE
else None
),
serial_number=serial_num if model != SPEAKER_GROUP_MODEL else None,
serial_number=serial_num if model != SPEAKER_GROUP_DEVICE_TYPE else None,
)
self.entity_description = description
self._attr_unique_id = f"{serial_num}-{description.key}"

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==12.0.0"]
"requirements": ["aioamazondevices==13.0.0"]
}

View File

@@ -16,9 +16,6 @@ from .coordinator import AmazonConfigEntry
ATTR_TEXT_COMMAND = "text_command"
ATTR_SOUND = "sound"
ATTR_INFO_SKILL = "info_skill"
SERVICE_TEXT_COMMAND = "send_text_command"
SERVICE_SOUND_NOTIFICATION = "send_sound"
SERVICE_INFO_SKILL = "send_info_skill"
SCHEMA_SOUND_SERVICE = vol.Schema(
{
@@ -128,17 +125,17 @@ 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,
"send_sound",
async_send_sound_notification,
SCHEMA_SOUND_SERVICE,
),
(
SERVICE_TEXT_COMMAND,
"send_text_command",
async_send_text_command,
SCHEMA_CUSTOM_COMMAND,
),
(
SERVICE_INFO_SKILL,
"send_info_skill",
async_send_info_skill,
SCHEMA_INFO_SKILL,
),

View File

@@ -16,8 +16,6 @@ ATTRIBUTION = "Data provided by Amber Electric"
LOGGER = logging.getLogger(__package__)
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
SERVICE_GET_FORECASTS = "get_forecasts"
GENERAL_CHANNEL = "general"
CONTROLLED_LOAD_CHANNEL = "controlled_load"
FEED_IN_CHANNEL = "feed_in"

View File

@@ -22,7 +22,6 @@ from .const import (
DOMAIN,
FEED_IN_CHANNEL,
GENERAL_CHANNEL,
SERVICE_GET_FORECASTS,
)
from .coordinator import AmberConfigEntry
from .helpers import format_cents_to_dollars, normalize_descriptor
@@ -101,7 +100,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
hass.services.async_register(
DOMAIN,
SERVICE_GET_FORECASTS,
"get_forecasts",
handle_get_forecasts,
GET_FORECASTS_SCHEMA,
supports_response=SupportsResponse.ONLY,

View File

@@ -49,18 +49,6 @@ SCAN_INTERVAL = timedelta(seconds=15)
STREAM_SOURCE_LIST = ["snapshot", "mjpeg", "rtsp"]
_SRV_EN_REC = "enable_recording"
_SRV_DS_REC = "disable_recording"
_SRV_EN_AUD = "enable_audio"
_SRV_DS_AUD = "disable_audio"
_SRV_EN_MOT_REC = "enable_motion_recording"
_SRV_DS_MOT_REC = "disable_motion_recording"
_SRV_GOTO = "goto_preset"
_SRV_CBW = "set_color_bw"
_SRV_TOUR_ON = "start_tour"
_SRV_TOUR_OFF = "stop_tour"
_SRV_PTZ_CTRL = "ptz_control"
_ATTR_PTZ_TT = "travel_time"
_ATTR_PTZ_MOV = "movement"
_MOV = [
@@ -103,17 +91,17 @@ _SRV_PTZ_SCHEMA = _SRV_SCHEMA.extend(
)
CAMERA_SERVICES = {
_SRV_EN_REC: (_SRV_SCHEMA, "async_enable_recording", ()),
_SRV_DS_REC: (_SRV_SCHEMA, "async_disable_recording", ()),
_SRV_EN_AUD: (_SRV_SCHEMA, "async_enable_audio", ()),
_SRV_DS_AUD: (_SRV_SCHEMA, "async_disable_audio", ()),
_SRV_EN_MOT_REC: (_SRV_SCHEMA, "async_enable_motion_recording", ()),
_SRV_DS_MOT_REC: (_SRV_SCHEMA, "async_disable_motion_recording", ()),
_SRV_GOTO: (_SRV_GOTO_SCHEMA, "async_goto_preset", (_ATTR_PRESET,)),
_SRV_CBW: (_SRV_CBW_SCHEMA, "async_set_color_bw", (_ATTR_COLOR_BW,)),
_SRV_TOUR_ON: (_SRV_SCHEMA, "async_start_tour", ()),
_SRV_TOUR_OFF: (_SRV_SCHEMA, "async_stop_tour", ()),
_SRV_PTZ_CTRL: (
"enable_recording": (_SRV_SCHEMA, "async_enable_recording", ()),
"disable_recording": (_SRV_SCHEMA, "async_disable_recording", ()),
"enable_audio": (_SRV_SCHEMA, "async_enable_audio", ()),
"disable_audio": (_SRV_SCHEMA, "async_disable_audio", ()),
"enable_motion_recording": (_SRV_SCHEMA, "async_enable_motion_recording", ()),
"disable_motion_recording": (_SRV_SCHEMA, "async_disable_motion_recording", ()),
"goto_preset": (_SRV_GOTO_SCHEMA, "async_goto_preset", (_ATTR_PRESET,)),
"set_color_bw": (_SRV_CBW_SCHEMA, "async_set_color_bw", (_ATTR_COLOR_BW,)),
"start_tour": (_SRV_SCHEMA, "async_start_tour", ()),
"stop_tour": (_SRV_SCHEMA, "async_stop_tour", ()),
"ptz_control": (
_SRV_PTZ_SCHEMA,
"async_ptz_control",
(_ATTR_PTZ_MOV, _ATTR_PTZ_TT),

View File

@@ -75,7 +75,7 @@
"name": "Go to preset"
},
"ptz_control": {
"description": "Moves (pan/tilt) and/or zoom a PTZ camera.",
"description": "Moves (pan/tilt) and/or zooms a PTZ camera.",
"fields": {
"entity_id": {
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",

View File

@@ -534,6 +534,10 @@ class Analytics:
payload = await _async_snapshot_payload(self._hass)
if not payload:
LOGGER.info("Skipping snapshot submission, no data to send")
return
headers = {
"Content-Type": "application/json",
"User-Agent": f"home-assistant/{HA_VERSION}",

View File

@@ -38,7 +38,6 @@ def get_app_entity_description(
translation_key="apps",
name=name_slug,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.apps.get(name_slug),
)
@@ -52,7 +51,6 @@ def get_core_integration_entity_description(
translation_key="core_integrations",
name=name,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.core_integrations.get(domain),
)
@@ -66,7 +64,6 @@ def get_custom_integration_entity_description(
translation_key="custom_integrations",
translation_placeholders={"custom_integration_domain": domain},
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.custom_integrations.get(domain),
)
@@ -77,7 +74,6 @@ GENERAL_SENSORS = [
translation_key="total_active_installations",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.active_installations,
),
AnalyticsSensorEntityDescription(
@@ -85,7 +81,6 @@ GENERAL_SENSORS = [
translation_key="total_reports_integrations",
entity_registry_enabled_default=False,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement="active installations",
value_fn=lambda data: data.reports_integrations,
),
]

View File

@@ -24,14 +24,23 @@
},
"entity": {
"sensor": {
"apps": {
"unit_of_measurement": "active installations"
},
"core_integrations": {
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
},
"custom_integrations": {
"name": "{custom_integration_domain} (custom)"
"name": "{custom_integration_domain} (custom)",
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
},
"total_active_installations": {
"name": "Total active installations"
"name": "Total active installations",
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
},
"total_reports_integrations": {
"name": "Total reported integrations"
"name": "Total reported integrations",
"unit_of_measurement": "[%key:component::analytics_insights::entity::sensor::apps::unit_of_measurement%]"
}
}
},

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