Compare commits

...

802 Commits

Author SHA1 Message Date
Paulus Schoutsen
2f37a58798 Add config.yaml 2026-03-01 23:00:44 -05: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
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
2794 changed files with 151900 additions and 20521 deletions

1
.agent/skills Symbolic link
View File

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

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

View File

@@ -272,7 +272,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 +294,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
@@ -321,8 +336,9 @@ jobs:
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 \

View File

@@ -37,10 +37,10 @@ on:
type: boolean
env:
CACHE_VERSION: 2
CACHE_VERSION: 3
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.3"
HA_SHORT_VERSION: "2026.4"
DEFAULT_PYTHON: "3.14.2"
ALL_PYTHON_VERSIONS: "['3.14.2']"
# 10.3 is the oldest supported version
@@ -605,7 +605,7 @@ jobs:
with:
persist-credentials: false
- name: Dependency review
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3
with:
license-check: false # We use our own license audit checks

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

@@ -110,7 +110,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp313", "cp314"]
abi: ["cp314"]
arch: ["amd64", "aarch64"]
include:
- arch: amd64
@@ -161,7 +161,7 @@ jobs:
strategy:
fail-fast: false
matrix:
abi: ["cp313", "cp314"]
abi: ["cp314"]
arch: ["amd64", "aarch64"]
include:
- arch: amd64

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.*
@@ -130,6 +131,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,6 +211,7 @@ 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.fritz.*
homeassistant.components.fritzbox.*
@@ -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.*
@@ -366,6 +373,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 +409,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 +426,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 +445,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 +482,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.*
@@ -521,6 +534,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.*
@@ -531,6 +545,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.*
@@ -563,12 +578,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.*
@@ -581,6 +598,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.*
@@ -597,6 +615,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.*

49
CODEOWNERS generated
View File

@@ -242,6 +242,8 @@ build.json @home-assistant/supervisor
/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
@@ -399,12 +401,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
@@ -555,8 +555,6 @@ build.json @home-assistant/supervisor
/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
@@ -719,8 +717,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
@@ -753,6 +751,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 +786,14 @@ build.json @home-assistant/supervisor
/tests/components/improv_ble/ @emontnemery
/homeassistant/components/incomfort/ @jbouwh
/tests/components/incomfort/ @jbouwh
/homeassistant/components/indevolt/ @xirtnl
/tests/components/indevolt/ @xirtnl
/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 +1082,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 +1100,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
@@ -1279,6 +1285,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
@@ -1642,6 +1650,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 @slangstrom
/tests/components/systemnexa2/ @konsulten @slangstrom
/homeassistant/components/tado/ @erwindouna
/tests/components/tado/ @erwindouna
/homeassistant/components/tag/ @home-assistant/core
@@ -1667,6 +1677,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
@@ -1733,6 +1745,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 +1882,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 +1899,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 +1961,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

126
CREATE_CONFIG_YAML.md Normal file
View File

@@ -0,0 +1,126 @@
# Create `config.yaml` For Config Flows
## Goal
Document the persisted config entry and subentry payloads in each integration's `config.yaml` under `config_entry`, using selector-based field metadata that is consistent with Home Assistant selectors.
The output must describe what is **actually stored** in config entries (`data`, `options`, and `subentries`), not just what is shown in forms.
## Required Files Per Integration
For each integration with `"config_flow": true` in `manifest.json`, inspect:
1. `config_flow.py`
2. `__init__.py` (for migration and runtime usage confirmation)
3. `const.py` (for `CONF_*`, version constants, and aliases)
4. `strings.json` / translations only as fallback for field names not inferable from code
5. Existing `config.yaml` (target file)
## Version Rules
1. Default version is `major: 1`, `minor: 1` when no explicit version is defined.
2. Read `VERSION` and `MINOR_VERSION` from the config flow class.
3. If the class uses constants (for example `CONFIG_FLOW_VERSION`), resolve them from `const.py`.
4. Document all known config-entry versions when code clearly supports multiple versions:
- Current version from config flow class.
- Historical versions from explicit migration branches (for example `async_migrate_entry` checks in `__init__.py`).
5. Apply the same version logic to subentries (default `1.1` when unspecified).
## Storage Target Rules (Critical)
Always determine where values are persisted:
1. `ConfigFlow.async_create_entry(data=...)` -> persisted in config entry `data`.
2. `ConfigFlow.async_create_entry(..., options=...)` -> persisted in config entry `options`.
3. `OptionsFlow.async_create_entry(data=...)` -> persisted in config entry `options`.
4. `SchemaConfigFlowHandler` (default implementation):
- Config flow values are stored in `options`.
- Config entry `data` is empty.
- Exception: class overrides `async_create_entry` (then follow override).
5. `async_update_reload_and_abort(..., data=..., options=...)` updates existing entry payloads and must align with documented fields.
## Form-To-Storage Mapping Rules
When `user_input` is stored directly, form schema must be mirrored in `config.yaml`.
### Config Flow
If step logic returns `async_create_entry(data=user_input)`:
1. Find the matching `async_show_form(..., data_schema=...)` for that step.
2. Extract all schema keys.
3. Add those keys to `config_entry.versions[*].data.fields`.
### Options Flow
If options step returns `async_create_entry(data=user_input)`:
1. Extract step schema keys.
2. Add those keys to `config_entry.versions[*].options.fields`.
### Dict Payloads
If `async_create_entry(data={...})` (or via a local dict variable/function that clearly returns a dict):
1. Extract literal keys.
2. Add keys to the relevant persisted section (`data` or `options`).
## Helper Flow Rules
### `register_discovery_flow(...)`
Creates entry with `data={}` by default. Keep data empty unless integration overrides flow behavior elsewhere.
### `register_webhook_flow(...)`
Creates entry with:
1. `webhook_id`
2. `cloudhook`
These must be documented in `config_entry.versions[*].data.fields`.
### `AbstractOAuth2FlowHandler`
Default OAuth payload includes:
1. `auth_implementation`
2. `token`
If integration overrides `async_oauth_create_entry` and adds additional stored keys, include those too.
## Subentry Rules
1. Find `async_get_supported_subentry_types(...)` mapping and subentry flow classes (`ConfigSubentryFlow`).
2. For each `subentry_type`, document under:
- `config_entry.subentries.<subentry_type>.versions`
3. Extract persisted subentry payload keys from:
- `async_create_entry(data=...)` in subentry flow
- direct subentry update calls with explicit data payloads
4. Apply required/default/selector extraction exactly as for main config/option flows.
## Field Metadata Rules
Each field entry should include:
1. `required` (true/false)
2. `selector` (valid HA selector structure)
3. Optional `default` and `example` when directly known from code
### Required Flag
1. `vol.Required(...)` -> `required: true`
2. `vol.Optional(...)` -> `required: false`
3. Literal dict payloads without schema context -> `required: true` unless clearly optional in code path
### Selector Mapping
Use explicit selector calls when present (for example `TextSelector`, `NumberSelector`, `BooleanSelector`, `LocationSelector`, `SelectSelector`, etc).
If schema uses plain validators:
1. `bool` / `cv.boolean` -> `selector: { boolean: {} }`
2. numeric validators -> `selector: { number: {} }`
3. `vol.In(...)` / constrained choices -> `selector: { select: {} }`
4. unknown / string-like -> `selector: { text: {} }`
5. structured blobs (for example OAuth `token`) -> `selector: { object: {} }`
## Validation Checklist (Per Integration)
1. `config.yaml` exists when `manifest.json` has `config_flow: true`.
2. `config_entry.versions` contains correct version entries.
3. Documented fields exactly match persisted payloads (`data` vs `options`).
4. `required` and selector format are valid.
5. `subentries` are documented when supported.
6. No placeholder empty blocks where code stores actual fields.
## Final QA Commands
Run after updates:
```bash
python -m script.hassfest -p config_entry --action validate
ruff check script/hassfest/config_entry.py
```
## High-Risk Pitfalls
1. Assuming fields in forms are always stored in `data` (wrong for `SchemaConfigFlowHandler`).
2. Missing fields when `data=user_input` is used with a non-empty schema.
3. Skipping helper flows (`register_webhook_flow`, OAuth2 base handler behavior).
4. Ignoring options/subentry flows that store separate payloads.
5. Using placeholders instead of integration-specific field definitions.

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

@@ -210,6 +210,7 @@ DEFAULT_INTEGRATIONS = {
"analytics", # Needed for onboarding
"application_credentials",
"backup",
"brands",
"frontend",
"hardware",
"labs",

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,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
polling:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
is_new_style_scale:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,26 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
latitude:
required: false
selector:
text: {}
longitude:
required: false
selector:
text: {}
name:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
id:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
api_token:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
connection_type:
required: true
selector:
select:
options:
- Cloud
- Local
default: Cloud
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,34 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
port:
required: false
selector:
text: {}
ssl:
required: false
selector:
text: {}
username:
required: false
selector:
text: {}
verify_ssl:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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,18 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
ip_address:
required: false
selector:
text: {}
port:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,34 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
latitude:
required: false
selector:
text: {}
longitude:
required: false
selector:
text: {}
name:
required: false
selector:
text: {}
options:
fields:
radar_updates:
required: false
selector:
text: {}
station_updates:
required: false
selector:
text: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
server_url:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,26 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
latitude:
required: false
selector:
text: {}
longitude:
required: false
selector:
text: {}
name:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,30 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
latitude:
required: false
selector:
text: {}
longitude:
required: false
selector:
text: {}
radius:
required: false
selector:
text: {}
options:
fields:
radius:
required: false
selector:
text: {}
subentries: {}

View File

@@ -0,0 +1,22 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
username:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,19 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
advanced_settings:
required: true
selector:
text: {}
mac_address:
required: true
selector:
select:
options: []
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
email:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,26 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
ip_address:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
options:
fields:
clip_negatives:
required: false
selector:
text: {}
return_average:
required: false
selector:
text: {}
subentries: {}

View File

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
secret:
required: true
selector:
text: {}
id:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
device_model:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 3
minor: 1
data:
fields:
integration_type:
required: true
selector:
text: {}
options:
fields:
show_on_map:
required: false
selector:
text: {}
subentries: {}

View File

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
ip_address:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,22 @@
config_entry:
versions:
- version:
major: 1
minor: 2
data:
fields:
host:
required: false
selector:
text: {}
id:
required: false
selector:
text: {}
port:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,22 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
id:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
username:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
auth_implementation:
required: true
selector:
text: {}
token:
required: true
selector:
object: {}
options:
fields: {}
subentries: {}

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
@@ -86,7 +68,7 @@ rules:
repair-issues: todo
stale-devices:
status: todo
comment: Stale devices can be done dynamically
comment: We can automatically remove removed devices
# 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

@@ -0,0 +1,29 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
device_baudrate:
required: true
selector:
number:
mode: box
default: 115200
device_path:
required: true
selector:
text: {}
default: /dev/ttyUSB0
options:
fields:
arm_options:
required: false
selector:
text: {}
zone_options:
required: false
selector:
text: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 3
data:
fields:
login_data:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,14 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,16 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
site_id:
required: true
selector:
select:
mode: dropdown
options: []
options:
fields: {}
subentries: {}

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

@@ -0,0 +1,17 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
station:
required: true
selector:
select:
multiple: false
sort: true
options: []
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,18 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
api_key:
required: false
selector:
text: {}
app_key:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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