Compare commits

...

188 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
1351 changed files with 69657 additions and 10406 deletions

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/**

View File

@@ -40,7 +40,7 @@ env:
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

@@ -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

@@ -289,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.*
@@ -544,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.*

14
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,8 +401,6 @@ 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
@@ -717,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
@@ -792,6 +792,8 @@ build.json @home-assistant/supervisor
/tests/components/inels/ @epdevlab
/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
@@ -1897,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

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

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

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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -89,11 +89,10 @@ async def async_setup_entry(
"""Set up the AirOS binary sensors from a config entry."""
coordinator = config_entry.runtime_data
entities: list[BinarySensorEntity] = []
entities.extend(
entities = [
AirOSBinarySensor(coordinator, description)
for description in COMMON_BINARY_SENSORS
)
]
if coordinator.device_data["fw_major"] == 8:
entities.extend(

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

@@ -182,15 +182,15 @@ 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 COMMON_SENSORS
)
entities = [AirOSSensor(coordinator, description) for description in COMMON_SENSORS]
if coordinator.device_data["fw_major"] == 8:
async_add_entities(
entities.extend(
AirOSSensor(coordinator, description) for description in AIROS8_SENSORS
)
async_add_entities(entities)
class AirOSSensor(AirOSEntity, SensorEntity):
"""Representation of a Sensor."""

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

@@ -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

@@ -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

@@ -26,24 +26,26 @@ rules:
unique-config-entry: done
# Silver
action-exceptions: todo
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
comment: Integration does not have an options flow.
docs-installation-parameters: done
entity-unavailable: todo
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
diagnostics: done
discovery: done
discovery-update-info:
status: exempt

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

@@ -32,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

@@ -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: {}

View File

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

View File

@@ -0,0 +1,34 @@
config_entry:
versions:
- version:
major: 2
minor: 1
data:
fields:
tracked_apps:
required: false
selector:
text: {}
tracked_custom_integrations:
required: false
selector:
text: {}
tracked_integrations:
required: false
selector:
text: {}
options:
fields:
tracked_apps:
required: false
selector:
text: {}
tracked_custom_integrations:
required: false
selector:
text: {}
tracked_integrations:
required: false
selector:
text: {}
subentries: {}

View File

@@ -0,0 +1,26 @@
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: {}
username:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -0,0 +1,77 @@
config_entry:
versions:
- version:
major: 1
minor: 2
data:
fields:
adb_server_ip:
required: false
selector:
text: {}
adb_server_port:
required: false
selector:
text: {}
adbkey:
required: false
selector:
text: {}
device_class:
required: false
selector:
text: {}
host:
required: false
selector:
text: {}
port:
required: false
selector:
text: {}
options:
fields:
app_delete:
required: false
selector:
boolean: {}
default: false
apps:
required: false
selector:
select:
mode: dropdown
options: []
exclude_unnamed_apps:
required: false
selector:
boolean: {}
get_sources:
required: false
selector:
boolean: {}
rule_delete:
required: false
selector:
boolean: {}
default: false
screencap_interval:
required: true
selector:
number:
mode: box
state_detection_rules:
required: false
selector:
select:
mode: dropdown
options: []
turn_off_command:
required: false
selector:
text: {}
turn_on_command:
required: false
selector:
text: {}
subentries: {}

View File

@@ -36,7 +36,7 @@ from .const import (
SIGNAL_CONFIG_ENTITY,
)
from .entity import AndroidTVEntity, adb_decorator
from .services import ATTR_ADB_RESPONSE, ATTR_HDMI_INPUT, SERVICE_LEARN_SENDEVENT
from .services import ATTR_ADB_RESPONSE, ATTR_HDMI_INPUT
_LOGGER = logging.getLogger(__name__)
@@ -271,7 +271,7 @@ class ADBDevice(AndroidTVEntity, MediaPlayerEntity):
self.async_write_ha_state()
msg = (
f"Output from service '{SERVICE_LEARN_SENDEVENT}' from"
f"Output from service 'learn_sendevent' from"
f" {self.entity_id}: '{output}'"
)
persistent_notification.async_create(

View File

@@ -16,11 +16,6 @@ ATTR_DEVICE_PATH = "device_path"
ATTR_HDMI_INPUT = "hdmi_input"
ATTR_LOCAL_PATH = "local_path"
SERVICE_ADB_COMMAND = "adb_command"
SERVICE_DOWNLOAD = "download"
SERVICE_LEARN_SENDEVENT = "learn_sendevent"
SERVICE_UPLOAD = "upload"
@callback
def async_setup_services(hass: HomeAssistant) -> None:
@@ -29,7 +24,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_ADB_COMMAND,
"adb_command",
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={vol.Required(ATTR_COMMAND): cv.string},
func="adb_command",
@@ -37,7 +32,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_LEARN_SENDEVENT,
"learn_sendevent",
entity_domain=MEDIA_PLAYER_DOMAIN,
schema=None,
func="learn_sendevent",
@@ -45,7 +40,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_DOWNLOAD,
"download",
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={
vol.Required(ATTR_DEVICE_PATH): cv.string,
@@ -56,7 +51,7 @@ def async_setup_services(hass: HomeAssistant) -> None:
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_UPLOAD,
"upload",
entity_domain=MEDIA_PLAYER_DOMAIN,
schema={
vol.Required(ATTR_DEVICE_PATH): cv.string,

View File

@@ -0,0 +1,38 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
pin:
required: true
selector:
text: {}
options:
fields:
app_delete:
required: false
selector:
text: {}
app_icon:
required: false
selector:
text: {}
app_id:
required: false
selector:
text: {}
app_name:
required: false
selector:
text: {}
apps:
required: false
selector:
text: {}
enable_ime:
required: false
selector:
text: {}
subentries: {}

View File

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

View File

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

View File

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

View File

@@ -46,6 +46,7 @@ class AnthropicTaskEntity(
ai_task.AITaskEntityFeature.GENERATE_DATA
| ai_task.AITaskEntityFeature.SUPPORT_ATTACHMENTS
)
_attr_translation_key = "ai_task_data"
async def _async_generate_data(
self,

View File

@@ -0,0 +1,68 @@
config_entry:
versions:
- version:
major: 2
minor: 3
data:
fields:
api_key:
required: false
selector:
text: {}
options:
fields: {}
subentries:
ai_task_data:
versions:
- version:
major: 1
minor: 1
data:
fields:
chat_model:
required: false
selector:
select:
custom_value: true
options: []
max_tokens:
required: false
selector:
number:
mode: box
temperature:
required: false
selector:
number:
min: 0
max: 1
step: 0.05
options:
fields: {}
conversation:
versions:
- version:
major: 1
minor: 1
data:
fields:
chat_model:
required: false
selector:
select:
custom_value: true
options: []
max_tokens:
required: false
selector:
number:
mode: box
temperature:
required: false
selector:
number:
min: 0
max: 1
step: 0.05
options:
fields: {}

View File

@@ -43,7 +43,9 @@ from homeassistant.helpers.selector import (
from homeassistant.helpers.typing import VolDictType
from .const import (
CODE_EXECUTION_UNSUPPORTED_MODELS,
CONF_CHAT_MODEL,
CONF_CODE_EXECUTION,
CONF_MAX_TOKENS,
CONF_PROMPT,
CONF_RECOMMENDED,
@@ -415,6 +417,16 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
else:
self.options.pop(CONF_THINKING_EFFORT, None)
if not model.startswith(tuple(CODE_EXECUTION_UNSUPPORTED_MODELS)):
step_schema[
vol.Optional(
CONF_CODE_EXECUTION,
default=DEFAULT[CONF_CODE_EXECUTION],
)
] = bool
else:
self.options.pop(CONF_CODE_EXECUTION, None)
if not model.startswith(tuple(WEB_SEARCH_UNSUPPORTED_MODELS)):
step_schema.update(
{

View File

@@ -11,6 +11,7 @@ DEFAULT_AI_TASK_NAME = "Claude AI Task"
CONF_RECOMMENDED = "recommended"
CONF_PROMPT = "prompt"
CONF_CHAT_MODEL = "chat_model"
CONF_CODE_EXECUTION = "code_execution"
CONF_MAX_TOKENS = "max_tokens"
CONF_TEMPERATURE = "temperature"
CONF_THINKING_BUDGET = "thinking_budget"
@@ -25,6 +26,7 @@ CONF_WEB_SEARCH_TIMEZONE = "timezone"
DEFAULT = {
CONF_CHAT_MODEL: "claude-haiku-4-5",
CONF_CODE_EXECUTION: False,
CONF_MAX_TOKENS: 3000,
CONF_TEMPERATURE: 1.0,
CONF_THINKING_BUDGET: 0,
@@ -65,6 +67,10 @@ WEB_SEARCH_UNSUPPORTED_MODELS = [
"claude-3-haiku",
]
CODE_EXECUTION_UNSUPPORTED_MODELS = [
"claude-3-haiku",
]
DEPRECATED_MODELS = [
"claude-3",
]

View File

@@ -37,6 +37,7 @@ class AnthropicConversationEntity(
"""Anthropic conversation agent."""
_attr_supports_streaming = True
_attr_translation_key = "conversation"
def __init__(self, entry: AnthropicConfigEntry, subentry: ConfigSubentry) -> None:
"""Initialize the agent."""

View File

@@ -3,19 +3,23 @@
import base64
from collections.abc import AsyncGenerator, Callable, Iterable
from dataclasses import dataclass, field
from datetime import UTC, datetime
import json
from mimetypes import guess_file_type
from pathlib import Path
from typing import Any
from typing import Any, Literal, cast
import anthropic
from anthropic import AsyncStream
from anthropic.types import (
Base64ImageSourceParam,
Base64PDFSourceParam,
BashCodeExecutionToolResultBlock,
CitationsDelta,
CitationsWebSearchResultLocation,
CitationWebSearchResultLocationParam,
CodeExecutionTool20250825Param,
Container,
ContentBlockParam,
DocumentBlockParam,
ImageBlockParam,
@@ -41,6 +45,7 @@ from anthropic.types import (
TextCitation,
TextCitationParam,
TextDelta,
TextEditorCodeExecutionToolResultBlock,
ThinkingBlock,
ThinkingBlockParam,
ThinkingConfigAdaptiveParam,
@@ -51,18 +56,21 @@ from anthropic.types import (
ToolChoiceAutoParam,
ToolChoiceToolParam,
ToolParam,
ToolResultBlockParam,
ToolUnionParam,
ToolUseBlock,
ToolUseBlockParam,
Usage,
WebSearchTool20250305Param,
WebSearchToolRequestErrorParam,
WebSearchToolResultBlock,
WebSearchToolResultBlockParam,
WebSearchToolResultError,
WebSearchToolResultBlockParamContentParam,
)
from anthropic.types.bash_code_execution_tool_result_block_param import (
Content as BashCodeExecutionToolResultContentParam,
)
from anthropic.types.message_create_params import MessageCreateParamsStreaming
from anthropic.types.text_editor_code_execution_tool_result_block_param import (
Content as TextEditorCodeExecutionToolResultContentParam,
)
import voluptuous as vol
from voluptuous_openapi import convert
@@ -74,10 +82,12 @@ from homeassistant.helpers import device_registry as dr, llm
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.json import json_dumps
from homeassistant.util import slugify
from homeassistant.util.json import JsonObjectType
from . import AnthropicConfigEntry
from .const import (
CONF_CHAT_MODEL,
CONF_CODE_EXECUTION,
CONF_MAX_TOKENS,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
@@ -134,6 +144,7 @@ class ContentDetails:
citation_details: list[CitationDetails] = field(default_factory=list)
thinking_signature: str | None = None
redacted_thinking: str | None = None
container: Container | None = None
def has_content(self) -> bool:
"""Check if there is any text content."""
@@ -144,6 +155,7 @@ class ContentDetails:
return (
self.thinking_signature is not None
or self.redacted_thinking is not None
or self.container is not None
or self.has_citations()
)
@@ -188,30 +200,53 @@ class ContentDetails:
def _convert_content(
chat_content: Iterable[conversation.Content],
) -> list[MessageParam]:
) -> tuple[list[MessageParam], str | None]:
"""Transform HA chat_log content into Anthropic API format."""
messages: list[MessageParam] = []
container_id: str | None = None
for content in chat_content:
if isinstance(content, conversation.ToolResultContent):
external_tool = True
if content.tool_name == "web_search":
tool_result_block: ContentBlockParam = WebSearchToolResultBlockParam(
type="web_search_tool_result",
tool_use_id=content.tool_call_id,
content=content.tool_result["content"]
if "content" in content.tool_result
else WebSearchToolRequestErrorParam(
type="web_search_tool_result_error",
error_code=content.tool_result.get("error_code", "unavailable"), # type: ignore[typeddict-item]
tool_result_block: ContentBlockParam = {
"type": "web_search_tool_result",
"tool_use_id": content.tool_call_id,
"content": cast(
WebSearchToolResultBlockParamContentParam,
content.tool_result["content"]
if "content" in content.tool_result
else {
"type": "web_search_tool_result_error",
"error_code": content.tool_result.get(
"error_code", "unavailable"
),
},
),
)
external_tool = True
}
elif content.tool_name == "bash_code_execution":
tool_result_block = {
"type": "bash_code_execution_tool_result",
"tool_use_id": content.tool_call_id,
"content": cast(
BashCodeExecutionToolResultContentParam, content.tool_result
),
}
elif content.tool_name == "text_editor_code_execution":
tool_result_block = {
"type": "text_editor_code_execution_tool_result",
"tool_use_id": content.tool_call_id,
"content": cast(
TextEditorCodeExecutionToolResultContentParam,
content.tool_result,
),
}
else:
tool_result_block = ToolResultBlockParam(
type="tool_result",
tool_use_id=content.tool_call_id,
content=json_dumps(content.tool_result),
)
tool_result_block = {
"type": "tool_result",
"tool_use_id": content.tool_call_id,
"content": json_dumps(content.tool_result),
}
external_tool = False
if not messages or messages[-1]["role"] != (
"assistant" if external_tool else "user"
@@ -277,6 +312,11 @@ def _convert_content(
data=content.native.redacted_thinking,
)
)
if (
content.native.container is not None
and content.native.container.expires_at > datetime.now(UTC)
):
container_id = content.native.container.id
if content.content:
current_index = 0
@@ -325,10 +365,23 @@ def _convert_content(
ServerToolUseBlockParam(
type="server_tool_use",
id=tool_call.id,
name="web_search",
name=cast(
Literal[
"web_search",
"bash_code_execution",
"text_editor_code_execution",
],
tool_call.tool_name,
),
input=tool_call.tool_args,
)
if tool_call.external and tool_call.tool_name == "web_search"
if tool_call.external
and tool_call.tool_name
in [
"web_search",
"bash_code_execution",
"text_editor_code_execution",
]
else ToolUseBlockParam(
type="tool_use",
id=tool_call.id,
@@ -347,10 +400,10 @@ def _convert_content(
# If there is only one text block, simplify the content to a string
messages[-1]["content"] = messages[-1]["content"][0]["text"]
else:
# Note: We don't pass SystemContent here as its passed to the API as the prompt
raise TypeError(f"Unexpected content type: {type(content)}")
# Note: We don't pass SystemContent here as it's passed to the API as the prompt
raise HomeAssistantError("Unexpected content type in chat log")
return messages
return messages, container_id
async def _transform_stream( # noqa: C901 - This is complex, but better to have it in one place
@@ -389,8 +442,8 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
Each message could contain multiple blocks of the same type.
"""
if stream is None:
raise TypeError("Expected a stream of messages")
if stream is None or not hasattr(stream, "__aiter__"):
raise HomeAssistantError("Expected a stream of messages")
current_tool_block: ToolUseBlockParam | ServerToolUseBlockParam | None = None
current_tool_args: str
@@ -403,8 +456,6 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
LOGGER.debug("Received response: %s", response)
if isinstance(response, RawMessageStartEvent):
if response.message.role != "assistant":
raise ValueError("Unexpected message role")
input_usage = response.message.usage
first_block = True
elif isinstance(response, RawContentBlockStartEvent):
@@ -478,7 +529,14 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
input={},
)
current_tool_args = ""
elif isinstance(response.content_block, WebSearchToolResultBlock):
elif isinstance(
response.content_block,
(
WebSearchToolResultBlock,
BashCodeExecutionToolResultBlock,
TextEditorCodeExecutionToolResultBlock,
),
):
if content_details:
content_details.delete_empty()
yield {"native": content_details}
@@ -487,26 +545,16 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
yield {
"role": "tool_result",
"tool_call_id": response.content_block.tool_use_id,
"tool_name": "web_search",
"tool_name": response.content_block.type.removesuffix(
"_tool_result"
),
"tool_result": {
"type": "web_search_tool_result_error",
"error_code": response.content_block.content.error_code,
"content": cast(
JsonObjectType, response.content_block.to_dict()["content"]
)
}
if isinstance(
response.content_block.content, WebSearchToolResultError
)
else {
"content": [
{
"type": "web_search_result",
"encrypted_content": block.encrypted_content,
"page_age": block.page_age,
"title": block.title,
"url": block.url,
}
for block in response.content_block.content
]
},
if isinstance(response.content_block.content, list)
else cast(JsonObjectType, response.content_block.content.to_dict()),
}
first_block = True
elif isinstance(response, RawContentBlockDeltaEvent):
@@ -555,6 +603,7 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
elif isinstance(response, RawMessageDeltaEvent):
if (usage := response.usage) is not None:
chat_log.async_trace(_create_token_stats(input_usage, usage))
content_details.container = response.delta.container
if response.delta.stop_reason == "refusal":
raise HomeAssistantError("Potential policy violation detected")
elif isinstance(response, RawMessageStopEvent):
@@ -615,7 +664,7 @@ class AnthropicBaseLLMEntity(Entity):
system = chat_log.content[0]
if not isinstance(system, conversation.SystemContent):
raise TypeError("First message must be a system message")
raise HomeAssistantError("First message must be a system message")
# System prompt with caching enabled
system_prompt: list[TextBlockParam] = [
@@ -626,7 +675,7 @@ class AnthropicBaseLLMEntity(Entity):
)
]
messages = _convert_content(chat_log.content[1:])
messages, container_id = _convert_content(chat_log.content[1:])
model = options.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL])
@@ -636,6 +685,7 @@ class AnthropicBaseLLMEntity(Entity):
max_tokens=options.get(CONF_MAX_TOKENS, DEFAULT[CONF_MAX_TOKENS]),
system=system_prompt,
stream=True,
container=container_id,
)
if not model.startswith(tuple(NON_ADAPTIVE_THINKING_MODELS)):
@@ -674,6 +724,14 @@ class AnthropicBaseLLMEntity(Entity):
for tool in chat_log.llm_api.tools
]
if options.get(CONF_CODE_EXECUTION):
tools.append(
CodeExecutionTool20250825Param(
name="code_execution",
type="code_execution_20250825",
),
)
if options.get(CONF_WEB_SEARCH):
web_search = WebSearchTool20250305Param(
name="web_search",
@@ -784,21 +842,25 @@ class AnthropicBaseLLMEntity(Entity):
try:
stream = await client.messages.create(**model_args)
messages.extend(
_convert_content(
[
content
async for content in chat_log.async_add_delta_content_stream(
self.entity_id,
_transform_stream(
chat_log,
stream,
output_tool=structure_name or None,
),
)
]
)
new_messages, model_args["container"] = _convert_content(
[
content
async for content in chat_log.async_add_delta_content_stream(
self.entity_id,
_transform_stream(
chat_log,
stream,
output_tool=structure_name or None,
),
)
]
)
messages.extend(new_messages)
except anthropic.AuthenticationError as err:
self.entry.async_start_reauth(self.hass)
raise HomeAssistantError(
"Authentication error with Anthropic API, reauthentication required"
) from err
except anthropic.AnthropicError as err:
raise HomeAssistantError(
f"Sorry, I had a problem talking to Anthropic: {err}"

View File

@@ -0,0 +1,14 @@
{
"entity": {
"ai_task": {
"ai_task_data": {
"default": "mdi:asterisk"
}
},
"conversation": {
"conversation": {
"default": "mdi:asterisk"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"domain": "anthropic",
"name": "Anthropic Conversation",
"name": "Anthropic",
"after_dependencies": ["assist_pipeline", "intent"],
"codeowners": ["@Shulyaka"],
"config_flow": true,

View File

@@ -31,10 +31,7 @@ rules:
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: todo
comment: |
Reevaluate exceptions for entity services.
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
@@ -92,7 +89,7 @@ rules:
No entities disabled by default.
entity-translations: todo
exception-translations: todo
icon-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues: done
stale-devices:

View File

@@ -69,6 +69,7 @@
},
"model": {
"data": {
"code_execution": "[%key:component::anthropic::config_subentries::conversation::step::model::data::code_execution%]",
"thinking_budget": "[%key:component::anthropic::config_subentries::conversation::step::model::data::thinking_budget%]",
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data::thinking_effort%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data::user_location%]",
@@ -76,6 +77,7 @@
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search_max_uses%]"
},
"data_description": {
"code_execution": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::code_execution%]",
"thinking_budget": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::thinking_budget%]",
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::thinking_effort%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::user_location%]",
@@ -127,6 +129,7 @@
},
"model": {
"data": {
"code_execution": "Code execution",
"thinking_budget": "Thinking budget",
"thinking_effort": "Thinking effort",
"user_location": "Include home location",
@@ -134,6 +137,7 @@
"web_search_max_uses": "Maximum web searches"
},
"data_description": {
"code_execution": "Allow the model to execute code in a secure sandbox environment, enabling it to analyze data and perform complex calculations.",
"thinking_budget": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking.",
"thinking_effort": "Control how many tokens Claude uses when responding, trading off between response thoroughness and token efficiency",
"user_location": "Localize search results based on home location",

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

View File

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

View File

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

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,23 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
brand:
required: true
selector:
select:
options: []
refresh_token:
required: true
selector:
text: {}
refresh_token_creation_time:
required: true
selector:
text: {}
options:
fields: {}
subentries: {}

View File

@@ -117,6 +117,7 @@ class SharpAquosTVDevice(MediaPlayerEntity):
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.PLAY
)
_attr_volume_step = 2 / 60
def __init__(
self, name: str, remote: sharp_aquos_rc.TV, power_on_enabled: bool = False
@@ -161,22 +162,6 @@ class SharpAquosTVDevice(MediaPlayerEntity):
"""Turn off tvplayer."""
self._remote.power(0)
@_retry
def volume_up(self) -> None:
"""Volume up the media player."""
if self.volume_level is None:
_LOGGER.debug("Unknown volume in volume_up")
return
self._remote.volume(int(self.volume_level * 60) + 2)
@_retry
def volume_down(self) -> None:
"""Volume down media player."""
if self.volume_level is None:
_LOGGER.debug("Unknown volume in volume_down")
return
self._remote.volume(int(self.volume_level * 60) - 2)
@_retry
def set_volume_level(self, volume: float) -> None:
"""Set Volume media player."""

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,58 @@
config_entry:
versions:
- version:
major: 1
minor: 1
data:
fields:
host:
required: false
selector:
text: {}
mode:
required: false
selector:
text: {}
password:
required: false
selector:
text: {}
port:
required: false
selector:
text: {}
protocol:
required: false
selector:
text: {}
ssh_key:
required: false
selector:
text: {}
username:
required: false
selector:
text: {}
options:
fields:
consider_home:
required: false
selector:
text: {}
dnsmasq:
required: false
selector:
text: {}
interface:
required: false
selector:
text: {}
require_ip:
required: false
selector:
text: {}
track_unknown:
required: false
selector:
text: {}
subentries: {}

View File

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

View File

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

View File

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

View File

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

View File

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

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:
access_token:
required: false
selector:
text: {}
device:
required: false
selector:
text: {}
email:
required: false
selector:
text: {}
host:
required: false
selector:
text: {}
options:
fields: {}
subentries: {}

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